StopWatch

Support the following products:

StopWatch

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:

stopwatch_rtc_example.png

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:

stopwatch_power_example.png

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:

stopwatch_aduio_example.png

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