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.
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.
- 1 × Arduino Uno R3In Starter Kit, Claw Machine Kit, Vending Machine Kit.
- 1 × Three-digit seven segment, multiplexed
- 8 × Resistor, 220 ΩIn Starter Kit, Claw Machine Kit, Halloween Prop Kit.
- 1 × PotentiometerIn Halloween Prop Kit, Starter Projects Kit.
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 pin | Connects to |
|---|---|
| Three-digit seven segment, multiplexed pin DIG3 | Arduino Uno R3 pin 12 |
| Three-digit seven segment, multiplexed pin DIG2 | Arduino Uno R3 pin 11 |
| Three-digit seven segment, multiplexed pin DIG1 | Arduino Uno R3 pin 10 |
| Resistor, 220 Ω (8) pin 1 | Arduino Uno R3 pin 9 |
| Resistor, 220 Ω (7) pin 1 | Arduino Uno R3 pin 8 |
| Resistor, 220 Ω (6) pin 1 | Arduino Uno R3 pin 7 |
| Resistor, 220 Ω (5) pin 1 | Arduino Uno R3 pin 6 |
| Resistor, 220 Ω (4) pin 1 | Arduino Uno R3 pin 5 |
| Resistor, 220 Ω (3) pin 1 | Arduino Uno R3 pin 4 |
| Resistor, 220 Ω (2) pin 1 | Arduino Uno R3 pin 3 |
| Resistor, 220 Ω (1) pin 1 | Arduino Uno R3 pin 2 |
| Potentiometer pin 1 | Arduino Uno R3 pin 5V |
| Potentiometer pin 3 | Arduino Uno R3 pin GND |
| Potentiometer pin 2 | Arduino Uno R3 pin A0 |
| Resistor, 220 Ω (5) pin 2 | Three-digit seven segment, multiplexed pin E |
| Resistor, 220 Ω (4) pin 2 | Three-digit seven segment, multiplexed pin D |
| Resistor, 220 Ω (8) pin 2 | Three-digit seven segment, multiplexed pin DP |
| Resistor, 220 Ω (3) pin 2 | Three-digit seven segment, multiplexed pin C |
| Resistor, 220 Ω (7) pin 2 | Three-digit seven segment, multiplexed pin G |
| Resistor, 220 Ω (1) pin 2 | Three-digit seven segment, multiplexed pin A |
| Resistor, 220 Ω (6) pin 2 | Three-digit seven segment, multiplexed pin F |
| Resistor, 220 Ω (2) pin 2 | Three-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.
// 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
loopmust 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.