# imports
import machine
import math
import time

#######################################
# Pin and constant definitions
#######################################

SEVEN_SEGMENT_START_PIN = 0
DISPLAY_COUNT = 4
DECIMAL_PRECISION = 3
ADC_MAX_VALUE = 65535
VOLTAGE_REF = 3.3

# NTC sensor constants
BETA = 3950
T0 = 298.15
R0 = 10000
R_FIXED = 10000

#######################################
# Global variables
#######################################
display_value = 0
segment_pins = []
display_select_pins = []
current_display_index = DISPLAY_COUNT - 1
display_timer = None
last_interrupt_time = 0
adc = machine.ADC(26)

#######################################
# Function definitions
#######################################

# Function to read the ADC pin and
# to convert the digital value to a voltage level in the 0-3.3V range
# This function updates the value of the display_value global variable
def read_analogue_voltage(pin):
    global display_value, last_interrupt_time
    current_time = time.ticks_ms()
    if time.ticks_diff(current_time, last_interrupt_time) > 200:
        time.sleep_ms(100)  


        samples = [adc.read_u16() for _ in range(10)]
        raw = sum(samples) // len(samples)
        voltage = (raw / ADC_MAX_VALUE) * VOLTAGE_REF

     
        if voltage < 0 or voltage > VOLTAGE_REF:
            print("Invalid or noisy voltage reading. Ignored.")
            return


        display_value = round(voltage * 1000)  # 3.300 → 3300
        print("Voltage = {:.3f} V".format(voltage))

        # NTC temp (Optionly)
        try:
            if 0 < voltage < VOLTAGE_REF:
                resistance = (voltage * R_FIXED) / (VOLTAGE_REF - voltage)
                temp_kelvin = 1 / (1/T0 + (1/BETA) * math.log(resistance / R0))
                temp_celsius = temp_kelvin - 273.15
                print("Temp ≈ {:.2f} °C".format(temp_celsius))
        except:
            print("NTC reading error.")

        last_interrupt_time = current_time


# Function to disable timer that triggers scanning 7 segment displays
def disable_display_timer():
    global display_timer
    display_timer.deinit()

# Function to enable timer that triggers scanning 7 segment displays
def enable_display_timer():
    global display_timer
    display_timer.init(period=30, mode=machine.Timer.PERIODIC, callback=scan_display)

# Function to handle scanning 7 segment displays
# Display the value stored in the display_value global variable
# on available 7-segment displays
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 -= 1
    if current_display_index < 0:
        current_display_index = DISPLAY_COUNT - 1

# Function display the given value on the display with the specified index
# dp_enable specifies if the decimal pooint should be on or off
def display_digit(digit_value, digit_index, dp_enable=False):
    digit_list_hex = [
        0x40,  # 0
        0x79,  # 1
        0x24,  # 2
        0x30,  # 3
        0x19,  # 4
        0x12,  # 5
        0x02,  # 6
        0x78,  # 7
        0x00,  # 8
        0x10,  # 9
        0x7F   # Blank
    ]

    if digit_value < 0 or digit_value > 9:
        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 not dp_enable else 0)

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

# Function to test avaiable 7-segment displays
def display_value_test():
    global display_value
    disable_display_timer()
    current_display_index = 0

    for i in range(10):  # Display digits 0-9
        display_digit(i, -1, i % 2 == 0)
        time.sleep(0.5)

    display_digit(10, -1, False)  # Blank
    enable_display_timer()

# Function to setup GPIO/ADC pins, timers and interrupts
def setup():
    global segment_pins, display_select_pins, display_timer
    # Segment pins (A-G + DP)
    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)

    # Digit 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)

    # Timer for scanning display
    display_timer = machine.Timer()
    enable_display_timer()

    # Setup button interrupt on GPIO16
    button = machine.Pin(16, machine.Pin.IN, machine.Pin.PULL_UP)
    button.irq(trigger=machine.Pin.IRQ_FALLING, handler=read_analogue_voltage)

# Start program
if __name__ == '__main__':
    setup()
    #display_value_test()
    while True:
        time.sleep(0.1)
$abcdeabcde151015202530354045505560fghijfghij
$abcdeabcde151015202530fghijfghij
BOOTSELLED1239USBRaspberryPiPico©2020RP2-8020/21P64M15.00TTT