Python+ESP嵌入式开发快速上手
环境搭建
MicroPython介绍
MicroPython 是一种精简版的 Python 编程语言,专门设计用于嵌入式系统和物联网(IoT)开发。它提供了一个 Python 3 的子集,适用于资源受限的微控制器和单片机环境。MicroPython 允许开发人员使用 Python 的简洁语法和强大功能来编写嵌入式系统的代码。
以下是一些关于 MicroPython 的要点:
-
特点:
- 小巧灵活:MicroPython 是一种轻量级的实现,适用于资源有限的设备。
- 易于学习:对于已经熟悉 Python 的开发者来说,学习 MicroPython 是相对简单的。
- 交互式 REPL:MicroPython 提供了交互式的 REPL(Read-Eval-Print Loop),方便调试和快速原型开发。
- 面向对象编程:支持面向对象编程范例,包括类、对象和继承等概念。
-
用途:
- MicroPython 适用于许多嵌入式系统和物联网应用的开发,如传感器网络、自动化控制、物联网设备等。
- 它可以在各种微控制器和单片机平台上运行,例如 ESP8266、ESP32、STM32 等。
-
如何开始:
- 您可以从 MicroPython 官方网站(https://micropython.org/)下载适用于您的目标平台的固件。
- 使用 USB 连接您的开发板或设备,您可以通过串口 REPL 与设备交互。
- 通过编辑和上传 Python 脚本,您可以开始编写和运行 MicroPython 代码。
-
语法:
- MicroPython 支持 Python 3 的语法和一些标准库,但并非完全与 CPython 兼容。
- 基本的 Python 语法、函数和类在 MicroPython 中基本都能工作,但某些高级特性和标准库可能不被支持。
MicroPython 为嵌入式开发者提供了一个用 Python 进行编程的轻量级选项,让开发者能够更方便地利用 Python 的优势开发嵌入式系统和物联网设备。
安装Thonny
Thonny 是一款简单易用的 Python 集成开发环境 (IDE),旨在帮助初学者和教育者快速入门 Python 编程。它提供了许多功能和工具,使得编写、运行和调试 Python 代码变得更加简单和直观。
下面是 Thonny 的一些特点和常用功能:
-
简单易用:Thonny 的用户界面设计简洁直观,适合初学者快速上手并开始编写 Python 代码。
-
集成调试器:Thonny 集成了 Python 的调试器,可以帮助您在代码中查找和修复错误,便于编写稳健的程序。
-
代码补全:Thonny 提供代码补全功能,可以快速补全代码,减少编写代码时的错误。
-
可视化变量监视:在调试过程中,Thonny 提供了可视化的变量监视器,方便您跟踪程序中的变量值。
-
文件管理器:Thonny 提供了文件管理器,可以轻松管理项目文件和目录。
-
集成 Package Manager:可以通过 Thonny 的集成 Package Manager 安装、升级和管理 Python 的第三方库。
-
支持不同的 Python 解释器:允许您在 Thonny 中配置和切换不同版本的 Python 解释器,以便于开发不同环境下的项目。
-
支持多平台:Thonny 可在 Windows、macOS 和 Linux 等操作系统上运行,提供跨平台的开发体验。
ESP8266系列环境搭建
基于Windows系统下,烧录固件到 ESP8266 模块,请按照以下步骤进行操作:
准备工作
- 下载固件:首先,确保您已经从(官方网站)[https://micropython.org/download/ESP8266_GENERIC/]或适当的资源处下载了要烧录到 ESP8266 上的固件。通常,固件文件是一个二进制文件(.bin 格式)。
![img](https://img2023.cnblogs.com/blog/2432811/202406/2432811-20240622141547983-418034145.png)
- 安装烧录工具:您需要安装适用于 ESP8266 的烧录工具。一个常用的工具是 esptool。
pip install esptool
烧录固件步骤
- 连接 ESP8266
使用 USB 转 TTL 串口连接器将 ESP8266 模块连接到计算机。确保选择正确的串口和波特率。
电脑通过usb连接esp32,查看esp32的端口号,我这里是COM4口。
![img](https://img2023.cnblogs.com/blog/2432811/202406/2432811-20240622142900643-619811353.png)
- 擦除 Flash
在烧录新固件之前,您可能需要擦除 ESP8266 上的 Flash 存储器。这可以通过下列 esptool命令完成:
esptool --port COM4 erase_flash
确保将 COM4
替换为您的 ESP8266 的实际端口号。
![img](https://img2023.cnblogs.com/blog/2432811/202406/2432811-20240622143152698-1547351879.png)
擦除完成。
- 烧录固件
使用 esptool来烧录固件到 ESP8266。示例命令如下:
esptool --port COM4 --baud 460800 write_flash --flash_size=detect 0 ESP8266_GENERIC-20240602-v1.23.0.bin
--port COM4
: 指定 ESP8266 的串口号为COM4
。--baud 460800
: 指定波特率。write_flash
: 烧录固件到 Flash 存储器。ESP8266_GENERIC-20240602-v1.23.0.bin
: 替换为您要烧录的固件文件名。
- 等待烧录完成
烧录过程需要一些时间。请耐心等待,直到烧录进度完成。
- 重启设备
一旦固件烧录完成,断开并重新连接 ESP8266 模块,或者通过复位将其重启。
获取 MicroPython REPL 提示
REPL 代表 Read Evaluate Print Loop,是您可以在 ESP8266 上访问的交互式 MicroPython 提示的名称。到目前为止,使用 REPL 是测试代码和运行命令的最简单方法。
有两种访问 REPL 的方法:通过 UART 串行端口的有线连接,或通过 WiFi。
通过串口进行REPL
REPL 在 UART0 串行外围设备上始终可用,该外围设备连接到用于 TX 的引脚 GPIO1 和用于 RX 的 GPIO3。REPL 的波特率为 115200。如果您的板上有 USB 串行转换器,那么您应该能够直接从您的 PC 访问 REPL。否则,您将需要一种与 UART 通信的方法。
使用Putty、MobaXterm等终端工具连接ESP8266开发版。
连续按多次Enter
键,出现>>>
提示符说明连接成功。
尝试在提示符下键入以下内容:
>>> print('hello esp8266!')
hello esp8266!
如果你使用thonny开发编辑器,右下角可以直接选择连接到的设备。
![img](https://img2023.cnblogs.com/blog/2432811/202406/2432811-20240622153459617-335423902.png)
配置WebREPL(网络浏览器交互提示)
连接WIFI
使用thonny编辑器,选择设备中连接。
可以看到默认有boot.py文件存在,右键点击选择新建文件命名为main.py。
修改main.py添加连接wifi代码:
def do_connect():
import json
import network
# 尝试读取配置文件wifi_confi.json,这里我们以json的方式来存储WIFI配置
# wifi_config.json在根目录下
# 若不是初次运行,则将文件中的内容读取并加载到字典变量 config
try:
with open('wifi_config.json','r') as f:
config = json.loads(f.read())
# 若初次运行,则将进入excpet,执行配置文件的创建
except:
essid = input('wifi name:') # 输入essid
password = input('wifi passwrod:') # 输入password
config = dict(essid=essid, password=password) # 创建字典
with open('wifi_config.json','w') as f:
f.write(json.dumps(config)) # 将字典序列化为json字符串,存入wifi_config.json
#以下为正常的WIFI连接流程
wifi = network.WLAN(network.STA_IF)
if not wifi.isconnected():
print('connecting to network...')
wifi.active(True)
wifi.connect(config['essid'], config['password'])
import time
time.sleep(5) #一般睡个5-10秒,应该绰绰有余
if not wifi.isconnected():
wifi.active(False) #关掉连接,免得repl死循环输出
print('wifi connection error, please reconnect')
import os
# 连续输错essid和password会导致wifi_config.json不存在
try:
os.remove('wifi_config.json') # 删除配置文件
except:
pass
do_connect() # 重新连接
else:
print('network config:', wifi.ifconfig())
if __name__ == '__main__':
do_connect()
点击运行,输入账号密码连接WIFI.
![img](https://img2023.cnblogs.com/blog/2432811/202406/2432811-20240622155102962-1351076045.png)
配置WebREPL服务
要配置 ESP8266 上的 WebREPL(Web-based REPL),您可以按照以下步骤进行操作:
- 启用 WebREPL:
- 在 REPL 中输入以下命令以启用 WebREPL:
>>>import webrepl_setup
WebREPL daemon auto-start status: disabled #当前状态:disabled
Would you like to (E)nable or (D)isable it running on boot?
(Empty line to quit)
> e #输入e,开启
Would you like to change WebREPL password? (y/n) y #输入y,修改与设置密码
New password (4-9 chars): 123456 #输入密码
Confirm password: 123456 #再次输入密码
Changes will be activated after reboot
Would you like to reboot now? (y/n) y #重启生效
- 按照提示设置 WebREPL 密码。这将设置一个用于连接 WebREPL 的密码。
- 连接到 WebREPL:
使用串口连接ESP8266,在REPL输入:
>>> import webrepl
>>> webrepl.start()
WebREPL server started on http://192.168.31.241:8266/
Started webrepl in normal mode
- 现在,您可以连接到 ESP8266 的 WebREPL。您可以使用代表ESP8266_IP的IP地址。
- 打开浏览器并导航到
http://192.168.4.1:8266
。 - 输入先前设置的 WebREPL 密码以连接到 ESP8266。
![img](https://img2023.cnblogs.com/blog/2432811/202406/2432811-20240622155559651-1942156201.png)
-
使用 WebREPL:
- 一旦连接成功,您将可以通过 WebREPL 控制台与 ESP8266 进行交互。
- 您可以在 WebREPL 控制台中执行 Python 命令和脚本,实时查看输出。
-
远程控制 ESP8266:
- 通过 WebREPL,您可以远程控制 ESP8266,执行代码并监视输出。
- 还可以上传和下载文件,访问设备上的文件系统等操作。
通过上述步骤,您可以配置并使用 ESP8266 上的 WebREPL,这使得与设备互动更为方便。
ESP32系列环境搭建
ESP32和ESP8266的不同在于,下载对应的固件后,烧录固件的命令不一样。
如果是ESP32:
1.擦除flash。
esptool --chip esp32 --port COM6 erase_flash
确保将 COM6 替换为您的 ESP32 的实际端口号。
- 烧录固件
使用 esptool来烧录固件到 ESP32。示例命令如下:
esptool --chip esp32 --port COM6 --baud 460800 write_flash -z 0x1000 ESP32_GENERIC-20240602-v1.23.0.bin
其他步骤无变化。
ESP32-Camera固件烧录
进入 micropython-camera-driver 单击 firmware 目录。
下载 micropython_camera_feeeb5ea3_esp32_idf4_4.bin
1.擦除flash。
esptool --chip esp32 --port COM4 erase_flash
确保将 COM4 替换为您的 ESP32 的实际端口号。擦除后按下ESP32-Camera板上的Reset键。
- 烧录固件
使用 esptool来烧录固件到 ESP32。示例命令如下:
esptool --chip esp32 --port COM4 --baud 460800 write_flash -z 0x1000 micropython_camera_feeeb5ea3_esp32_idf4_4.bin
其他步骤无变化。
ESP8266使用教程
修改main.py文件
创建函数do_thing实现自定义功能块的调用。
def do_connect():
import json
import network
# 尝试读取配置文件wifi_confi.json,这里我们以json的方式来存储WIFI配置
# wifi_config.json在根目录下
# 若不是初次运行,则将文件中的内容读取并加载到字典变量 config
try:
with open("wifi_config.json", "r") as f:
config = json.loads(f.read())
# 若初次运行,则将进入excpet,执行配置文件的创建
except:
essid = input("wifi name:") # 输入essid
password = input("wifi passwrod:") # 输入password
config = dict(essid=essid, password=password) # 创建字典
with open("wifi_config.json", "w") as f:
f.write(json.dumps(config)) # 将字典序列化为json字符串,存入wifi_config.json
# 以下为正常的WIFI连接流程
wifi = network.WLAN(network.STA_IF)
if not wifi.isconnected():
print("connecting to network...")
wifi.active(True)
wifi.connect(config["essid"], config["password"])
import time
time.sleep(5) # 一般睡个5-10秒,应该绰绰有余
if not wifi.isconnected():
wifi.active(False) # 关掉连接,免得repl死循环输出
print("wifi connection error, please reconnect")
import os
# 连续输错essid和password会导致wifi_config.json不存在
try:
os.remove("wifi_config.json") # 删除配置文件
except:
pass
do_connect() # 重新连接
else:
print("network config:", wifi.ifconfig())
def do_thing():
try:
# 在这里实现自定义的功能
finally:
pass
if __name__ == "__main__":
do_connect()
do_thing()
点亮LED灯
要实现简单的 LED 点灯功能,您可以使用以下代码示例通过 ESP8266 控制 LED 灯点亮和熄灭:
MicroPython 代码示例
定义led.py
文件。
import time
from machine import Pin
def led_light():
# 实现灯闪烁
led = Pin(2, Pin.OUT)
while 1:
led.value(1)
time.sleep(1)
led.value(0)
time.sleep(1)
修改main.py
文件,调用led_light
函数。
#...
from led import *
def do_thing():
try:
led_light()
finally:
pass
#...
实现步骤
-
连接 LED:将 LED 的正极连接到 ESP8266 的 GPIO 引脚(例如 GPIO 2),将负极连接到 GND 引脚。
-
通过 Thonny IDE 或 WebREPL 连接到 ESP8266,并将上面的代码上传并运行。
-
LED 控制:
- 代码中的循环会使 LED 灯每隔一秒交替点亮和熄灭。
- 您可以根据需要调整延时时间来控制 LED 点亮和熄灭的频率。
PWM 呼吸灯
def pwm_led():
# 实现呼吸灯
led2 = PWM(Pin(2))
led2.freq(1000)
while True: # 控制整体的重复 (1.从不亮到亮 2.从亮到不亮)
# 1.实现从不亮到亮
for i in range(0, 1024):
led2.duty(i)
time.sleep_ms(1) # 每隔1ms变化一次亮度
# 2.实现从亮到不亮
for i in range(1023, -1, -1):
led2.duty(i)
time.sleep_ms(1)
PC控制LED灯
准备工具
整体思路
![img](https://img2023.cnblogs.com/blog/2432811/202406/2432811-20240623002105635-1160182365.png)
-
esp8266连接wifi,让其拥有IP地址
-
创建UDP Socket
-
接收UDP数据
-
根据接收到的UDP数据控制LED灯的亮灭
实现代码
修改main.py。
from net import *
from machine import Pin
def creat_udp_socket():
import socket
# 1.创建SOCKET
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 2.绑定一个固定的端口
udp_socket.bind(("0.0.0.0",8000))
print("绑定端口成功")
return udp_socket
def do_connect():
import json
import network
# 尝试读取配置文件wifi_confi.json,这里我们以json的方式来存储WIFI配置
# wifi_config.json在根目录下
# 若不是初次运行,则将文件中的内容读取并加载到字典变量 config
try:
with open("wifi_config.json", "r") as f:
config = json.loads(f.read())
# 若初次运行,则将进入excpet,执行配置文件的创建
except:
essid = input("wifi name:") # 输入essid
password = input("wifi passwrod:") # 输入password
config = dict(essid=essid, password=password) # 创建字典
with open("wifi_config.json", "w") as f:
f.write(json.dumps(config)) # 将字典序列化为json字符串,存入wifi_config.json
# 以下为正常的WIFI连接流程
wifi = network.WLAN(network.STA_IF)
if not wifi.isconnected():
print("connecting to network...")
wifi.active(True)
wifi.connect(config["essid"], config["password"])
import time
time.sleep(5) # 一般睡个5-10秒,应该绰绰有余
if not wifi.isconnected():
wifi.active(False) # 关掉连接,免得repl死循环输出
print("wifi connection error, please reconnect")
import os
# 连续输错essid和password会导致wifi_config.json不存在
try:
os.remove("wifi_config.json") # 删除配置文件
except:
pass
do_connect() # 重新连接
else:
print("network config:", wifi.ifconfig())
def main():
# 创建UDP套接字
udp_socket = creat_udp_socket()
# 创建GPIO引脚对象
LED2 = Pin(2, Pin.OUT)
# 接收UDP数据
while True:
recv_data, sender_info = udp_socket.recvfrom(1024)
print("{}发送的数据,{}".format(sender_info, recv_data))
recv_data_str = recv_data.decode("utf-8")
print("解码后的数据:{}".format(recv_data_str))
# 根据收到的UDP数据控制LED灯的亮灭
if recv_data_str == "on":
LED2.value(0)
print("开灯")
elif recv_data_str == "off":
LED2.value(1)
print("关灯")
if __name__ == "__main__":
do_connect()
main()
打开NetAssist配置本地UDP服务。给对应端口发送on
字符,灯会点亮,发送off
字符,灯会熄灭。
ESP32使用教程
实现异步操作
uasyncio
模块
uasyncio
是 MicroPython 中的一个用于异步编程的库,它提供了协程(coroutine)和事件循环(event loop)的支持,可以实现类似多线程的并发任务处理。以下是关于 uasyncio
模块的详细介绍:
uasyncio 主要组件
-
协程 (Coroutines):
- 在
uasyncio
中,任务通常以协程的形式表示,通过async def
定义的异步函数就是一个协程。 - 协程使用
await
关键字来挂起自己的执行,让事件循环切换到其他任务执行。
- 在
-
事件循环 (Event Loop):
- 事件循环负责调度和执行多个协程任务,它负责管理任务的运行顺序和事件的调度。
- 通过事件循环,可以让不同的协程交替执行,实现异步执行的效果。
-
异步 I/O:
uasyncio
支持异步 I/O 操作,可以在等待 I/O 操作完成时挂起当前任务,避免阻塞整个程序。- 通过
await
关键字,可以等待异步操作的结果,如网络请求、定时器等。
-
定时器:
uasyncio
提供了定时器功能,可以用来实现定时任务、延时任务等。- 通过
await asyncio.sleep(delay)
可以让当前任务挂起一段时间。
uasyncio 使用流程:
-
导入模块:
import uasyncio as asyncio
-
定义协程任务:
- 使用
async def
定义协程函数,函数内部可以使用await
进行挂起操作。
- 使用
-
创建事件循环:
loop = asyncio.get_event_loop()
-
将任务添加到事件循环:
loop.create_task(coroutine_function())
-
执行事件循环:
loop.run_forever()
示例应用场景
- 异步处理网络请求与响应
- 定时任务的调度
- 非阻塞的 I/O 操作,如串口通信、传感器数据处理
- 实现简单的状态机等
uasyncio
提供了一种简单且高效的异步编程方式,尤其适用于资源有限的嵌入式系统中。通过合理利用协程和事件循环,可以实现任务间的并发执行,提高系统的效率。
案例
使用 uasyncio
模块在 MicroPython 中实现并发任务处理。在这个例子中,协程 task1
每秒打印一次信息,而协程 task2
每隔 0.5
秒控制 LED 灯的状态切换。这两个任务会交替执行,通过事件循环实现并发的效果。
import uasyncio as asyncio
# 定义两个协程任务
async def task1():
while True:
print("Task 1 is running...")
await asyncio.sleep(1)
async def task2():
led_state = False
while True:
led_state = not led_state
print("LED is on" if led_state else "LED is off")
await asyncio.sleep(0.5)
# 创建事件循环
loop = asyncio.get_event_loop()
# 将任务添加到事件循环
loop.create_task(task1())
loop.create_task(task2())
# 执行事件循环
loop.run_forever()
将这段代码上传到 MicroPython 设备上,例如 ESP8266 或 ESP32,然后运行它,观察任务的交替执行。这个例子展示了 uasyncio 如何帮助实现简单的并发任务处理,适用于需要异步执行的场景。
修改main.py实现异步操作
修改main.py,实现多个任务异步操作。
import uasyncio as asyncio
async def do_connect():
import json
import network
# 尝试读取配置文件wifi_confi.json,这里我们以json的方式来存储WIFI配置
# wifi_config.json在根目录下
# 若不是初次运行,则将文件中的内容读取并加载到字典变量 config
try:
with open('wifi_config.json','r') as f:
config = json.loads(f.read())
# 若初次运行,则将进入excpet,执行配置文件的创建
except:
essid = input('wifi name:') # 输入essid
password = input('wifi passwrod:') # 输入password
config = dict(essid=essid, password=password) # 创建字典
with open('wifi_config.json','w') as f:
f.write(json.dumps(config)) # 将字典序列化为json字符串,存入wifi_config.json
#以下为正常的WIFI连接流程
wifi = network.WLAN(network.STA_IF)
if not wifi.isconnected():
print('connecting to network...')
wifi.active(True)
wifi.connect(config['essid'], config['password'])
await asyncio.sleep(5) #一般睡个5-10秒,应该绰绰有余
if not wifi.isconnected():
wifi.active(False) #关掉连接,免得repl死循环输出
print('wifi connection error, please reconnect')
import os
# 连续输错essid和password会导致wifi_config.json不存在
try:
os.remove('wifi_config.json') # 删除配置文件
except:
pass
await do_connect() # 重新连接
else:
print('network config:', wifi.ifconfig())
async def do_thing():
print("do_thing")
if __name__ == '__main__':
# 创建事件循环
loop = asyncio.get_event_loop()
# 将任务添加到事件循环
loop.create_task(do_connect())
loop.create_task(do_thing())
# 执行事件循环
loop.run_forever()
这样启动wifi和执行其他任务就能异步进行。
单个数码管显示
数码管,也称作辉光管,是一种可以显示数字和其他信息的电子设备。玻璃管中包括一个金属丝网制成的阳极的多个阴极。
数码管(LED Segment Displays)是由多个发光二极管封装在一起组成“8”字型的器件,引线已在内部连接完成,只需引出它们和各个笔划,公共电极。共阳极就是把所有LED的阳极连接到共同接点com,每个LED的阴极分别为a、b、c、d、e、f、g及dp(小数点);共阴极
则是把所有LED的阴极连接到共同接点com,而每个LED的阳极分别为a、b、c、d、e、f、g及dp(小数点),如下图所示:
![img](https://img2023.cnblogs.com/blog/2432811/202406/2432811-20240624225432294-1363079871.png)
从0到9显示数字,实现代码:
from machine import Pin
import time
# 定义引脚对应数码管的位置
a = Pin(13, Pin.OUT)
b = Pin(12, Pin.OUT)
c = Pin(14, Pin.OUT)
d = Pin(27, Pin.OUT)
e = Pin(26, Pin.OUT)
f = Pin(25, Pin.OUT)
g = Pin(33, Pin.OUT)
h = Pin(32, Pin.OUT) # 点号
# 将对应的引脚对象存储到列表
led_list = [a, b, c, d, e, f, g]
number_dict = {
0: "11111100", # 顺序依次是abcdefg
1: "01100000",
2: "11011010",
3: "11110010",
4: "01100110",
5: "10110110",
6: "10111110",
7: "11100000",
8: "11111110",
9: "11110110",
}
def show_number(number):
if number_dict.get(number):
i = 0
for num in number_dict.get(number):
if num == "1":
led_list[i].value(1)
else:
led_list[i].value(0)
i += 1
for i in range(10):
show_number(i)
time.sleep(1)
四位数码管显示
![img](https://img2023.cnblogs.com/blog/2432811/202406/2432811-20240624225915383-1368427350.png)
连接方式
![img](https://img2023.cnblogs.com/blog/2432811/202406/2432811-20240624232701086-922096025.png)
实现数字显示代码
from machine import Pin
import time
# 定义引脚对应数码管的位置
a = Pin(13, Pin.OUT)
b = Pin(12, Pin.OUT)
c = Pin(14, Pin.OUT)
d = Pin(27, Pin.OUT)
e = Pin(26, Pin.OUT)
f = Pin(25, Pin.OUT)
g = Pin(33, Pin.OUT)
h = Pin(32, Pin.OUT)
led1 = Pin(5, Pin.OUT) # LED灯1
led2 = Pin(18, Pin.OUT) # LED灯2
led3 = Pin(19, Pin.OUT) # LED灯3
led4 = Pin(21, Pin.OUT) # LED灯4
# 将对应的引脚对象存储到列表
led_list = [a, b, c, d, e, f, g, h]
led_light_list = [led1, led2, led3, led4]
number_dict = {
0: "11111100", # 顺序依次是abcdefgh
1: "01100000",
2: "11011010",
3: "11110010",
4: "01100110",
5: "10110110",
6: "10111110",
7: "11100000",
8: "11111110",
9: "11110110",
}
def show_number(number):
if number_dict.get(number):
i = 0
for num in number_dict.get(number):
if num == "1":
led_list[i].value(1)
else:
led_list[i].value(0)
i += 1
# for i in range(10):
# show_number(i)
# time.sleep(1)
def light(i):
# 清除所有灯
for led in led_light_list:
led.value(1)
led_light_list[i].value(0)
def show_4_number(number):
if 0 <= number <= 9999:
i = 0
for num in "%04d" % number: # 1234-->"1234"
# print(num, i)
show_number(int(num)) # 控制显示什么数
light(i)
i += 1
time.sleep_ms(5)
# 显示固定数字
# while True:
# show_4_number(1234)
#
for i in range(100, 10000):
for j in range(10):
show_4_number(i)