Built-in project · Arduino Uno R3

Three-digit voltmeter on an Arduino Uno

A potentiometer on A0 and a bare three-digit seven-segment display: the Uno shows the wiper voltage, 0.00 to 5.00 V, scanning the digits itself. The whole build, an Arduino Uno and 10 more parts, runs here in your browser on the firmware below; open it in the editor to change the wiring or the code and run it again.

Teaching? Assign it to your class in one click.

Intermediate About 30 minutes to build and run. Runs in your browser. Free, and no account needed.

The parts retail for about $29. On Mokxi they're free.

The circuit runs here once the page has loaded.

What you'll learn

  • Reading a potentiometer as a voltage
  • Scaling a count to hundredths of a volt with integer math
  • Multiplexing a three-digit display
  • Splitting a number into digits with / and %
  • The decimal point as a segment bit
  • Difficulty: Intermediate
  • Time: About 30 minutes
  • Board: Arduino Uno R3
  • Parts: 11

How it works

The potentiometer's wiper goes to A0. Ten times a second the sketch reads it and scales it so 1023 means 5.00 V, stored as hundredths of a volt in hundredths.

The display has three digits that share their segment lines, so the sketch lights one digit at a time. The segments A to DP go through 220 ohm resistors to pins 2 to 9, and the digits are on pins 10, 11 and 12.

In loop the first digit is shown with its decimal point, then the second, then the third, over and over. It is a common cathode display, so a digit is on while its pin is LOW.

Parts list

11 parts, plus the jumper wires. They retail for about $29; on Mokxi they're free.

Wiring table

Every connection, pin by pin, read from the circuit itself. Each row joins the pin on the left to the pin on the right, by a jumper wire or a breadboard strip.

Part and pinConnects to
Three-digit seven segment, multiplexed pin DIG3Arduino Uno R3 pin 12
Three-digit seven segment, multiplexed pin DIG2Arduino Uno R3 pin 11
Three-digit seven segment, multiplexed pin DIG1Arduino Uno R3 pin 10
Resistor, 220 Ω (8) pin 1Arduino Uno R3 pin 9
Resistor, 220 Ω (7) pin 1Arduino Uno R3 pin 8
Resistor, 220 Ω (6) pin 1Arduino Uno R3 pin 7
Resistor, 220 Ω (5) pin 1Arduino Uno R3 pin 6
Resistor, 220 Ω (4) pin 1Arduino Uno R3 pin 5
Resistor, 220 Ω (3) pin 1Arduino Uno R3 pin 4
Resistor, 220 Ω (2) pin 1Arduino Uno R3 pin 3
Resistor, 220 Ω (1) pin 1Arduino Uno R3 pin 2
Potentiometer pin 1Arduino Uno R3 pin 5V
Potentiometer pin 3Arduino Uno R3 pin GND
Potentiometer pin 2Arduino Uno R3 pin A0
Resistor, 220 Ω (5) pin 2Three-digit seven segment, multiplexed pin E
Resistor, 220 Ω (4) pin 2Three-digit seven segment, multiplexed pin D
Resistor, 220 Ω (8) pin 2Three-digit seven segment, multiplexed pin DP
Resistor, 220 Ω (3) pin 2Three-digit seven segment, multiplexed pin C
Resistor, 220 Ω (7) pin 2Three-digit seven segment, multiplexed pin G
Resistor, 220 Ω (1) pin 2Three-digit seven segment, multiplexed pin A
Resistor, 220 Ω (6) pin 2Three-digit seven segment, multiplexed pin F
Resistor, 220 Ω (2) pin 2Three-digit seven segment, multiplexed pin B

The code

The firmware exactly as the editor opens it. Change a line there and press Run: it compiles in the browser.

sketch.ino · Arduino Uno R3
// Three-digit voltmeter: a potentiometer's wiper on A0, shown as 0.00 to 5.00
// on a bare three-digit display that the sketch multiplexes.
//
//   segments A B C D E F G DP -> pins 2 to 9, each through its own 220 ohm
//   digits DIG1 DIG2 DIG3     -> pins 10, 11 and 12
//   potentiometer             -> 5V, A0 (the wiper), GND
//
// Common cathode: a digit is on while its common is LOW. The point after the
// first digit is that digit's DP segment.

const uint8_t SEGMENT_PINS[8] = {2, 3, 4, 5, 6, 7, 8, 9};
const uint8_t DIGIT_PINS[3] = {10, 11, 12};
const uint8_t FONT[10] = {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F};
const uint8_t POINT = 0x80;

int hundredths = 0;  // the voltage in hundredths of a volt
unsigned long lastRead = 0;

void showDigit(uint8_t which, uint8_t pattern) {
  for (uint8_t i = 0; i < 3; i++) digitalWrite(DIGIT_PINS[i], HIGH);
  for (uint8_t s = 0; s < 8; s++) digitalWrite(SEGMENT_PINS[s], (pattern >> s) & 1);
  digitalWrite(DIGIT_PINS[which], LOW);
  delay(4);
}

void setup() {
  for (uint8_t i = 0; i < 8; i++) pinMode(SEGMENT_PINS[i], OUTPUT);
  for (uint8_t i = 0; i < 3; i++) {
    pinMode(DIGIT_PINS[i], OUTPUT);
    digitalWrite(DIGIT_PINS[i], HIGH);
  }
}

void loop() {
  if (millis() - lastRead >= 100) {
    lastRead = millis();
    // 1023 is 5.00 V: scale the reading to hundredths, rounded.
    hundredths = (int)((analogRead(A0) * 500L + 511) / 1023);
  }
  showDigit(0, FONT[hundredths / 100] | POINT);
  showDigit(1, FONT[(hundredths / 10) % 10]);
  showDigit(2, FONT[hundredths % 10]);
}

Code walkthrough

The key functions of the sketch, and what each one does.

showDigit sketch.ino

showDigit first turns all three digits off. Then it writes the segment pattern to pins 2 to 9, turns on the chosen digit by pulling its pin LOW, and waits 4 ms.

void showDigit(uint8_t which, uint8_t pattern) {
  for (uint8_t i = 0; i < 3; i++) digitalWrite(DIGIT_PINS[i], HIGH);
  for (uint8_t s = 0; s < 8; s++) digitalWrite(SEGMENT_PINS[s], (pattern >> s) & 1);
  digitalWrite(DIGIT_PINS[which], LOW);
  delay(4);
}

setup sketch.ino

setup makes the eight segment pins and three digit pins outputs, and sets the digit pins HIGH so all digits begin off.

void setup() {
  for (uint8_t i = 0; i < 8; i++) pinMode(SEGMENT_PINS[i], OUTPUT);
  for (uint8_t i = 0; i < 3; i++) {
    pinMode(DIGIT_PINS[i], OUTPUT);
    digitalWrite(DIGIT_PINS[i], HIGH);
  }
}

loop sketch.ino

loop refreshes the reading every 100 ms, rounding to hundredths. Then it shows the hundreds digit with the point, the tens digit, and the ones digit, one after another.

void loop() {
  if (millis() - lastRead >= 100) {
    lastRead = millis();
    // 1023 is 5.00 V: scale the reading to hundredths, rounded.
    hundredths = (int)((analogRead(A0) * 500L + 511) / 1023);
  }
  showDigit(0, FONT[hundredths / 100] | POINT);
  showDigit(1, FONT[(hundredths / 10) % 10]);
  showDigit(2, FONT[hundredths % 10]);
}

Troubleshooting

The usual mistakes with this circuit, and the fix for each.

  • The reading is off by a decimal place. This project
    The point is on the first digit by design, since the value goes 0.00 to 5.00.
  • Digits are swapped or wrong. This project
    DIG1, DIG2 and DIG3 go to pins 10, 11 and 12 in order. The segments go to pins 2 to 9 in order.
  • The display flickers. This project
    loop must keep drawing all three digits. Long delays make it blink.
  • Only one digit lights, or all show the same number. Three-digit seven segment
    A multiplexed display lights one digit at a time. Turn on one digit, put out its segments, wait a moment, then move to the next, in a fast loop with no long delays.
  • Digits flicker or ghost into neighbors. Three-digit seven segment
    Refresh every digit at least 60 times a second. Turn all digits off before changing the segment pattern. Avoid delay() and use timed loops.
  • The LED is dim, burns out, or the pin reads wrong because the resistor value is off. Resistor
    Read the color bands again. A 220 ohm to 330 ohm resistor suits a typical LED on 5 V. 10k is a common pull-up or pull-down value. Check with a multimeter if unsure.
  • A pull-up or pull-down resistor is missing, so the input floats and reads random values. Resistor
    Tie the input to 5 V or ground through about 10k. Or use INPUT_PULLUP and wire the switch to ground.
  • The reading jumps around or only covers part of the range. Potentiometer
    Wire the two outer legs to 5 V and ground and the middle leg (the wiper) to an analog pin. Using the wrong two legs gives a dead or partial range.

Change it and keep it

Open it in the editor, change the circuit or the code, and keep your version in a free account.