from time import sleep_ms
from machine import Pin, SoftI2C
from i2c_lcd import I2cLcd
import math
from decimal import *
######## Definitions ########
# Define LCD params
AddressOfLcd = 0x27
i2c = SoftI2C(scl=Pin(22), sda=Pin(21), freq=400000) # connect scl to GPIO 22, sda to GPIO 21
lcd = I2cLcd(i2c, AddressOfLcd, 4, 20)
# Define keypad layout
keypad = [
['MC', 'M+', 'M-', 'MR' ],
['C', '+/-', '%', '/' ],
['7', '8', '9', '*' ],
['4', '5', '6', '-' ],
['1', '2', '3', '+' ],
['SQRT', '0', '.', '=' ]
]
# Define the row and column pins
row_pins = [Pin(13, Pin.OUT), Pin(12, Pin.OUT), Pin(14, Pin.OUT), Pin(32, Pin.OUT), Pin(4, Pin.OUT), Pin(2, Pin.OUT)]
col_pins = [Pin(27, Pin.IN, Pin.PULL_DOWN), Pin(26, Pin.IN, Pin.PULL_DOWN), Pin(25, Pin.IN, Pin.PULL_DOWN), Pin(33, Pin.IN, Pin.PULL_DOWN)]
# Initialize the row pins to HIGH
for row_pin in row_pins:
row_pin.value(0)
# Initialize the col pins to LOW
for col_pin in col_pins:
col_pin.value(0)
# Variables to store user input and calculation
user_input = ""
result = None
math_sign = ""
sign_applied = False # When sign button has not been clicked
memory_result = 0.0;
equals_pressed = False
######## Methods ########
# Pad String
def pad_string(input_string, desired_length = 15):
current_length = len(input_string)
if current_length >= desired_length:
return input_string # No need to pad if it's long enough
# Calculate the number of spaces needed
spaces_needed = desired_length - current_length
# Append the required spaces to the string
padded_string = input_string + " " * spaces_needed
return padded_string
# Print user input
def lcd_print(row, value, start_col = 1, space_padding = True):
print("move to : " + str(row))
lcd.move_to(start_col,row)
if space_padding:
lcd.putstr(pad_string(str(value)))
else:
lcd.putstr(str(value))
# Get Key Pressed value
def get_key():
for i, row_pin in enumerate(row_pins):
# Drive the current row HIGH
row_pin.value(1)
for j, col_pin in enumerate(col_pins):
if col_pin.value() == 1:
# Key is pressed, return the corresponding character
row_pin.value(0)
print("value : " + keypad[i][j])
return keypad[i][j]
sleep_ms(5)
row_pin.value(0)
return None
# Math Calc Function
def calculate(num1, operator, num2):
if operator == '+':
result = num1 + num2
elif operator == '-':
result = num1 - num2
elif operator == '*':
result = num1 * num2
elif operator == '/':
if num2 == 0:
return "Division by zero is not allowed"
result = num1 / num2
elif operator == 'sqrt':
if num1 < 0:
return "Square root of a negative number is not allowed"
result = math.sqrt(num1)
else:
return "Unsupported operator"
return str(result)
# Evaluate Math Expression
def evaluate_expression():
global user_input
global result
global math_sign
try:
calc_result = float(result)
except ValueError:
calc_result = 0.0
print("Invalid Result float format: " + result)
try:
calc_user_input = float(user_input)
except ValueError:
calc_user_input = 0.0
print("Invalid User Input format: " + user_input)
try:
cc_result = DecimalNumber(str(calc_result))
print("cc_result", cc_result)
cc_user_input = DecimalNumber(str(user_input))
print("cc_user_input", cc_result / cc_user_input)
return calculate(cc_result, math_sign, cc_user_input)
except Exception as e:
print("Exception: ", e)
return "Error"
def print_result_to_top_row():
global result
global equals_pressed
if equals_pressed == True:
lcd_print(0, result) # Print Result on top Row
lcd_print(3, " ") # Clear Result Row
equals_pressed = False
# Run keyboard scan
def keyboard_scan():
global user_input
global result
global math_sign
global sign_applied
global memory_result
global equals_pressed
key = get_key()
if key is not None:
if key == 'C':
# Clear the input and result
user_input = ''
result = None
lcd.clear()
math_sign = ""
sign_applied = False
lcd_print(2, "-----", 0, False)
equals_pressed = False
elif key == '=':
# Calculate the result
try:
if user_input:
result = evaluate_expression()
#result = evaluate_expression(result if result is not None else "" + math_sign + user_input)
lcd_print(3, " " if result is None else str(result))
user_input = "" # Clear input after result is calculated
equals_pressed = True
except Exception as e:
print("Error Occured: ", e)
elif key == '+' or key == '-' or key == '*' or key == '/':
print_result_to_top_row()
# save and print sign
math_sign = key # store sign
lcd_print(1, key, 0, False) # Print current sign
result = user_input if sign_applied == False else result
user_input = ""
lcd_print(1, " ") # Clear input Row
sign_applied = True
elif key == 'MC':
memory_result = 0.0
elif key == 'M+':
try:
memory_result = memory_result + float(result if sign_applied == True else user_input)
print("memory_result: " + memory_result)
except:
pass
elif key == 'M-':
try:
memory_result = memory_result - float(result if sign_applied == True else user_input)
print("memory_result: " + memory_result)
except:
pass
elif key == 'MR':
user_input = str(memory_result)
lcd_print(1, user_input) # Print Memory value
elif key == '+/-':
print_result_to_top_row()
if len(user_input) > 0:
# Use eval to evaluate the expression
user_input = str(float(user_input) * -1)
# print to LCD
lcd_input_row = 0 if sign_applied == False else 1
lcd_print(lcd_input_row, str(user_input))
lcd_print(3, " ") # Clear Result Row
elif key == '%':
print_result_to_top_row()
if len(user_input) > 0:
# Use eval to evaluate the expression
user_input = str(float(user_input) * 0.01)
# print to LCD
lcd_input_row = 0 if sign_applied == False else 1
lcd_print(lcd_input_row, str(user_input))
lcd_print(3, " ") # Clear Result Row
elif key == 'SQRT':
print_result_to_top_row()
if len(user_input) > 0:
# Create a string representing the square root operation
expression = "math.sqrt({})".format(user_input)
# Use eval to evaluate the expression
user_input = str(eval(expression))
# print to LCD
lcd_input_row = 0 if sign_applied == False else 1
lcd_print(lcd_input_row, str(user_input))
lcd_print(3, " ") # Clear Result Row
else:
# Append the key to the user input
user_input += key
lcd_input_row = 0 if sign_applied == False else 1
lcd_print(lcd_input_row, str(user_input))
print_result_to_top_row()
# Add a small delay to debounce the keypad
sleep_ms(80)
lcd_print(2, "-----", 0, False)
while True:
keyboard_scan()