Python+ESP嵌入式开发快速上手

环境搭建

MicroPython介绍

MicroPython 是一种精简版的 Python 编程语言,专门设计用于嵌入式系统和物联网(IoT)开发。它提供了一个 Python 3 的子集,适用于资源受限的微控制器和单片机环境。MicroPython 允许开发人员使用 Python 的简洁语法和强大功能来编写嵌入式系统的代码。

以下是一些关于 MicroPython 的要点:

  1. 特点

    • 小巧灵活:MicroPython 是一种轻量级的实现,适用于资源有限的设备。
    • 易于学习:对于已经熟悉 Python 的开发者来说,学习 MicroPython 是相对简单的。
    • 交互式 REPL:MicroPython 提供了交互式的 REPL(Read-Eval-Print Loop),方便调试和快速原型开发。
    • 面向对象编程:支持面向对象编程范例,包括类、对象和继承等概念。
  2. 用途

    • MicroPython 适用于许多嵌入式系统和物联网应用的开发,如传感器网络、自动化控制、物联网设备等。
    • 它可以在各种微控制器和单片机平台上运行,例如 ESP8266、ESP32、STM32 等。
  3. 如何开始

  4. 语法

    • MicroPython 支持 Python 3 的语法和一些标准库,但并非完全与 CPython 兼容。
    • 基本的 Python 语法、函数和类在 MicroPython 中基本都能工作,但某些高级特性和标准库可能不被支持。

MicroPython 为嵌入式开发者提供了一个用 Python 进行编程的轻量级选项,让开发者能够更方便地利用 Python 的优势开发嵌入式系统和物联网设备。

安装Thonny

Thonny 是一款简单易用的 Python 集成开发环境 (IDE),旨在帮助初学者和教育者快速入门 Python 编程。它提供了许多功能和工具,使得编写、运行和调试 Python 代码变得更加简单和直观。

下面是 Thonny 的一些特点和常用功能:

  1. 简单易用:Thonny 的用户界面设计简洁直观,适合初学者快速上手并开始编写 Python 代码。

  2. 集成调试器:Thonny 集成了 Python 的调试器,可以帮助您在代码中查找和修复错误,便于编写稳健的程序。

  3. 代码补全:Thonny 提供代码补全功能,可以快速补全代码,减少编写代码时的错误。

  4. 可视化变量监视:在调试过程中,Thonny 提供了可视化的变量监视器,方便您跟踪程序中的变量值。

  5. 文件管理器:Thonny 提供了文件管理器,可以轻松管理项目文件和目录。

  6. 集成 Package Manager:可以通过 Thonny 的集成 Package Manager 安装、升级和管理 Python 的第三方库。

  7. 支持不同的 Python 解释器:允许您在 Thonny 中配置和切换不同版本的 Python 解释器,以便于开发不同环境下的项目。

  8. 支持多平台:Thonny 可在 Windows、macOS 和 Linux 等操作系统上运行,提供跨平台的开发体验。

Thonny官方网站

ESP8266系列环境搭建

基于Windows系统下,烧录固件到 ESP8266 模块,请按照以下步骤进行操作:

准备工作

  1. 下载固件:首先,确保您已经从(官方网站)[https://micropython.org/download/ESP8266_GENERIC/]或适当的资源处下载了要烧录到 ESP8266 上的固件。通常,固件文件是一个二进制文件(.bin 格式)。
img
  1. 安装烧录工具:您需要安装适用于 ESP8266 的烧录工具。一个常用的工具是 esptool。
pip install esptool

烧录固件步骤

  1. 连接 ESP8266

使用 USB 转 TTL 串口连接器将 ESP8266 模块连接到计算机。确保选择正确的串口和波特率。

电脑通过usb连接esp32,查看esp32的端口号,我这里是COM4口。

img
  1. 擦除 Flash

在烧录新固件之前,您可能需要擦除 ESP8266 上的 Flash 存储器。这可以通过下列 esptool命令完成:

esptool --port COM4 erase_flash

确保将 COM4 替换为您的 ESP8266 的实际端口号。

img

擦除完成。

  1. 烧录固件

使用 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: 替换为您要烧录的固件文件名。
  1. 等待烧录完成

烧录过程需要一些时间。请耐心等待,直到烧录进度完成。

  1. 重启设备

一旦固件烧录完成,断开并重新连接 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开发版。

img

连续按多次Enter键,出现>>>提示符说明连接成功。

尝试在提示符下键入以下内容:

>>> print('hello esp8266!')
hello esp8266!

如果你使用thonny开发编辑器,右下角可以直接选择连接到的设备。

img

配置WebREPL(网络浏览器交互提示)

连接WIFI

使用thonny编辑器,选择设备中连接。

img

可以看到默认有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
配置WebREPL服务

要配置 ESP8266 上的 WebREPL(Web-based REPL),您可以按照以下步骤进行操作:

  1. 启用 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 的密码。
  1. 连接到 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
  1. 使用 WebREPL

    • 一旦连接成功,您将可以通过 WebREPL 控制台与 ESP8266 进行交互。
    • 您可以在 WebREPL 控制台中执行 Python 命令和脚本,实时查看输出。
  2. 远程控制 ESP8266

    • 通过 WebREPL,您可以远程控制 ESP8266,执行代码并监视输出。
    • 还可以上传和下载文件,访问设备上的文件系统等操作。

通过上述步骤,您可以配置并使用 ESP8266 上的 WebREPL,这使得与设备互动更为方便。

ESP32系列环境搭建

ESP32和ESP8266的不同在于,下载对应的固件后,烧录固件的命令不一样。

如果是ESP32:

1.擦除flash。

esptool --chip esp32 --port COM6 erase_flash

确保将 COM6 替换为您的 ESP32 的实际端口号。

  1. 烧录固件

使用 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

img

1.擦除flash。

esptool --chip esp32 --port COM4 erase_flash

确保将 COM4 替换为您的 ESP32 的实际端口号。擦除后按下ESP32-Camera板上的Reset键。

  1. 烧录固件

使用 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

#...

实现步骤

  1. 连接 LED:将 LED 的正极连接到 ESP8266 的 GPIO 引脚(例如 GPIO 2),将负极连接到 GND 引脚。

  2. 通过 Thonny IDE 或 WebREPL 连接到 ESP8266,并将上面的代码上传并运行。

  3. 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灯

准备工具

下载NetAssist网络调试工具

整体思路

img
  1. esp8266连接wifi,让其拥有IP地址

  2. 创建UDP Socket

  3. 接收UDP数据

  4. 根据接收到的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 主要组件

  1. 协程 (Coroutines)

    • uasyncio 中,任务通常以协程的形式表示,通过 async def 定义的异步函数就是一个协程。
    • 协程使用 await 关键字来挂起自己的执行,让事件循环切换到其他任务执行。
  2. 事件循环 (Event Loop)

    • 事件循环负责调度和执行多个协程任务,它负责管理任务的运行顺序和事件的调度。
    • 通过事件循环,可以让不同的协程交替执行,实现异步执行的效果。
  3. 异步 I/O

    • uasyncio 支持异步 I/O 操作,可以在等待 I/O 操作完成时挂起当前任务,避免阻塞整个程序。
    • 通过 await 关键字,可以等待异步操作的结果,如网络请求、定时器等。
  4. 定时器

    • uasyncio 提供了定时器功能,可以用来实现定时任务、延时任务等。
    • 通过 await asyncio.sleep(delay) 可以让当前任务挂起一段时间。

uasyncio 使用流程:

  1. 导入模块

    import uasyncio as asyncio
    
  2. 定义协程任务

    • 使用 async def 定义协程函数,函数内部可以使用 await 进行挂起操作。
  3. 创建事件循环

    loop = asyncio.get_event_loop()
    
  4. 将任务添加到事件循环

    loop.create_task(coroutine_function())
    
  5. 执行事件循环

    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

从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

连接方式

img

实现数字显示代码

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)

posted @ 2024-06-23 18:17  Apostle浩  阅读(164)  评论(0编辑  收藏  举报