StackChan
支持以下产品:
UiFlow2 应用示例
舵机零点校准
备注
不同设备之间的机械装配会有差异。刷入新固件后,需要手动校准舵机零点参考。
在 UiFlow2 中打开 stackchan_servo_zero_calibrate.m5f2 项目。
运行程序。
手动转动头部:在 X 方向上,使显示屏与底座朝向一致;在 Y 方向上,使显示屏与底座垂直。
点击 Save 按钮。
UiFlow2 代码块:
示例输出:
None
舵机角度读取
在 UiFlow2 中打开 stackchan_servo_read_example.m5f2 项目。
此示例演示在关闭力矩的情况下读取 X 和 Y 舵机角度(单位:度),使头部可以自由转动。
备注
开启力矩控制时会保持上次目标位置,并阻碍手动转动。关闭力矩控制后可在角度实时更新时自由调整头部姿态,便于检查校准结果。
UiFlow2 代码块:
示例输出:
None
舵机控制
在 UiFlow2 中打开 stackchan_servo_control_example.m5f2 项目。
此示例演示使用 set_servo_angle 和 set_servo_x_pwm 控制舵机移动到指定位置,并以 PWM 模式驱动 X 轴舵机。
UiFlow2 代码块:
示例输出:
None
人脸跟踪
在 UiFlow2 中打开 stackchan_face_tracking_example.m5f2 项目。
此示例实现人脸跟踪。
UiFlow2 代码块:
示例输出:
None
舵机电源信息
在 UiFlow2 中打开 stackchan_servo_power_example.m5f2 项目。
此示例演示读取并显示舵机电源信息。
UiFlow2 代码块:
示例输出:
None
触摸与 RGB
在 UiFlow2 中打开 stackchan_tp_rgb_example.m5f2 项目。
此示例演示将触摸区域映射到 RGB 灯带颜色(两条灯带上的三个逻辑触摸点)。
UiFlow2 代码块:
示例输出:
None
NFC
在 UiFlow2 中打开 stackchan_nfc_detect_example.m5f2 项目。
此示例演示检测 NFC 标签,并在屏幕上显示 UID 和标签类型。完整的 NFC Unit API 参考(detect、读写、标签类型等)请参见 NFC Unit。
UiFlow2 代码块:
示例输出:
None
红外(IR)
在 UiFlow2 中打开 stackchan_ir_tx_rx_example.m5f2 项目。
此示例演示 NEC 风格的红外发送与接收。
UiFlow2 代码块:
示例输出:
None
MicroPython 应用示例
舵机零点校准
备注
不同设备之间的机械装配会有差异。刷入新固件后,需要手动校准舵机零点参考。
运行程序。
手动转动头部:在 X 方向上,使显示屏与底座朝向一致;在 Y 方向上,使显示屏与底座垂直。
点击 Save 按钮。
MicroPython 代码块:
1# SPDX-FileCopyrightText: 2026 M5Stack Technology CO LTD 2# 3# SPDX-License-Identifier: MIT 4 5import os, sys, io 6import M5 7from M5 import * 8import m5ui 9import lvgl as lv 10import time 11from hardware.stackchan import StackChan 12 13 14page0 = None 15label_title = None 16button_save = None 17label_angle_x = None 18label_angle_y = None 19label_tip = None 20stackchan = None 21x_angle = None 22y_angle = None 23last_time = None 24 25 26def button_save_short_clicked_event(event_struct): 27 global \ 28 page0, \ 29 label_title, \ 30 button_save, \ 31 label_angle_x, \ 32 label_angle_y, \ 33 label_tip, \ 34 stackchan, \ 35 x_angle, \ 36 y_angle, \ 37 last_time 38 stackchan.set_servo_zero() 39 label_tip.set_text(str("Tip: Calibration success")) 40 Speaker.tone(1000, 100) 41 42 43def button_save_event_handler(event_struct): 44 global \ 45 page0, \ 46 label_title, \ 47 button_save, \ 48 label_angle_x, \ 49 label_angle_y, \ 50 label_tip, \ 51 stackchan, \ 52 x_angle, \ 53 y_angle, \ 54 last_time 55 event = event_struct.code 56 if event == lv.EVENT.SHORT_CLICKED and True: 57 button_save_short_clicked_event(event_struct) 58 return 59 60 61def setup(): 62 global \ 63 page0, \ 64 label_title, \ 65 button_save, \ 66 label_angle_x, \ 67 label_angle_y, \ 68 label_tip, \ 69 stackchan, \ 70 x_angle, \ 71 y_angle, \ 72 last_time 73 74 M5.begin() 75 Widgets.setRotation(1) 76 m5ui.init() 77 page0 = m5ui.M5Page(bg_c=0x000000) 78 label_title = m5ui.M5Label( 79 "Servo Calibration", 80 x=55, 81 y=5, 82 text_c=0x0DC9F4, 83 bg_c=0x000000, 84 bg_opa=0, 85 font=lv.font_montserrat_24, 86 parent=page0, 87 ) 88 button_save = m5ui.M5Button( 89 text="Save", 90 x=128, 91 y=195, 92 bg_c=0x2196F3, 93 text_c=0xFFFFFF, 94 font=lv.font_montserrat_14, 95 parent=page0, 96 ) 97 label_angle_x = m5ui.M5Label( 98 "X-Axis Servo Angle:", 99 x=10, 100 y=130, 101 text_c=0x0DC9F4, 102 bg_c=0x000000, 103 bg_opa=0, 104 font=lv.font_montserrat_18, 105 parent=page0, 106 ) 107 label_angle_y = m5ui.M5Label( 108 "Y-Axis Servo Angle:", 109 x=8, 110 y=160, 111 text_c=0x0DC9F4, 112 bg_c=0x000000, 113 bg_opa=0, 114 font=lv.font_montserrat_18, 115 parent=page0, 116 ) 117 label_tip = m5ui.M5Label( 118 "Tip:Move by hand, tap Save.", 119 x=33, 120 y=70, 121 text_c=0xD2E711, 122 bg_c=0xFFFFFF, 123 bg_opa=0, 124 font=lv.font_montserrat_18, 125 parent=page0, 126 ) 127 128 button_save.add_event_cb(button_save_event_handler, lv.EVENT.ALL, None) 129 130 stackchan = StackChan(i2c=1, uart=1) 131 page0.screen_load() 132 stackchan.set_servo_power(enable=True) 133 stackchan.set_servo_torque(stackchan.SERVO_ID_X, enable=False) 134 stackchan.set_servo_torque(stackchan.SERVO_ID_Y, enable=False) 135 Speaker.begin() 136 Speaker.setVolumePercentage(0.6) 137 Speaker.tone(1000, 100) 138 139 140def loop(): 141 global \ 142 page0, \ 143 label_title, \ 144 button_save, \ 145 label_angle_x, \ 146 label_angle_y, \ 147 label_tip, \ 148 stackchan, \ 149 x_angle, \ 150 y_angle, \ 151 last_time 152 M5.update() 153 if (time.ticks_diff((time.ticks_ms()), last_time)) >= 100: 154 x_angle = stackchan.get_servo_angle(stackchan.SERVO_ID_X) 155 y_angle = stackchan.get_servo_angle(stackchan.SERVO_ID_Y) 156 label_angle_x.set_text(str((str("X-Axis Servo Angle:") + str(x_angle)))) 157 label_angle_y.set_text(str((str("Y-Axis Servo Angle:") + str(y_angle)))) 158 159 160if __name__ == "__main__": 161 try: 162 setup() 163 while True: 164 loop() 165 except (Exception, KeyboardInterrupt) as e: 166 try: 167 m5ui.deinit() 168 from utility import print_error_msg 169 170 print_error_msg(e) 171 except ImportError: 172 print("please update to latest firmware")
示例输出:
None
舵机角度读取
此示例演示在关闭力矩的情况下读取 X 和 Y 舵机角度(单位:度),使头部可以自由转动。
备注
开启力矩控制时会保持上次目标位置,并阻碍手动转动。关闭力矩控制后可在角度实时更新时自由调整头部姿态,便于检查校准结果。
MicroPython 代码块:
1import os, sys, io 2import M5 3from M5 import * 4import m5ui 5import lvgl as lv 6import time 7from hardware.stackchan import StackChan 8 9 10page0 = None 11label_title = None 12label_agnle_x = None 13label_angle_y = None 14stackchan = None 15last_time = None 16x_angle = None 17y_angle = None 18 19 20def setup(): 21 global page0, label_title, label_agnle_x, label_angle_y, stackchan, last_time, x_angle, y_angle 22 23 M5.begin() 24 Widgets.setRotation(1) 25 m5ui.init() 26 page0 = m5ui.M5Page(bg_c=0x000000) 27 label_title = m5ui.M5Label( 28 "Servo Read Example", 29 x=34, 30 y=10, 31 text_c=0x0DC9F4, 32 bg_c=0x000000, 33 bg_opa=0, 34 font=lv.font_montserrat_24, 35 parent=page0, 36 ) 37 label_agnle_x = m5ui.M5Label( 38 "X-Axis Servo Angle:", 39 x=10, 40 y=80, 41 text_c=0x0DC9F4, 42 bg_c=0x000000, 43 bg_opa=0, 44 font=lv.font_montserrat_24, 45 parent=page0, 46 ) 47 label_angle_y = m5ui.M5Label( 48 "Y-Axis Servo Angle:", 49 x=10, 50 y=125, 51 text_c=0x0DC9F4, 52 bg_c=0x000000, 53 bg_opa=0, 54 font=lv.font_montserrat_24, 55 parent=page0, 56 ) 57 58 stackchan = StackChan(i2c=1, uart=1) 59 page0.screen_load() 60 stackchan.set_servo_power(enable=True) 61 stackchan.set_servo_torque(stackchan.SERVO_ID_X, enable=False) 62 stackchan.set_servo_torque(stackchan.SERVO_ID_Y, enable=False) 63 64 65def loop(): 66 global page0, label_title, label_agnle_x, label_angle_y, stackchan, last_time, x_angle, y_angle 67 M5.update() 68 if (time.ticks_diff((time.ticks_ms()), last_time)) >= 100: 69 last_time = time.ticks_ms() 70 x_angle = stackchan.get_servo_angle(stackchan.SERVO_ID_X) 71 y_angle = stackchan.get_servo_angle(stackchan.SERVO_ID_Y) 72 label_agnle_x.set_text(str((str("X-Axis Servo Angle: ") + str(x_angle)))) 73 label_angle_y.set_text(str((str("Y-Axis Servo Angle: ") + str(y_angle)))) 74 75 76if __name__ == "__main__": 77 try: 78 setup() 79 while True: 80 loop() 81 except (Exception, KeyboardInterrupt) as e: 82 try: 83 m5ui.deinit() 84 from utility import print_error_msg 85 86 print_error_msg(e) 87 except ImportError: 88 print("please update to latest firmware")
示例输出:
None
舵机控制
此示例演示使用 set_servo_angle 和 set_servo_x_pwm 控制舵机移动到指定位置,并以 PWM 模式驱动 X 轴舵机。
MicroPython 代码块:
1# SPDX-FileCopyrightText: 2026 M5Stack Technology CO LTD 2# 3# SPDX-License-Identifier: MIT 4 5import os, sys, io 6import M5 7from M5 import * 8import m5ui 9import lvgl as lv 10import time 11from hardware.stackchan import StackChan 12 13 14page0 = None 15label_title = None 16label_status = None 17stackchan = None 18 19 20def setup(): 21 global page0, label_title, label_status, stackchan 22 23 M5.begin() 24 Widgets.setRotation(1) 25 m5ui.init() 26 page0 = m5ui.M5Page(bg_c=0x000000) 27 label_title = m5ui.M5Label( 28 "Servo Control Example", 29 x=20, 30 y=5, 31 text_c=0x0DC9F4, 32 bg_c=0x000000, 33 bg_opa=0, 34 font=lv.font_montserrat_24, 35 parent=page0, 36 ) 37 label_status = m5ui.M5Label( 38 "--", 39 x=153, 40 y=115, 41 text_c=0x0DC9F4, 42 bg_c=0xFFFFFF, 43 bg_opa=0, 44 font=lv.font_montserrat_16, 45 parent=page0, 46 ) 47 48 stackchan = StackChan(i2c=1, uart=1) 49 page0.screen_load() 50 Speaker.begin() 51 Speaker.setVolumePercentage(0.5) 52 stackchan.set_servo_power(enable=True) 53 stackchan.set_servo_torque(stackchan.SERVO_ID_X, enable=True) 54 stackchan.set_servo_torque(stackchan.SERVO_ID_X, enable=True) 55 stackchan.set_servo_angle(stackchan.SERVO_ID_X, 0, 1000, 0) 56 stackchan.set_servo_angle(stackchan.SERVO_ID_Y, 45, 1000, 0) 57 Speaker.tone(678, 300) 58 time.sleep_ms(2000) 59 label_status.set_text(str("Rotate counterclockwise")) 60 label_status.align_to(page0, lv.ALIGN.CENTER, 0, 0) 61 stackchan.set_servo_x_pwm(-50) 62 time.sleep_ms(3000) 63 label_status.set_text(str("Rotate clockwise")) 64 label_status.align_to(page0, lv.ALIGN.CENTER, 0, 0) 65 stackchan.set_servo_x_pwm(50) 66 time.sleep_ms(3000) 67 label_status.set_text(str("Go back to center")) 68 label_status.align_to(page0, lv.ALIGN.CENTER, 0, 0) 69 stackchan.set_servo_angle(stackchan.SERVO_ID_X, 0, 1000, 0) 70 71 72def loop(): 73 global page0, label_title, label_status, stackchan 74 M5.update() 75 76 77if __name__ == "__main__": 78 try: 79 setup() 80 while True: 81 loop() 82 except (Exception, KeyboardInterrupt) as e: 83 try: 84 m5ui.deinit() 85 from utility import print_error_msg 86 87 print_error_msg(e) 88 except ImportError: 89 print("please update to latest firmware")
示例输出:
None
人脸跟踪
案例实现人脸跟踪功能
MicroPython 代码块:
1import os, sys, io 2import M5 3from M5 import * 4import camera 5import dl 6import image 7from hardware.stackchan import StackChan 8 9 10stackchan = None 11 12 13import math 14 15img = None 16dl_detection_result = None 17dl_detector = None 18lost_frame = None 19res = None 20bbox = None 21f_x = None 22f_y = None 23neutral = None 24SMOOTH = None 25f_w = None 26DEADZONE_NORM = None 27f_h = None 28Y_NEUTRAL = None 29f_cx = None 30img_cx = None 31f_cy = None 32img_cy = None 33ex = None 34angle_x = None 35ey = None 36angle_y = None 37x_target = None 38y_target = None 39 40 41def setup(): 42 global \ 43 stackchan, \ 44 img, \ 45 dl_detection_result, \ 46 dl_detector, \ 47 lost_frame, \ 48 res, \ 49 bbox, \ 50 f_x, \ 51 f_y, \ 52 neutral, \ 53 SMOOTH, \ 54 f_w, \ 55 DEADZONE_NORM, \ 56 f_h, \ 57 Y_NEUTRAL, \ 58 f_cx, \ 59 img_cx, \ 60 f_cy, \ 61 img_cy, \ 62 ex, \ 63 angle_x, \ 64 ey, \ 65 angle_y, \ 66 x_target, \ 67 y_target 68 69 M5.begin() 70 Widgets.setRotation(1) 71 Widgets.fillScreen(0x222222) 72 73 stackchan = StackChan(i2c=1, uart=1) 74 camera.init(pixformat=camera.RGB565, framesize=camera.QVGA) 75 dl_detector = dl.ObjectDetector(dl.model.HUMAN_FACE_DETECT) 76 stackchan.set_servo_power(enable=True) 77 stackchan.set_servo_torque(stackchan.SERVO_ID_X, enable=True) 78 stackchan.set_servo_torque(stackchan.SERVO_ID_Y, enable=True) 79 stackchan.set_servo_angle(stackchan.SERVO_ID_X, 0, 0, 0) 80 stackchan.set_servo_angle(stackchan.SERVO_ID_Y, 45, 0, 0) 81 SMOOTH = 0.1 82 DEADZONE_NORM = 0.06 83 Y_NEUTRAL = 45 84 img_cx = 160 85 img_cy = 120 86 angle_x = 0 87 angle_y = Y_NEUTRAL 88 neutral = True 89 90 91def loop(): 92 global \ 93 stackchan, \ 94 img, \ 95 dl_detection_result, \ 96 dl_detector, \ 97 lost_frame, \ 98 res, \ 99 bbox, \ 100 f_x, \ 101 f_y, \ 102 neutral, \ 103 SMOOTH, \ 104 f_w, \ 105 DEADZONE_NORM, \ 106 f_h, \ 107 Y_NEUTRAL, \ 108 f_cx, \ 109 img_cx, \ 110 f_cy, \ 111 img_cy, \ 112 ex, \ 113 angle_x, \ 114 ey, \ 115 angle_y, \ 116 x_target, \ 117 y_target 118 M5.update() 119 img = camera.snapshot() 120 dl_detection_result = dl_detector.infer(img) 121 if dl_detection_result: 122 lost_frame = 0 123 res = dl_detection_result[0] 124 bbox = res.bbox() 125 f_x = res.x() 126 f_y = res.y() 127 f_w = res.w() 128 f_h = res.h() 129 f_cx = int(f_x + f_w * 0.5) 130 f_cy = int(f_y + f_h * 0.5) 131 ex = (f_cx - img_cx) / img_cx 132 ey = (f_cy - img_cy) / img_cy 133 if math.fabs(ex) < DEADZONE_NORM: 134 ex = 0 135 if math.fabs(ey) < DEADZONE_NORM: 136 ey = 0 137 x_target = min(max(ex * -135, -135), 135) 138 y_target = min(max(Y_NEUTRAL - Y_NEUTRAL * ey, 0), 90) 139 angle_x = angle_x + SMOOTH * (x_target - angle_x) 140 angle_y = angle_y + SMOOTH * (y_target - angle_y) 141 stackchan.set_servo_angle(stackchan.SERVO_ID_X, angle_x, 100, 0) 142 stackchan.set_servo_angle(stackchan.SERVO_ID_Y, angle_y, 100, 0) 143 neutral = False 144 lost_frame = 0 145 img.draw_rectangle(f_x, f_y, f_w, f_h, color=0x6600CC, thickness=3, fill=False) 146 else: 147 lost_frame = (lost_frame if isinstance(lost_frame, (int, float)) else 0) + 1 148 if lost_frame > 20 and not neutral: 149 stackchan.set_servo_angle(stackchan.SERVO_ID_X, 0, 1000, 0) 150 stackchan.set_servo_angle(stackchan.SERVO_ID_Y, 45, 1000, 0) 151 neutral = True 152 M5.Lcd.show(img, 0, 0, 320, 240) 153 154 155if __name__ == "__main__": 156 try: 157 setup() 158 while True: 159 loop() 160 except (Exception, KeyboardInterrupt) as e: 161 try: 162 from utility import print_error_msg 163 164 print_error_msg(e) 165 except ImportError: 166 print("please update to latest firmware")
示例输出:
None
舵机电源信息
此示例演示读取并显示舵机电源信息。
MicroPython 代码块:
1# SPDX-FileCopyrightText: 2026 M5Stack Technology CO LTD 2# 3# SPDX-License-Identifier: MIT 4 5import os, sys, io 6import M5 7from M5 import * 8import m5ui 9import lvgl as lv 10import time 11from hardware.stackchan import StackChan 12 13 14page0 = None 15label_title = None 16label_voltage = None 17label_current = None 18label_power = None 19stackchan = None 20last_time = None 21volatge = None 22current = None 23power = None 24 25 26def setup(): 27 global \ 28 page0, \ 29 label_title, \ 30 label_voltage, \ 31 label_current, \ 32 label_power, \ 33 stackchan, \ 34 last_time, \ 35 volatge, \ 36 current, \ 37 power 38 39 M5.begin() 40 Widgets.setRotation(1) 41 m5ui.init() 42 page0 = m5ui.M5Page(bg_c=0x000000) 43 label_title = m5ui.M5Label( 44 "Servo Power Info", 45 x=56, 46 y=5, 47 text_c=0x0DC9F4, 48 bg_c=0x000000, 49 bg_opa=0, 50 font=lv.font_montserrat_24, 51 parent=page0, 52 ) 53 label_voltage = m5ui.M5Label( 54 "Voltage:", 55 x=10, 56 y=80, 57 text_c=0x0DC9F4, 58 bg_c=0x000000, 59 bg_opa=0, 60 font=lv.font_montserrat_24, 61 parent=page0, 62 ) 63 label_current = m5ui.M5Label( 64 "Current:", 65 x=10, 66 y=115, 67 text_c=0x0DC9F4, 68 bg_c=0x000000, 69 bg_opa=0, 70 font=lv.font_montserrat_24, 71 parent=page0, 72 ) 73 label_power = m5ui.M5Label( 74 "Power:", 75 x=25, 76 y=150, 77 text_c=0x0DC9F4, 78 bg_c=0x000000, 79 bg_opa=0, 80 font=lv.font_montserrat_24, 81 parent=page0, 82 ) 83 84 stackchan = StackChan(i2c=1, uart=1) 85 page0.screen_load() 86 87 88def loop(): 89 global \ 90 page0, \ 91 label_title, \ 92 label_voltage, \ 93 label_current, \ 94 label_power, \ 95 stackchan, \ 96 last_time, \ 97 volatge, \ 98 current, \ 99 power 100 M5.update() 101 if (time.ticks_diff((time.ticks_ms()), last_time)) >= 200: 102 last_time = time.ticks_ms() 103 volatge = stackchan.get_battery_voltage() 104 current = stackchan.get_battery_current() 105 power = stackchan.get_battery_power() 106 label_voltage.set_text(str((str("Voltage: ") + str((str(volatge) + str(" V")))))) 107 label_current.set_text(str((str("Current: ") + str((str(current) + str(" A")))))) 108 label_power.set_text(str((str("Power: ") + str((str(power) + str(" W")))))) 109 110 111if __name__ == "__main__": 112 try: 113 setup() 114 while True: 115 loop() 116 except (Exception, KeyboardInterrupt) as e: 117 try: 118 m5ui.deinit() 119 from utility import print_error_msg 120 121 print_error_msg(e) 122 except ImportError: 123 print("please update to latest firmware")
示例输出:
None
触摸与 RGB
此示例演示将触摸区域映射到 RGB 灯带颜色(两条灯带上的三个逻辑触摸点)。
MicroPython 代码块:
1# SPDX-FileCopyrightText: 2026 M5Stack Technology CO LTD 2# 3# SPDX-License-Identifier: MIT 4 5import os, sys, io 6import M5 7from M5 import * 8import m5ui 9import lvgl as lv 10import time 11from hardware.stackchan import StackChan 12 13 14page0 = None 15label_title = None 16stackchan = None 17tp = None 18tp1 = None 19last_time = None 20tp2 = None 21tp3 = None 22 23 24def setup(): 25 global page0, label_title, stackchan, tp, tp1, last_time, tp2, tp3 26 27 M5.begin() 28 Widgets.setRotation(1) 29 m5ui.init() 30 page0 = m5ui.M5Page(bg_c=0x000000) 31 label_title = m5ui.M5Label( 32 "TP & RGB Strip Example", 33 x=13, 34 y=10, 35 text_c=0x0DC9F4, 36 bg_c=0x000000, 37 bg_opa=0, 38 font=lv.font_montserrat_24, 39 parent=page0, 40 ) 41 42 stackchan = StackChan(i2c=1, uart=1) 43 page0.screen_load() 44 last_time = [0, 0, 0] 45 Speaker.begin() 46 Speaker.setVolumePercentage(0.5) 47 48 49def loop(): 50 global page0, label_title, stackchan, tp, tp1, last_time, tp2, tp3 51 M5.update() 52 tp = stackchan.get_touch() 53 tp1 = tp[0] 54 tp2 = tp[1] 55 tp3 = tp[2] 56 if tp1: 57 last_time[0] = time.ticks_ms() 58 stackchan.set_rgb_color(0, 0, 0x33CC00) 59 stackchan.set_rgb_color(0, 1, 0x33CC00) 60 stackchan.set_rgb_color(1, 0, 0x33CC00) 61 stackchan.set_rgb_color(1, 1, 0x33CC00) 62 Speaker.tone(700, 50) 63 else: 64 if (time.ticks_diff((time.ticks_ms()), (last_time[0]))) > 300: 65 stackchan.set_rgb_color(0, 0, 0x000000) 66 stackchan.set_rgb_color(0, 1, 0x000000) 67 stackchan.set_rgb_color(1, 0, 0x000000) 68 stackchan.set_rgb_color(1, 1, 0x000000) 69 if tp2: 70 last_time[1] = time.ticks_ms() 71 stackchan.set_rgb_color(0, 2, 0x00CCCC) 72 stackchan.set_rgb_color(0, 3, 0x00CCCC) 73 stackchan.set_rgb_color(1, 2, 0x00CCCC) 74 stackchan.set_rgb_color(1, 3, 0x00CCCC) 75 Speaker.tone(900, 50) 76 else: 77 if (time.ticks_diff((time.ticks_ms()), (last_time[1]))) > 300: 78 stackchan.set_rgb_color(0, 2, 0x000000) 79 stackchan.set_rgb_color(0, 3, 0x000000) 80 stackchan.set_rgb_color(1, 2, 0x000000) 81 stackchan.set_rgb_color(1, 3, 0x000000) 82 if tp3: 83 last_time[2] = time.ticks_ms() 84 stackchan.set_rgb_color(0, 4, 0x000099) 85 stackchan.set_rgb_color(0, 5, 0x000099) 86 stackchan.set_rgb_color(1, 4, 0x000099) 87 stackchan.set_rgb_color(1, 5, 0x000099) 88 Speaker.tone(1100, 50) 89 else: 90 if (time.ticks_diff((time.ticks_ms()), (last_time[2]))) > 300: 91 stackchan.set_rgb_color(0, 4, 0x000000) 92 stackchan.set_rgb_color(0, 5, 0x000000) 93 stackchan.set_rgb_color(1, 4, 0x000000) 94 stackchan.set_rgb_color(1, 5, 0x000000) 95 96 97if __name__ == "__main__": 98 try: 99 setup() 100 while True: 101 loop() 102 except (Exception, KeyboardInterrupt) as e: 103 try: 104 m5ui.deinit() 105 from utility import print_error_msg 106 107 print_error_msg(e) 108 except ImportError: 109 print("please update to latest firmware")
示例输出:
None
NFC
此示例演示检测 NFC 标签,并在屏幕上显示 UID 和标签类型。完整的 NFC Unit API 参考(detect、读写、标签类型等)请参见 NFC Unit。
MicroPython 代码块:
1# SPDX-FileCopyrightText: 2026 M5Stack Technology CO LTD 2# 3# SPDX-License-Identifier: MIT 4 5import os, sys, io 6import M5 7from M5 import * 8import m5ui 9import lvgl as lv 10import time 11from hardware.stackchan import StackChan 12 13 14page0 = None 15label_title = None 16label_uid = None 17label_type = None 18label_size = None 19stackchan = None 20card_0 = None 21card_uid = None 22new = None 23card_type = None 24card_size = None 25last_time = None 26 27 28def setup(): 29 global \ 30 page0, \ 31 label_title, \ 32 label_uid, \ 33 label_type, \ 34 label_size, \ 35 stackchan, \ 36 card_0, \ 37 card_uid, \ 38 new, \ 39 card_type, \ 40 card_size, \ 41 last_time 42 43 M5.begin() 44 Widgets.setRotation(1) 45 m5ui.init() 46 page0 = m5ui.M5Page(bg_c=0x000000) 47 label_title = m5ui.M5Label( 48 "NFC Card detect", 49 x=58, 50 y=5, 51 text_c=0x13C2EB, 52 bg_c=0xFFFFFF, 53 bg_opa=0, 54 font=lv.font_montserrat_24, 55 parent=page0, 56 ) 57 label_uid = m5ui.M5Label( 58 "UID:", 59 x=18, 60 y=70, 61 text_c=0xFFFFFF, 62 bg_c=0xFFFFFF, 63 bg_opa=0, 64 font=lv.font_montserrat_16, 65 parent=page0, 66 ) 67 label_type = m5ui.M5Label( 68 "Tyep:", 69 x=10, 70 y=100, 71 text_c=0xFFFFFF, 72 bg_c=0xFFFFFF, 73 bg_opa=0, 74 font=lv.font_montserrat_16, 75 parent=page0, 76 ) 77 label_size = m5ui.M5Label( 78 "Size:", 79 x=16, 80 y=130, 81 text_c=0xFFFFFF, 82 bg_c=0xFFFFFF, 83 bg_opa=0, 84 font=lv.font_montserrat_16, 85 parent=page0, 86 ) 87 88 page0.screen_load() 89 stackchan = StackChan(i2c=1, uart=1) 90 Speaker.begin() 91 Speaker.setVolumePercentage(0.6) 92 93 94def loop(): 95 global \ 96 page0, \ 97 label_title, \ 98 label_uid, \ 99 label_type, \ 100 label_size, \ 101 stackchan, \ 102 card_0, \ 103 card_uid, \ 104 new, \ 105 card_type, \ 106 card_size, \ 107 last_time 108 M5.update() 109 card_0 = stackchan.nfc.detect() 110 if card_0: 111 card_uid = card_0.uid_str 112 card_type = card_0.type_name 113 card_size = card_0.user_memory 114 label_uid.set_text(str((str("UID: ") + str(card_uid)))) 115 label_type.set_text(str((str("Tyep: ") + str(card_type)))) 116 label_size.set_text(str((str("Size: ") + str(card_size)))) 117 if (time.ticks_diff((time.ticks_ms()), last_time)) >= 3000 or new: 118 last_time = time.ticks_ms() 119 stackchan.set_rgb_color(0x009900) 120 Speaker.tone(1234, 100) 121 time.sleep_ms(100) 122 stackchan.set_rgb_color(0x000000) 123 new = False 124 else: 125 new = True 126 127 128if __name__ == "__main__": 129 try: 130 setup() 131 while True: 132 loop() 133 except (Exception, KeyboardInterrupt) as e: 134 try: 135 m5ui.deinit() 136 from utility import print_error_msg 137 138 print_error_msg(e) 139 except ImportError: 140 print("please update to latest firmware")
示例输出:
None
红外(IR)
此示例演示 NEC 风格的红外发送与接收。
MicroPython 代码块:
1# SPDX-FileCopyrightText: 2026 M5Stack Technology CO LTD 2# 3# SPDX-License-Identifier: MIT 4 5import os, sys, io 6import M5 7from M5 import * 8from hardware import IR 9from hardware.stackchan import StackChan 10 11 12label_title = None 13label_tx_addr = None 14label_tx_data = None 15label_rx_addr = None 16label_rx_data = None 17ir = None 18stackchan = None 19ir_data = None 20ir_addr = None 21tx_data = None 22ir_tx = None 23tx_addr = None 24 25 26def ir_rx_event(_data, _addr, _ctrl): 27 global \ 28 label_title, \ 29 label_tx_addr, \ 30 label_tx_data, \ 31 label_rx_addr, \ 32 label_rx_data, \ 33 ir, \ 34 stackchan, \ 35 ir_data, \ 36 ir_addr, \ 37 tx_data, \ 38 ir_tx, \ 39 tx_addr 40 ir_data = _data 41 ir_addr = _addr 42 label_rx_addr.setText(str((str("RX Addr: ") + str(ir_addr)))) 43 label_rx_data.setText(str((str("RX Data: ") + str(ir_data)))) 44 Speaker.tone(700, 100) 45 46 47def btn_pwr_was_clicked_event(state): 48 global \ 49 label_title, \ 50 label_tx_addr, \ 51 label_tx_data, \ 52 label_rx_addr, \ 53 label_rx_data, \ 54 ir, \ 55 stackchan, \ 56 ir_data, \ 57 ir_addr, \ 58 tx_data, \ 59 ir_tx, \ 60 tx_addr 61 tx_data = (tx_data if isinstance(tx_data, (int, float)) else 0) + 1 62 if tx_data > 255: 63 tx_data = 0 64 ir.tx(tx_addr, tx_data) 65 label_tx_addr.setText(str((str("TX Addr: ") + str(tx_addr)))) 66 label_tx_data.setText(str((str("TX Data: ") + str(tx_data)))) 67 68 69def setup(): 70 global \ 71 label_title, \ 72 label_tx_addr, \ 73 label_tx_data, \ 74 label_rx_addr, \ 75 label_rx_data, \ 76 ir, \ 77 stackchan, \ 78 ir_data, \ 79 ir_addr, \ 80 tx_data, \ 81 ir_tx, \ 82 tx_addr 83 84 M5.begin() 85 Widgets.setRotation(1) 86 Widgets.fillScreen(0x000000) 87 label_title = Widgets.Label( 88 "IR TX & RX Example", 41, 5, 1.0, 0x0DC9F4, 0x000000, Widgets.FONTS.Montserrat24 89 ) 90 label_tx_addr = Widgets.Label( 91 "TX Addr:", 9, 59, 1.0, 0xFFFFFF, 0x000000, Widgets.FONTS.Montserrat18 92 ) 93 label_tx_data = Widgets.Label( 94 "TX Data:", 170, 59, 1.0, 0xFFFFFF, 0x000000, Widgets.FONTS.Montserrat18 95 ) 96 label_rx_addr = Widgets.Label( 97 "RX Addr:", 10, 100, 1.0, 0xFFFFFF, 0x000000, Widgets.FONTS.Montserrat18 98 ) 99 label_rx_data = Widgets.Label( 100 "RX Data:", 170, 100, 1.0, 0xFFFFFF, 0x000000, Widgets.FONTS.Montserrat18 101 ) 102 103 BtnPWR.setCallback(type=BtnPWR.CB_TYPE.WAS_CLICKED, cb=btn_pwr_was_clicked_event) 104 105 stackchan = StackChan(i2c=1, uart=1) 106 ir = IR() 107 ir.rx_cb(ir_rx_event) 108 tx_addr = 1 109 tx_data = 0 110 Speaker.begin() 111 Speaker.setVolumePercentage(0.5) 112 label_tx_addr.setText(str((str("TX Addr: ") + str(tx_addr)))) 113 114 115def loop(): 116 global \ 117 label_title, \ 118 label_tx_addr, \ 119 label_tx_data, \ 120 label_rx_addr, \ 121 label_rx_data, \ 122 ir, \ 123 stackchan, \ 124 ir_data, \ 125 ir_addr, \ 126 tx_data, \ 127 ir_tx, \ 128 tx_addr 129 M5.update() 130 if ir_tx: 131 ir_tx = False 132 133 134if __name__ == "__main__": 135 try: 136 setup() 137 while True: 138 loop() 139 except (Exception, KeyboardInterrupt) as e: 140 try: 141 from utility import print_error_msg 142 143 print_error_msg(e) 144 except ImportError: 145 print("please update to latest firmware")
示例输出:
None
API 参考
StackChan
- class hardware.stackchan.StackChan
StackChan 板级驱动:包括 UART 上的 SCS 串口舵机、M5IOE1 上的 RGB 和舵机供电、Si12T 触摸、INA226(电池总线),以及作为
unit.nfc.NFCUnit的板载 NFC(ST25R3916)。该类是**单例**;请始终使用相同的
i2c和uartid 进行构造。After init, the instance exposes
nfc(aunit.nfc.NFCUnit—see NFC Unit for the complete API),touch,i2c, and low-levelservo(Scsclinstance) for advanced use.模块常量包括
SERVO_ID_X``(``1)、SERVO_ID_Y``(``2)及相关限制值;这些也可作为StackChan的类属性使用。UiFlow2 代码块:

MicroPython 代码块:
from hardware.stackchan import StackChan, SERVO_ID_X, SERVO_ID_Y sc = StackChan(i2c=1, uart=1)
- set_servo_zero()
将两个轴的逻辑**零点**保存到 NVS 中(命名空间
servo,键名zero_pos_1/zero_pos_2)。UiFlow2 代码块:

MicroPython 代码块:
sc.set_servo_zero()
- set_servo_power(enable=True)
通过 IO 扩展器启用或关闭舵机供电。
- 参数:
enable (bool) – 打开或关闭电源。
UiFlow2 代码块:

MicroPython 代码块:
sc.set_servo_power(True)
- set_servo_torque(servo_id, enable=True)
启用或禁用单个舵机的力矩。
UiFlow2 代码块:

MicroPython 代码块:
sc.set_servo_torque(SERVO_ID_X, True)
- set_servo_angle(servo_id, angle_deg, time_ms=10, speed=0)
将指定舵机移动到
angle_deg``(度)。X 轴(``SERVO_ID_X/ 平移)建议范围约为 -135°~135°,Y 轴(SERVO_ID_Y/ 俯仰)建议范围为 0°~90°。- 参数:
UiFlow2 代码块:


MicroPython 代码块:
sc.set_servo_angle(SERVO_ID_X, 0.0, 500, 0) sc.set_servo_angle(SERVO_ID_X, 0.0, 0, 50)
- get_servo_angle(servo_id)
读取舵机角度(单位:度)。
- 参数:
servo_id (int) –
SERVO_ID_X或SERVO_ID_Y。- 返回:
Angle in degrees, or
Noneif the read failed.
UiFlow2 代码块:

MicroPython 代码块:
deg = sc.get_servo_angle(SERVO_ID_X)
- set_servo_x_pwm(value)
以 PWM 模式驱动 X 轴舵机进行连续转动。用户范围为 -100~100;符号表示旋转方向,绝对值表示驱动力大小。
- 参数:
value (int) – 带符号的 PWM 强度(会被限制在范围内)。正负值表示相反的旋转方向;
0表示停止输出。
UiFlow2 代码块:

MicroPython 代码块:
sc.set_servo_x_pwm(50)
- set_rgb_color(*args)
设置灯带上的 RGB LED。
一个参数:将所有 LED 填充为
color。两个参数:
strip``(``0或1)和该逻辑灯带对应的color。三个参数:
strip、index、color,用于设置单颗 LED(strip为1时,index顺序与驱动一致)。
- 返回:
Trueon success where applicable.
UiFlow2 代码块:



MicroPython 代码块:
sc.set_rgb_color(0x00FF00) sc.set_rgb_color(0, 0x0000FF) sc.set_rgb_color(0, 0, 0xFF0000)
- get_rgb_color(strip, index)
获取单颗 LED 的 RGB 颜色。
UiFlow2 代码块:

MicroPython 代码块:
r, g, b = sc.get_rgb_color(0, 0)
- get_touch(index=None)
读取触摸状态(三个逻辑槽位)。
- 参数:
index (int) – 如果为
None,返回包含三个电平值的列表;如果为0、1或2,则返回对应槽位的电平值。- 返回:
返回
OUTPUT_NONE…OUTPUT_HIGH这类值;失败时返回None。
UiFlow2 代码块:


MicroPython 代码块:
tp = sc.get_touch() one = sc.get_touch(0)
- get_battery_voltage()
来自 INA226 的总线电压(伏特)。
- 返回:
floatorNoneif unavailable.
UiFlow2 代码块:

MicroPython 代码块:
v = sc.get_battery_voltage()
- get_battery_current()
来自 INA226 的电流(安培)。
- 返回:
floatorNone.
UiFlow2 代码块:

MicroPython 代码块:
a = sc.get_battery_current()
- get_battery_power()
当电压和电流都有效时,返回 INA226 的功率(瓦特)。
- 返回:
floatorNone.
UiFlow2 代码块:

MicroPython 代码块:
p = sc.get_battery_power()







