from machine import Pin
import math
import time
from utime import sleep, ticks_ms


SEVEN_SEGMENT_START_PIN = 0
DISPLAY_COUNT = 4
DECIMAL_PRECISION = 3
ANALOGUE_INPUT_PIN = 26
MEASUREMENT_COUNT = 16
ADC_RANGE = float((math.pow(2, 16) - 1))
button = machine.Pin(16, machine.Pin.IN, machine.Pin.PULL_UP)

digit_list_hex = [
    0x40,  # 0
    0x79,  # 1
    0x24,  # 2
    0x30,  # 3
    0x19,  # 4
    0x12,  # 5
    0x02,  # 6
    0x78,  # 7
    0x00,  # 8
    0x10,  # 9
    0x08,  # A
    0x03,  # B
    0x46,  # C
    0x21,  # D
    0x06,  # E
    0x0E,  # F
    0x7F   # Empty
]


display_value = 0
segment_pins = []
display_select_pins = []
current_display_index = DISPLAY_COUNT -1 
display_timer = None
counter_timer = None
analogue_voltage_pin = None
prev_analogue_voltage = -1
last_button_time_stamp = 0



def disable_timer():
    global display_timer
    display_timer.deinit()


def enable_timer():
    global display_timer
    display_timer.init(period=30, mode=machine.Timer.PERIODIC, callback=scan_display)


def scan_display(timer_int):
    global current_display_index, display_value


    digit = int((display_value // math.pow(10, current_display_index))) % 10

  
    display_digit(digit, current_display_index, 
        current_display_index == DECIMAL_PRECISION and 0 != DECIMAL_PRECISION)


    current_display_index = (current_display_index - 1)
    if current_display_index < 0:
        current_display_index = DISPLAY_COUNT -1

def read_analogue_voltage(pin):  
    global display_value, prev_disp_val, display_timer
    global prev_analogue_voltage, DECIMAL_PRECISION


    total_value = 0
    for _ in range(MEASUREMENT_COUNT):
        total_value += pin.read_u16()
    average_value = total_value // MEASUREMENT_COUNT


    analogue_voltage = round((average_value / ADC_RANGE) * 3.3, DISPLAY_COUNT - 1)

    if analogue_voltage != prev_analogue_voltage:
        prev_analogue_voltage = analogue_voltage

        new_display_value = int(analogue_voltage * math.pow(10, DECIMAL_PRECISION))

        if False:
            DECIMAL_PRECISION = 2
            temp = 0
            try:
                temp = round(1 / (math.log(1 / (ADC_RANGE / average_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, DECIMAL_PRECISION))

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

        if display_value != new_display_value:
            print(f'voltage: {analogue_voltage} mV')
            display_value = new_display_value

            disable_timer()
            current_display_index = DISPLAY_COUNT -1
            display_digit(16, -1)
            time.sleep(0.1)
            enable_timer()

def display_digit(digit_value, digit_index, dp_enable=False):

    if digit_value < 0 or digit_value > len(digit_list_hex):
        return


    for pin in display_select_pins:
        pin.value(0)


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

    segment_pins[7].value(1 if dp_enable == False else 0)


    if digit_index == -1:
        for pin in display_select_pins:
            pin.value(1)

    elif 0 <= digit_index < DISPLAY_COUNT:
        display_select_pins[digit_index].value(1)


def display_value_test():
    global display_value

    disable_timer()
    current_display_index = 0

    for i in range(0, len(digit_list_hex)):
        display_digit(i, -1, i % 2 != 0)
        time.sleep(0.5)

    for i in range(0, len(digit_list_hex)):
        display_digit(i, DISPLAY_COUNT - 1 - (i % DISPLAY_COUNT), True)
        time.sleep(0.5)        

    display_digit(16, -1, False)
    enable_timer()


def setup():
    global segment_pins, display_select_pins, analogue_voltage_pin, display_timer
    print("Voltmeter On")
    # Initialize segment and display select pins
    for i in range(SEVEN_SEGMENT_START_PIN + 8, SEVEN_SEGMENT_START_PIN + 8 + DISPLAY_COUNT):
        pin = machine.Pin(i, machine.Pin.OUT)
        pin.value(0)
        display_select_pins.append(pin)
    
    for i in range(SEVEN_SEGMENT_START_PIN, SEVEN_SEGMENT_START_PIN + 8):
        pin = machine.Pin(i, machine.Pin.OUT)
        pin.value(1)
        segment_pins.append(pin)

    # Initialize ADC pin
    analogue_voltage_pin = machine.ADC(machine.Pin(ANALOGUE_INPUT_PIN))

    # Initialize display timer
    display_timer = machine.Timer()
    enable_timer()

def interrupt_callback(pin):
    global last_button_time_stamp
    cur_button_ts = ticks_ms()
    button_press_delta = cur_button_ts - last_button_time_stamp
    if button_press_delta > 200:
        last_button_time_stamp = cur_button_ts
        read_analogue_voltage(analogue_voltage_pin)


button.irq(trigger=machine.Pin.IRQ_FALLING, handler= interrupt_callback)

if __name__ == '__main__':
    setup()
$abcdeabcde151015202530354045505560fghijfghij
$abcdeabcde151015202530fghijfghij
BOOTSELLED1239USBRaspberryPiPico©2020RP2-8020/21P64M15.00TTT