# imports
import machine
import math
import time

#######################################
# Pin and constant definitions
#######################################
SEGMENT_START_PIN = 0    #gpio 0 to 11
DISPLAY_COUNT = 4        #4 digit seven segment
ADC_MAX = 65535         #2**16-1
DISPLAY_MAX = 9999      #filling the seven segment
SAMPLE_COUNT = 16
V_REF = 3.3             # maximum voltage + can be modefied 

digit_list_hex = [#common anode reading 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   # no display
]

#######################################
# Global variables
#######################################
display_value = 0   #display 0 at first then the voltage value
current_index = 3   #leftmost digit
last_btn_time = 0
segment_pins = []   #from 0 to 8
select_pins = []    # from 8 to 12
analogue_pin = None
display_timer = None
btn_pressed = False
#######################################
# 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):  # We can use the pin parameter if needed
    global display_value, display_timer, V_REF, ADC_MAX, DISPLAY_MAX, SAMPLE_COUNT, current_index
    
    reading = 0
    for j in range(SAMPLE_COUNT):
        reading += pin.read_u16()
    average_reading = reading // SAMPLE_COUNT                 # averaging the value "percise measurement"

    voltage = round((average_reading * V_REF)/ADC_MAX, 3)#voltage value in 3 decimal places
    new_display_value = int(voltage * 1000)              # converting to millivolt to be displayed



    if new_display_value > DISPLAY_MAX:
        print(f"The value is more than{DISPLAY_MAX}")
        new_display_value = DISPLAY_MAX
    if new_display_value != display_value:
        print(f"The voltage is : {new_display_value} millivolt")
        display_value = new_display_value
        disable_display_timer()
        current_index = 3
        display_digit(16, -1)           # turning off all the digits between each press
        time.sleep(0.1)                 # be off for 100 ms between each press and reading value
        enable_display_timer()          # calling scan and displaying display value after changing to voltage

def interrupt_callback(pin):
    global btn_pressed, last_btn_time
    current_btn_ts = time.ticks_ms()
    btn_delta_time = current_btn_ts - last_btn_time
    if btn_delta_time > 200:                        #proper debouncing of 200 ms
        last_btn_time = current_btn_ts
        read_analogue_voltage(analogue_pin)         #is called when pressing by irq falling

    
# 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 = 4, mode = machine.Timer.PERIODIC, callback = scan_display)# handling with flickering

# 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 display_value, current_index
    #from leftmost digit to be displayed then period of 4ms transferring to digit until rightmost
    digit = int((abs(display_value) // (10**current_index)))%10 #getting the value of each digit by modulas
    display_digit(digit, current_index, current_index==3)#display each digit in segments function, dp at leftmost[index(3)]

    current_index = current_index - 1 #move to the other index"digit"
    if current_index < 0:             #if it's the last index return to the first index
        current_index = 3    
# 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):
    
    if digit_value < 0 or digit_value > len(digit_list_hex):#between 0 and 16
        return

    for pin in select_pins:
        pin.value(0)        #turning off

    for i in range(7):
        segment_pins[i].value((digit_list_hex[digit_value] >> i)&1)# value of segments but shifting right
    segment_pins[7].value(1 if dp_enable == False else 0)# enabling decimal point if true

    if digit_index == -1:
        for pin in select_pins:# display all the digits with value 16 empty
            pin.value(1)
    
    if 0 <= digit_index < 4:            # [0,3]
        select_pins[digit_index].value(1)# enabling the required digit

# Function to test avaiable 7-segment displays
def display_value_test():
    global display_value
    disable_display_timer()
    current_index = 0
    for i in range(0, len(digit_list_hex)):
        display_digit(i, -1, i % 2 !=0)
        time.sleep(0.75)

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



# Function to setup GPIO/ADC pins, timers and interrupts
def setup():
    global SEGMENT_START_PIN, DISPLAY_COUNT, analogue_pin, display_timer, select_pins, segment_pins

    for i in range(SEGMENT_START_PIN + 8, SEGMENT_START_PIN + 8 + DISPLAY_COUNT):# range(8,12)"4"
        pin = machine.Pin(i,machine.Pin.OUT)# i is 8 then 9 then 10 then 11
        pin.value(0)                        # disable all digits
        select_pins.append(pin)             #gpio_8,9,10,11 in list

    for i in range(SEGMENT_START_PIN, SEGMENT_START_PIN + 8):# range(0,8)"8" including dp(7)
        pin = machine.Pin(i, machine.Pin.OUT)# i is 0 then 1 then 2 then 3 then 4 then 5 then 6 then7
        pin.value(1)                         #disable all segments
        segment_pins.append(pin)              # gpio_0,1,2,3,4,5,6,7

    analogue_pin = machine.ADC(machine.Pin(26))           #POT or NTC or LDR
    interrupt_pin = machine.Pin(16, machine.Pin.IN, machine.Pin.PULL_UP)# push button to gnd

    display_timer = machine.Timer()            #read the time
    enable_display_timer()                   #calling the scan_display function
    interrupt_pin.irq(trigger=machine.Pin.IRQ_FALLING, handler=interrupt_callback)

    
    
if __name__ == '__main__':
    setup()
    #display_value_test()

#breif working idea:
# 
#beginning with setup to display turn off digits and displaying 0s at all digits
#then if button pressed calling the read analogue value to measure the reading and equalling to display value
#then transferring to the scan which get the value and display each digit from the leftmost to the rightmost 
#this is by display digit function wich get the value of segments by shifting right
#scan is repeated each 4 ms to display all digits"index" of the analogue or at first with 0
$abcdeabcde151015202530354045505560fghijfghij
$abcdeabcde151015202530fghijfghij
BOOTSELLED1239USBRaspberryPiPico©2020RP2-8020/21P64M15.00TTT