StopWatch
Support the following products:
UiFlow2 Example
RTC Clock
Open the stopwatch_rtc_example.m5f2 project in UiFlow2.
This example displays a digital clock (HH:MM:SS) on the round screen, reading time from the built-in RTC. Press BtnA to cycle through hour, minute, and second adjustment modes (the active field is highlighted in red). Press BtnB to increment the selected field. After adjusting seconds, press BtnA again to write the new time to the RTC.
UiFlow2 Code Block:
Example output:
None
Power Management
Open the stopwatch_power_example.m5f2 project in UiFlow2.
This example monitors USB, battery, and Grove port voltages, and shows charging status (battery text turns green while charging). Press BtnA to toggle Grove external output (5V OUT). Press BtnB to toggle battery charging.
UiFlow2 Code Block:
Example output:
None
Audio Recording and Playback
Open the stopwatch_aduio_example.m5f2 project in UiFlow2.
This example demonstrates audio recording and playback. Press BtnA to start a 5-second recording (countdown shown on screen). Press BtnB to play back the recorded file when not recording. The UI shows Idle, Recording…, or Playing… status.
UiFlow2 Code Block:
Example output:
None
MicroPython Example
RTC Clock
This example displays a digital clock (HH:MM:SS) on the round screen, reading time from the built-in RTC. Press BtnA to cycle through hour, minute, and second adjustment modes (the active field is highlighted in red). Press BtnB to increment the selected field. After adjusting seconds, press BtnA again to write the new time to the RTC.
MicroPython Code Block:
1import os, sys, io 2import M5 3from M5 import * 4import m5ui 5import lvgl as lv 6from hardware import RTC 7import time 8 9 10page0 = None 11label_tip = None 12label_hour = None 13label_min = None 14label_second = None 15label_dot1 = None 16label_dot2 = None 17rtc = None 18 19 20sw = None 21hour = None 22minute = None 23second = None 24last_time = None 25 26 27def btna_was_click_event(state): 28 global \ 29 page0, \ 30 label_tip, \ 31 label_hour, \ 32 label_min, \ 33 label_second, \ 34 label_dot1, \ 35 label_dot2, \ 36 rtc, \ 37 sw, \ 38 hour, \ 39 minute, \ 40 second, \ 41 last_time 42 Speaker.tone(1000, 200) 43 sw = (sw if isinstance(sw, (int, float)) else 0) + 1 44 if sw == 1: 45 label_hour.set_text_color(0xFF0000, 255, 0) 46 elif sw == 2: 47 label_hour.set_text_color(0x00CCCC, 255, 0) 48 label_min.set_text_color(0xFF0000, 255, 0) 49 elif sw == 3: 50 label_min.set_text_color(0x00CCCC, 255, 0) 51 label_second.set_text_color(0xFF0000, 255, 0) 52 else: 53 label_second.set_text_color(0x00CCCC, 255, 0) 54 sw = 0 55 rtc.init((2026, 5, 21, hour, minute, second, 339, 0)) 56 57 58def btnb_was_click_event(state): 59 global \ 60 page0, \ 61 label_tip, \ 62 label_hour, \ 63 label_min, \ 64 label_second, \ 65 label_dot1, \ 66 label_dot2, \ 67 rtc, \ 68 sw, \ 69 hour, \ 70 minute, \ 71 second, \ 72 last_time 73 Speaker.tone(1000, 200) 74 if sw == 1: 75 hour = (hour if isinstance(hour, (int, float)) else 0) + 1 76 if hour > 23: 77 hour = 0 78 elif sw == 2: 79 minute = (minute if isinstance(minute, (int, float)) else 0) + 1 80 if minute > 59: 81 minute = 0 82 elif sw == 3: 83 second = (second if isinstance(second, (int, float)) else 0) + 1 84 if second > 59: 85 second = 0 86 else: 87 sw = 0 88 89 90def setup(): 91 global \ 92 page0, \ 93 label_tip, \ 94 label_hour, \ 95 label_min, \ 96 label_second, \ 97 label_dot1, \ 98 label_dot2, \ 99 rtc, \ 100 sw, \ 101 hour, \ 102 minute, \ 103 second, \ 104 last_time 105 106 M5.begin() 107 m5ui.init() 108 page0 = m5ui.M5Page(bg_c=0x000000) 109 label_tip = m5ui.M5Label( 110 "Clock", 111 x=176, 112 y=40, 113 text_c=0x10AACD, 114 bg_c=0xFFFFFF, 115 bg_opa=0, 116 font=lv.font_montserrat_40, 117 parent=page0, 118 ) 119 label_hour = m5ui.M5Label( 120 "00", 121 x=100, 122 y=208, 123 text_c=0x03C4E3, 124 bg_c=0xFFFFFF, 125 bg_opa=0, 126 font=lv.font_montserrat_48, 127 parent=page0, 128 ) 129 label_min = m5ui.M5Label( 130 "00", 131 x=200, 132 y=206, 133 text_c=0x03C4E3, 134 bg_c=0xFFFFFF, 135 bg_opa=0, 136 font=lv.font_montserrat_48, 137 parent=page0, 138 ) 139 label_second = m5ui.M5Label( 140 "00", 141 x=300, 142 y=205, 143 text_c=0x03C4E3, 144 bg_c=0xFFFFFF, 145 bg_opa=0, 146 font=lv.font_montserrat_48, 147 parent=page0, 148 ) 149 label_dot1 = m5ui.M5Label( 150 ":", 151 x=177, 152 y=202, 153 text_c=0x03C4E3, 154 bg_c=0xFFFFFF, 155 bg_opa=0, 156 font=lv.font_montserrat_48, 157 parent=page0, 158 ) 159 label_dot2 = m5ui.M5Label( 160 ":", 161 x=275, 162 y=202, 163 text_c=0x03C4E3, 164 bg_c=0xFFFFFF, 165 bg_opa=0, 166 font=lv.font_montserrat_48, 167 parent=page0, 168 ) 169 170 BtnA.setCallback(type=BtnA.CB_TYPE.WAS_CLICKED, cb=btna_was_click_event) 171 BtnB.setCallback(type=BtnB.CB_TYPE.WAS_CLICKED, cb=btnb_was_click_event) 172 173 page0.screen_load() 174 rtc = RTC() 175 hour = 11 176 minute = 10 177 second = 0 178 sw = 0 179 label_dot1.align_to(page0, lv.ALIGN.TOP_MID, -45, 201) 180 label_dot2.align_to(page0, lv.ALIGN.TOP_MID, 45, 201) 181 Speaker.begin() 182 Speaker.setVolumePercentage(0.9) 183 Speaker.tone(1000, 300) 184 185 186def loop(): 187 global \ 188 page0, \ 189 label_tip, \ 190 label_hour, \ 191 label_min, \ 192 label_second, \ 193 label_dot1, \ 194 label_dot2, \ 195 rtc, \ 196 sw, \ 197 hour, \ 198 minute, \ 199 second, \ 200 last_time 201 M5.update() 202 if (time.ticks_diff((time.ticks_ms()), last_time)) >= 500: 203 last_time = time.ticks_ms() 204 if sw == 0: 205 hour = (rtc.local_datetime())[4] 206 minute = (rtc.local_datetime())[5] 207 second = (rtc.local_datetime())[6] 208 if hour < 10: 209 label_hour.set_text(str((str("0") + str(hour)))) 210 else: 211 label_hour.set_text(str(hour)) 212 label_hour.align_to(page0, lv.ALIGN.TOP_MID, -90, 205) 213 if minute < 10: 214 label_min.set_text(str((str("0") + str(minute)))) 215 else: 216 label_min.set_text(str(minute)) 217 label_min.align_to(page0, lv.ALIGN.TOP_MID, 0, 205) 218 if second < 10: 219 label_second.set_text(str((str("0") + str(second)))) 220 else: 221 label_second.set_text(str(second)) 222 label_second.align_to(page0, lv.ALIGN.TOP_MID, 90, 205) 223 224 225if __name__ == "__main__": 226 try: 227 setup() 228 while True: 229 loop() 230 except (Exception, KeyboardInterrupt) as e: 231 try: 232 m5ui.deinit() 233 from utility import print_error_msg 234 235 print_error_msg(e) 236 except ImportError: 237 print("please update to latest firmware")
Example output:
None
Power Management
This example monitors USB, battery, and Grove port voltages, and shows charging status (battery text turns green while charging). Press BtnA to toggle Grove external output (5V OUT). Press BtnB to toggle battery charging.
MicroPython Code Block:
1import os, sys, io 2import M5 3from M5 import * 4import m5ui 5import lvgl as lv 6import time 7 8 9page0 = None 10label_title = None 11label_usb = None 12label_bat = None 13label_grove = None 14label_tip1 = None 15label_tip2 = None 16 17 18grove_en = None 19charge_en = None 20last_time = None 21 22 23def btna_was_click_event(state): 24 global \ 25 page0, \ 26 label_title, \ 27 label_usb, \ 28 label_bat, \ 29 label_grove, \ 30 label_tip1, \ 31 label_tip2, \ 32 grove_en, \ 33 charge_en, \ 34 last_time 35 Speaker.tone(1000, 200) 36 grove_en = not grove_en 37 if grove_en: 38 Power.setExtOutput(True) 39 label_grove.set_text_color(0xFF0000, 255, 0) 40 else: 41 Power.setExtOutput(False) 42 label_grove.set_text_color(0xFFFFFF, 255, 0) 43 44 45def btnb_was_click_event(state): 46 global \ 47 page0, \ 48 label_title, \ 49 label_usb, \ 50 label_bat, \ 51 label_grove, \ 52 label_tip1, \ 53 label_tip2, \ 54 grove_en, \ 55 charge_en, \ 56 last_time 57 Speaker.tone(1000, 200) 58 charge_en = not charge_en 59 if charge_en: 60 Power.setBatteryCharge(True) 61 else: 62 Power.setBatteryCharge(False) 63 64 65def setup(): 66 global \ 67 page0, \ 68 label_title, \ 69 label_usb, \ 70 label_bat, \ 71 label_grove, \ 72 label_tip1, \ 73 label_tip2, \ 74 grove_en, \ 75 charge_en, \ 76 last_time 77 78 M5.begin() 79 m5ui.init() 80 page0 = m5ui.M5Page(bg_c=0x000000) 81 label_title = m5ui.M5Label( 82 "Power test", 83 x=123, 84 y=40, 85 text_c=0x10AACD, 86 bg_c=0xFFFFFF, 87 bg_opa=0, 88 font=lv.font_montserrat_40, 89 parent=page0, 90 ) 91 label_usb = m5ui.M5Label( 92 "USB: --mV", 93 x=170, 94 y=135, 95 text_c=0xFFFFFF, 96 bg_c=0x000000, 97 bg_opa=0, 98 font=lv.font_montserrat_24, 99 parent=page0, 100 ) 101 label_bat = m5ui.M5Label( 102 "Battery: --mV", 103 x=151, 104 y=170, 105 text_c=0xFFFFFF, 106 bg_c=0x000000, 107 bg_opa=0, 108 font=lv.font_montserrat_24, 109 parent=page0, 110 ) 111 label_grove = m5ui.M5Label( 112 "Grove: --mV", 113 x=160, 114 y=205, 115 text_c=0xFFFFFF, 116 bg_c=0x000000, 117 bg_opa=0, 118 font=lv.font_montserrat_24, 119 parent=page0, 120 ) 121 label_tip1 = m5ui.M5Label( 122 "Botton A Control Grove", 123 x=94, 124 y=330, 125 text_c=0xE9E20E, 126 bg_c=0x000000, 127 bg_opa=0, 128 font=lv.font_montserrat_24, 129 parent=page0, 130 ) 131 label_tip2 = m5ui.M5Label( 132 "Botton B Control Charge", 133 x=82, 134 y=366, 135 text_c=0xE9E20E, 136 bg_c=0x000000, 137 bg_opa=0, 138 font=lv.font_montserrat_24, 139 parent=page0, 140 ) 141 142 BtnA.setCallback(type=BtnA.CB_TYPE.WAS_CLICKED, cb=btna_was_click_event) 143 BtnB.setCallback(type=BtnB.CB_TYPE.WAS_CLICKED, cb=btnb_was_click_event) 144 145 page0.screen_load() 146 Power.setBatteryCharge(True) 147 Power.setExtOutput(False) 148 grove_en = False 149 charge_en = False 150 Speaker.begin() 151 Speaker.setVolumePercentage(0.8) 152 153 154def loop(): 155 global \ 156 page0, \ 157 label_title, \ 158 label_usb, \ 159 label_bat, \ 160 label_grove, \ 161 label_tip1, \ 162 label_tip2, \ 163 grove_en, \ 164 charge_en, \ 165 last_time 166 M5.update() 167 if (time.ticks_diff((time.ticks_ms()), last_time)) >= 500: 168 last_time = time.ticks_ms() 169 label_usb.set_text(str((str("USB: ") + str((str((Power.getVBUSVoltage())) + str("mV")))))) 170 label_usb.align_to(page0, lv.ALIGN.TOP_MID, 0, 135) 171 label_bat.set_text( 172 str((str("Battery: ") + str((str((Power.getBatteryVoltage())) + str("mV"))))) 173 ) 174 label_bat.align_to(page0, lv.ALIGN.TOP_MID, 0, 170) 175 label_grove.set_text( 176 str((str("Grove: ") + str((str((Power.getExtVoltage(M5.Power.PORT.A))) + str("mV"))))) 177 ) 178 label_grove.align_to(page0, lv.ALIGN.TOP_MID, 0, 205) 179 if Power.isCharging(): 180 label_bat.set_text_color(0x33FF33, 255, 0) 181 else: 182 label_bat.set_text_color(0xFFFFFF, 255, 0) 183 184 185if __name__ == "__main__": 186 try: 187 setup() 188 while True: 189 loop() 190 except (Exception, KeyboardInterrupt) as e: 191 try: 192 m5ui.deinit() 193 from utility import print_error_msg 194 195 print_error_msg(e) 196 except ImportError: 197 print("please update to latest firmware")
Example output:
None
Audio Recording and Playback
This example demonstrates audio recording and playback. Press BtnA to start a 5-second recording (countdown shown on screen). Press BtnB to play back the recorded file when not recording. The UI shows Idle, Recording…, or Playing… status.
MicroPython Code Block:
1import os, sys, io 2import M5 3from M5 import * 4import m5ui 5import lvgl as lv 6from audio import Recorder 7import time 8from audio import Player 9 10 11page0 = None 12label_title = None 13label_count = None 14label_state = None 15label_tip1 = None 16label_tip2 = None 17recorder = None 18player = None 19 20 21flag_record = None 22flag_play = None 23record_start_time = None 24remaining = None 25RECORD_TIME = None 26record_file_path = None 27 28 29def btna_was_click_event(state): 30 global \ 31 page0, \ 32 label_title, \ 33 label_count, \ 34 label_state, \ 35 label_tip1, \ 36 label_tip2, \ 37 recorder, \ 38 player, \ 39 flag_record, \ 40 flag_play, \ 41 record_start_time, \ 42 remaining, \ 43 RECORD_TIME, \ 44 record_file_path 45 flag_record = True 46 47 48def btnb_was_click_event(state): 49 global \ 50 page0, \ 51 label_title, \ 52 label_count, \ 53 label_state, \ 54 label_tip1, \ 55 label_tip2, \ 56 recorder, \ 57 player, \ 58 flag_record, \ 59 flag_play, \ 60 record_start_time, \ 61 remaining, \ 62 RECORD_TIME, \ 63 record_file_path 64 if not (recorder.is_recording()): 65 flag_play = True 66 67 68def setup(): 69 global \ 70 page0, \ 71 label_title, \ 72 label_count, \ 73 label_state, \ 74 label_tip1, \ 75 label_tip2, \ 76 recorder, \ 77 player, \ 78 flag_record, \ 79 flag_play, \ 80 record_start_time, \ 81 remaining, \ 82 RECORD_TIME, \ 83 record_file_path 84 85 M5.begin() 86 m5ui.init() 87 page0 = m5ui.M5Page(bg_c=0x000000) 88 label_title = m5ui.M5Label( 89 "Audio Test", 90 x=128, 91 y=40, 92 text_c=0x10AACD, 93 bg_c=0x000000, 94 bg_opa=0, 95 font=lv.font_montserrat_40, 96 parent=page0, 97 ) 98 label_count = m5ui.M5Label( 99 "5", 100 x=218, 101 y=205, 102 text_c=0x10AACD, 103 bg_c=0x000000, 104 bg_opa=0, 105 font=lv.font_montserrat_48, 106 parent=page0, 107 ) 108 label_state = m5ui.M5Label( 109 "Idle", 110 x=194, 111 y=116, 112 text_c=0x10CD53, 113 bg_c=0xFFFFFF, 114 bg_opa=0, 115 font=lv.font_montserrat_40, 116 parent=page0, 117 ) 118 label_tip1 = m5ui.M5Label( 119 "Button B Play", 120 x=148, 121 y=366, 122 text_c=0xE9E20E, 123 bg_c=0x000000, 124 bg_opa=0, 125 font=lv.font_montserrat_24, 126 parent=page0, 127 ) 128 label_tip2 = m5ui.M5Label( 129 "Button A Record", 130 x=131, 131 y=330, 132 text_c=0xE9E20E, 133 bg_c=0x000000, 134 bg_opa=0, 135 font=lv.font_montserrat_24, 136 parent=page0, 137 ) 138 139 BtnA.setCallback(type=BtnA.CB_TYPE.WAS_CLICKED, cb=btna_was_click_event) 140 BtnB.setCallback(type=BtnB.CB_TYPE.WAS_CLICKED, cb=btnb_was_click_event) 141 142 page0.screen_load() 143 Mic.end() 144 Speaker.end() 145 recorder = Recorder(8000, 16, True) 146 player = Player(None) 147 player.set_vol(80) 148 RECORD_TIME = 5 149 record_file_path = "rec1.amr" 150 flag_record = False 151 flag_play = False 152 153 154def loop(): 155 global \ 156 page0, \ 157 label_title, \ 158 label_count, \ 159 label_state, \ 160 label_tip1, \ 161 label_tip2, \ 162 recorder, \ 163 player, \ 164 flag_record, \ 165 flag_play, \ 166 record_start_time, \ 167 remaining, \ 168 RECORD_TIME, \ 169 record_file_path 170 M5.update() 171 if flag_record: 172 if not (recorder.is_recording()): 173 record_start_time = time.ticks_ms() 174 label_state.set_text(str("Recording...")) 175 label_state.set_text_color(0xFF0000, 255, 0) 176 label_state.align_to(page0, lv.ALIGN.TOP_MID, 0, 116) 177 Speaker.setPA(False) 178 recorder.record("file://flash/res/audio/" + str(record_file_path), RECORD_TIME, False) 179 else: 180 remaining = ( 181 RECORD_TIME - (time.ticks_diff((time.ticks_ms()), record_start_time)) / 1000 182 ) 183 if remaining > 0: 184 label_count.set_text(str(int(remaining))) 185 else: 186 label_count.set_text(str("0")) 187 flag_record = False 188 label_state.set_text(str("Idle")) 189 label_state.align_to(page0, lv.ALIGN.TOP_MID, 0, 116) 190 label_state.set_text_color(0x33CC00, 255, 0) 191 if not (recorder.is_recording()): 192 if flag_play: 193 flag_play = False 194 label_count.set_text(str("")) 195 label_state.set_text(str("Playing...")) 196 label_state.align_to(page0, lv.ALIGN.TOP_MID, 0, 116) 197 label_state.set_text_color(0xFF0000, 255, 0) 198 Speaker.setPA(True) 199 player.play( 200 "file://flash/res/audio/" + str(record_file_path), pos=0, volume=-1, sync=False 201 ) 202 else: 203 if not (player.pos()): 204 label_state.align_to(page0, lv.ALIGN.TOP_MID, 0, 116) 205 label_count.set_text(str(RECORD_TIME)) 206 label_state.set_text(str("Idle")) 207 label_state.set_text_color(0x33CC00, 255, 0) 208 209 210if __name__ == "__main__": 211 try: 212 setup() 213 while True: 214 loop() 215 except (Exception, KeyboardInterrupt) as e: 216 try: 217 m5ui.deinit() 218 from utility import print_error_msg 219 220 print_error_msg(e) 221 except ImportError: 222 print("please update to latest firmware")
Example output:
None



