I built this lamp for my school project, using the M5GO Kit.
The M5GO bot has 3 buttons:
- the left button (state
A) is used for advancing to the next date, - the middle button (state
B) is for entering "study" mode where it keeps tracks of how long you are studying for the session. It also has an ultrasonic sensor that detects the distance between the lamp and the user, and a light sensor to detect how much light the user is getting. It then delivers how much light is needed to deliver to the user. It amins around 1500 Lux to the user and then uses the inverse square law to calculate how "strong" the RGB LED needs to be and set it to that level. - The right button (state
C) is used to display statistics about your studying time.
My code is really hacky, and there are a lot of redundancies. Also, I ran into troubles: for example clicking different buttons in quick succession could cause the display to bug out. How can I refactor my code so that it is cleaner and avoid bugs?
In case my description is not good, you can take a look at the prototype.
from m5stack import *
from m5ui import *
from uiflow import *
from math import floor
import unit
import machine
import utime
import neopixel
import time
def set_state_a():
global state
state = 'A'
set_all(0,0,0)
time_label.hide()
studying_label.hide()
average_label.hide()
sum_label.hide()
max_label.hide()
min_label.hide()
date_label.show()
welcoming_label1.show()
welcoming_label2.show()
set_all(0,0,0)
def set_state_b():
global state
state = 'B'
date_label.hide()
welcoming_label1.hide()
welcoming_label2.hide()
average_label.hide()
sum_label.hide()
max_label.hide()
min_label.hide()
studying_label.show()
time_label.show()
def set_state_c():
global state
state = 'C'
set_all(0,0,0)
date_label.hide()
welcoming_label1.hide()
welcoming_label2.hide()
studying_label.hide()
time_label.hide()
sum_label.show()
average_label.show()
max_label.show()
min_label.show()
def set_all(r, g, b):
for i in range(3):
np[i] = (r, g, b)
np.write()
def cal_date_str(day, month, year):
day_text = str(day) if day >= 10 else '0' + str(day)
month_text = month_converter[month]
year_text = str(year)
date_label.setText(day_text+ ' ' + month_text + ' '+ year_text)
def cal_hr_str(start, label, seconds):
second_round = round(seconds)
min, sec = divmod(second_round, 60)
hour, min = divmod(min, 60)
sec_text = str(sec) if sec >= 10 else '0' + str(sec)
min_text = str(min) if min >= 10 else '0' + str(min)
hour_text = str(hour) if min >= 10 else '0' + str(hour)
label.setText(start + hour_text + ':' + min_text + ':'+ sec_text)
def buttonA_wasPressed():
global state, day, month, year, study_seconds, study_array
set_state_a()
day += 1
study_array.append(study_seconds/1000)
study_seconds = 0
if month == 2:
is_leap = year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)
if (is_leap and day > 29) or (not is_leap and day > 28):
day = 1
month = 3
elif month == 12 and day > 31:
day = 1
month = 1
year += 1
elif month in (1, 3, 5, 7, 8, 10) and day > 31:
day = 1
month += 1
elif month in (4, 6, 9, 11) and day > 30:
day = 1
month += 1
cal_date_str(day, month, year)
set_all(0,0,0)
def buttonB_wasPressed():
global state
if state == 'B':
set_state_a()
else:
set_state_b()
def buttonC_wasPressed():
global study_array, sum_label, average_label, max_label, min_label
set_state_c()
sum_seven_days = 0
if len(study_array) <= 6:
for val in study_array:
sum_seven_days += val
else:
for i in range(7):
sum_seven_days += study_array[len(sum_seven_days) - i - 1]
avr = sum_seven_days // 7
sum_arr = sum(study_array) / len(study_array)
max_arr = max(study_array)
min_arr = min(study_array)
cal_hr_str("Average study time last 7 days: ", average_label, avr)
cal_hr_str("Average study time: ", sum_label, sum_arr)
cal_hr_str("Max study time: ", max_label, max_arr)
cal_hr_str("Min study time: ", min_label, min_arr)
btnA.wasPressed(buttonA_wasPressed)
btnB.wasPressed(buttonB_wasPressed)
btnC.wasPressed(buttonC_wasPressed)
setScreenColor(0x222222)
ultrasonic = unit.get(unit.ULTRASONIC, unit.PORTA)
adc_pin = machine.Pin(36)
adc = machine.ADC(adc_pin)
adc.atten(machine.ADC.ATTN_11DB)
adc.width(machine.ADC.WIDTH_12BIT)
np = neopixel.NeoPixel(machine.Pin(26), 3)
date_label = M5TextBox(35, 60, "DD MMM YYYY", lcd.FONT_DejaVu40, 0xFFFFFF, rotate=0)
welcoming_label1 = M5TextBox(35, 150, "What a great day!", lcd.FONT_DejaVu18, 0xFFFFFF, rotate=0)
welcoming_label2 = M5TextBox(35, 180, "It's a great time to study!", lcd.FONT_DejaVu18, 0xFFFFFF, rotate=0)
studying_label = M5TextBox(50, 60, "Happy stuyding!", lcd.FONT_DejaVu24, 0xFFFFFF, rotate=0)
time_label = M5TextBox(60, 120, "00:00:00", lcd.FONT_DejaVu40, 0xFFFFFF, rotate=0)
average_label = M5TextBox(0, 60, "", lcd.FONT_Default, 0xFFFFFF, rotate=0)
sum_label = M5TextBox(0, 75, "", lcd.FONT_Default, 0xFFFFFF, rotate=0)
max_label = M5TextBox(0, 120, "", lcd.FONT_DejaVu18, 0xFFFFFF, rotate=0)
min_label = M5TextBox(0, 140, "", lcd.FONT_DejaVu18, 0xFFFFFF, rotate=0)
TARGET_AMBIENT = 1500
MIN_DIST, MAX_DIST = 40, 74
NORM = MAX_DIST ** 2 * TARGET_AMBIENT / 255
day, month, year = 20, 5, 2025
study_seconds = 0.0
month_converter = ['NaN', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
state = 'A'
study_array = []
set_state_a()
cal_date_str(day, month, year)
while True:
if state == 'B':
start = time.ticks_ms()
raw_value = adc.read()
brightness = 4095 - raw_value
dist = ultrasonic.distance / 10
if state != 'B' or brightness >= TARGET_AMBIENT:
set_all(0,0,0)
else:
brigtness_need = TARGET_AMBIENT - brightness
value = round(dist ** 2 * brigtness_need / NORM)
set_all(value, value, value)
end = time.ticks_ms()
study_seconds += end - start
# There is a problem when you swtich the states too fast, the display ended up bugging.
if state == 'B':
cal_hr_str("",time_label, study_seconds / 1000)