import machine
import math
import time

# Pin and constant definitions
SEGMENT_START_PIN = 0
ANALOG_INPUT_PIN = 26
INPUT_BUTTON_PIN = 16
NUM_DISPLAYS = 4
NUM_MEASUREMENTS = 16
VOLTAGE_PRECISION = 3
ADC_MAX_VALUE = float((math.pow(2, 16) - 1))

# HEX values for 7-segment display digits
SEGMENT_DIGIT_HEX = [
    0x40, 0x79, 0x24, 0x30, 0x19, 0x12, 0x02, 0x78, 
    0x00, 0x10, 0x08, 0x03, 0x46, 0x21, 0x06, 0x0E, 0x7F
]

# Global variables
current_display_value = 0
segment_control_pins = []
display_control_pins = []
analog_pin = None
button_control_pin = None
current_digit_index = NUM_DISPLAYS - 1
display_update_timer = None
previous_voltage = -1
last_button_press_time = 0

def read_voltage(pin):
    global current_display_value, previous_voltage, VOLTAGE_PRECISION

    total_adc_value = sum(pin.read_u16() for _ in range(NUM_MEASUREMENTS))
    average_adc_value = total_adc_value // NUM_MEASUREMENTS
    voltage = round((average_adc_value / ADC_MAX_VALUE) * 3.3, VOLTAGE_PRECISION)

    if voltage != previous_voltage:
        previous_voltage = voltage
        new_display_value = float(voltage * math.pow(10, VOLTAGE_PRECISION))

        if False:  # Placeholder for temperature reading logic
            VOLTAGE_PRECISION = 2
            try:
                temp = round(1 / (math.log(1 / (ADC_MAX_VALUE / average_adc_value - 1)) / 3950 + 1.0 / 298.15) - 273.15, 1)
                print(f'temp: {temp} C')
            except:
                pass
            new_display_value = int(temp * math.pow(10, VOLTAGE_PRECISION))

        max_display_value = math.pow(10, NUM_DISPLAYS) - 1
        if new_display_value > max_display_value:
            print(f'warn: {new_display_value} exceeds {max_display_value}, clipping')
            new_display_value = max_display_value

        if current_display_value != new_display_value:
            current_display_value = new_display_value
            stop_display_timer()
            global current_digit_index
            current_digit_index = NUM_DISPLAYS - 1
            update_digit_display(16, -1)
            time.sleep(0.1)
            start_display_timer()

    return voltage

def stop_display_timer():
    global display_update_timer
    display_update_timer.deinit()

def start_display_timer():
    global display_update_timer
    display_update_timer.init(period=4, mode=machine.Timer.PERIODIC, callback=scan_display)

def scan_display(timer):
    global current_digit_index, current_display_value

    digit = int(abs(current_display_value) // math.pow(10, current_digit_index)) % 10
    update_digit_display(digit, current_digit_index, current_digit_index == VOLTAGE_PRECISION and VOLTAGE_PRECISION != 0)
    current_digit_index = (current_digit_index - 1) % NUM_DISPLAYS

def update_digit_display(digit_value, digit_index, decimal_point=False):
    if not 0 <= digit_value < len(SEGMENT_DIGIT_HEX):
        return

    for pin in display_control_pins:
        pin.value(0)

    mask = SEGMENT_DIGIT_HEX[digit_value]
    for i in range(7):
        segment_control_pins[i].value((mask >> i) & 1)

    segment_control_pins[7].value(not decimal_point)

    if digit_index == -1:
        for pin in display_control_pins:
            pin.value(1)
    elif 0 <= digit_index < NUM_DISPLAYS:
        display_control_pins[digit_index].value(1)

def button_pressed(pin):
    global last_button_press_time

    current_time = time.ticks_ms()
    if current_time - last_button_press_time > 200:  # Debounce period of 200ms
        last_button_press_time = current_time
        voltage = read_voltage(analog_pin)
        if voltage is not None:
            print(f'voltage: {voltage} V')

def setup():
    global segment_control_pins, display_control_pins, analog_pin, button_control_pin, display_update_timer

    display_control_pins = [machine.Pin(i, machine.Pin.OUT, value=0) for i in range(SEGMENT_START_PIN + 8, SEGMENT_START_PIN + 8 + NUM_DISPLAYS)]
    segment_control_pins = [machine.Pin(i, machine.Pin.OUT, value=1) for i in range(SEGMENT_START_PIN, SEGMENT_START_PIN + 8)]
    analog_pin = machine.ADC(ANALOG_INPUT_PIN)
    button_control_pin = machine.Pin(INPUT_BUTTON_PIN, machine.Pin.IN, machine.Pin.PULL_UP)

    button_control_pin.irq(trigger=machine.Pin.IRQ_FALLING, handler=button_pressed)

    display_update_timer = machine.Timer()
    start_display_timer()

if __name__ == '__main__':
    setup()

    while True:
        time.sleep(1)  # Main loop idle, waiting for interrupts
$abcdeabcde151015202530354045505560fghijfghij
$abcdeabcde151015202530fghijfghij
BOOTSELLED1239USBRaspberryPiPico©2020RP2-8020/21P64M15.00TTT