"""
Embedded Voltmeter with 7-Segment Display
Measures voltage using ADC and displays on 4-digit 7-segment display
Button-triggered sampling with debounce protection
"""
import math
from time import sleep, ticks_ms, ticks_diff
from machine import ADC, Pin, Timer
#######################################
# CONFIGURATION
#######################################
# Hardware pins
ADC_PIN = 28 # GP26 (ADC0)
BTN_PIN = 26 # Button pin
SEGMENT_PINS = [0, 1, 2, 3, 4, 5, 6, 11] # a, b, c, d, e, f, g, dp
DIGIT_PINS = [7, 8, 9, 10] # Digit select pins
# Constants
VREF = 3.3 # Reference voltage
ADC_RES = 65535 # ADC resolution (16-bit)
DEBOUNCE_MS = 200 # Button debounce time in milliseconds
DISPLAY_REFRESH_FREQ = 500 # Display refresh frequency in Hz
DIGIT_DISPLAY_TIME = 0.002 # Time to display each digit (seconds)
BETA = 3950
# 7-segment digit encoding (abcdefg)
SEGMENT_MAP = [
0b0000001, # 0
0b1001111, # 1
0b0010010, # 2
0b0000110, # 3
0b1001100, # 4
0b0100100, # 5
0b0100000, # 6
0b0001111, # 7
0b0000000, # 8
0b0000100, # 9
0b1111111, # OFF
]
#######################################
# Global state
#######################################
display_value = 0
adc = None
segment_gpio = []
digit_gpio = []
display_timer = None
button = None
button_pressed = False
last_button_press = 0
#######################################
# ADC Functions
#######################################
def read_analogue_voltage():
"""Read voltage from ADC and update display value"""
global display_value
raw = adc.read_u16()
voltage = (raw / ADC_RES) * VREF
display_value = int(voltage * 1000)
# Calculate temp. based on wokwi sensor documentation
try:
celsius = 1 / (math.log(1 / (ADC_RES / raw - 1)) / BETA + 1.0 / 298.15) - 273.15
except ZeroDivisionError:
celsius = None
print(f"Analog voltage: {voltage:.3f}V _ Temp: {celsius}")
return voltage
#######################################
# Display Functions
#######################################
def enable_display_timer():
"""Initialize and start the display refresh timer"""
global display_timer
display_timer = Timer()
display_timer.init(freq=DISPLAY_REFRESH_FREQ, mode=Timer.PERIODIC, callback=scan_display)
def disable_display_timer():
"""Stop and deinitialize the display timer"""
global display_timer
if display_timer:
display_timer.deinit()
def scan_display(timer_int):
"""Update the 7-segment display (called by timer)"""
# Extract individual digits
value = display_value
digits = [
(value // 1000) % 10, # Thousands
(value // 100) % 10, # Hundreds
(value // 10) % 10, # Tens
value % 10 # Ones
]
# Display each digit in sequence (multiplexing)
for i in range(4):
# Turn off all digits first to prevent ghosting
for d in digit_gpio:
d.value(0)
# Set segments for current digit
dp = (i == 0) # Decimal point after first digit
display_digit(digits[i], i, dp_enable=dp)
# Enable current digit
digit_gpio[i].value(1)
sleep(DIGIT_DISPLAY_TIME)
def display_digit(digit_value, digit_index, dp_enable=False):
"""Display a single digit on the 7-segment display"""
# Set segments (a-g)
segs = SEGMENT_MAP[digit_value]
for i in range(7): # Only a-g, not DP
segment_gpio[i].value((segs >> (6 - i)) & 1)
# Set decimal point (segment 7 is DP)
if len(segment_gpio) > 7:
segment_gpio[7].value(0 if dp_enable else 1)
def display_value_test():
"""Test the display with incrementing values"""
for v in range(100, 400, 11):
global display_value
display_value = v
sleep(0.2)
#######################################
# Button Handling
#######################################
def button_irq_handler(pin):
"""Handle button press interrupt with debounce"""
global button_pressed, last_button_press
current_time = ticks_ms()
# Check if enough time has passed since last press (debounce)
if ticks_diff(current_time, last_button_press) > DEBOUNCE_MS:
button_pressed = True
last_button_press = current_time
#######################################
# Setup and Main Loop
#######################################
def setup():
"""Initialize hardware components"""
global adc, segment_gpio, digit_gpio, button
# Initialize ADC
adc = ADC(Pin(ADC_PIN))
# Initialize 7-segment display pins
segment_gpio = [Pin(p, Pin.OUT) for p in SEGMENT_PINS]
digit_gpio = [Pin(p, Pin.OUT) for p in DIGIT_PINS]
# Initialize button with interrupt
button = Pin(BTN_PIN, Pin.IN, Pin.PULL_UP)
button.irq(trigger=Pin.IRQ_FALLING, handler=button_irq_handler)
# Start display refresh
enable_display_timer()
if __name__ == '__main__':
setup()
# Main loop
while True:
if button_pressed:
read_analogue_voltage()
button_pressed = False
sleep(0.2)