入门教程第二篇来了。第一篇咱们把开发环境、串口驱动、固件烧录这些杂事处理完了这一篇开始正式进入 MicroPython 的世界。我这篇会围绕“微雪 ESP32 MicroPython”这个组合把语言基础、REPL 交互、GPIO 外设控制、中断、PWM、ADC一直到断电自动运行这些核心知识点串起来讲一遍。不管你是刚摸芯片的小白还是从 Arduino 转过来的老手这篇都能让你少走不少弯路。我在微雪 ESP32 上用 MicroPython 折腾了挺长时间踩过的坑比写过的代码还多。既然叫“基础知识”我就尽量不讲虚的直接给你能上手的代码和操作逻辑顺便把我翻过的车也交代一下。1. 从点灯到外设MicroPython 在 ESP32 上到底怎么工作1.1 这一课适合谁解决什么问题先说清楚这篇到底讲了啥。MicroPython 是 Python 3 语言的一个精简实现专门跑在微控制器上。它保留了 Python 最核心的语法和标准库同时增加了machine这种直接操作硬件的外设库。你的电脑上跑的是完整 Python到了 ESP32 这种只有几百 KB 内存的单片机上Python 就得瘦身这就是 MicroPython 存在的意义。这一课是系列教程的第二篇默认你已经完成了固件烧录手里有一块能连上电脑的微雪 ESP32 开发板。我见过太多朋友卡在“板子插上没反应”“代码不知道存到哪”“上电之后程序不自动跑”这些问题上所以这一篇除了讲语法还会重点把“写好的代码怎么真正跑起来”这个流程讲透。如果你是第一次接触单片机跟着我的代码敲一遍至少能理解两个核心概念一是 MicroPython 是解释执行的你在交互界面输入一行代码它立刻就能给你反馈二是所有硬件操作本质上都是“往某个寄存器的地址写值”MicroPython 把这层封装成了Pin、PWM、ADC这些好用的对象。理解了这两点后面学什么都顺。1.2 为什么选 MicroPython 而不是 Arduino很多人在 ESP32 上用 Arduino 写代码也挺爽的为什么还要专门学 MicroPython我说几个实际感受开发效率差距明显。MicroPython 不需要编译改完代码直接保存到板子重启就生效迭代速度快得多。Arduino 每次改动都要编译上传少说十几秒多则一分钟。交互式调试太香了。你可以直接在 REPL 里敲pin.value(1)看灯亮不亮敲adc.read()看传感器返回的原始值不用每次都用串口打印 debug——调试效率完全是另一个维度的体验。内存和性能换来了开发便利。MicroPython 在 ESP32 上能用的 RAM 本身就有限解释器还要吃掉一部分所以跑复杂算法或者高频数据采集时会力不从心。但反过来做原型验证、IoT 设备、传感器采集这类应用MicroPython 的开发速度优势极其明显。实际项目里怎么选我的经验是如果要做量产固件、对实时性要求极高那就用 Arduino 或 ESP-IDF如果只是做原型、做课程实验、快速验证传感器逻辑直接用 MicroPython它能让你把注意力集中在业务逻辑上而不是跟编译器较劲。2. 先把手伸进 REPL人机交互式调试入门2.1 连接串口设备名、驱动和工具REPL 是 Read-Eval-Print Loop 的缩写说白了就是一个交互式命令行。你把 ESP32 通过 USB 线连上电脑它会虚拟出一个串口设备。Windows 上通常显示为COM3、COM4这种名字Linux 和 macOS 上则是/dev/ttyUSB0或/dev/cu.usbserial-xxx。微雪的 ESP32 开发板大多板载了 CP2102 或 CP2104 串口芯片正常情况下装好驱动就能识别。如果你插上板子后设备管理器里没有反应或者出现黄色感叹号优先去装对应芯片的官方驱动然后换一根“能传数据”的 USB 线——有些线只能充电这个坑我已经踩过很多次了一换线立刻就能识别。连 REPL 我推荐三个工具按使用频率排序Thonny自带 MicroPython 支持和文件管理对新手最友好。打开后右下角选“MicroPython (ESP32)”再选对应串口就能看到提示符。串口终端工具比如 Windows 的 PuTTY、MobaXterm或者 Linux 的 minicom、screen。这类工具只做串口交互操作简单直接。ampy 或 rshell这两个是命令行工具主要用来上传和下载文件也能附带执行一些 MicroPython 代码。连接时要注意波特率。MicroPython 的 REPL 默认波特率是 115200如果你在串口工具里选错波特率屏幕上会出现乱码。另外串口默认是 8N1即 8 个数据位、无校验、1 个停止位这个配置一般不需要改。2.2 REPL 的基本操作和救命技巧连上 REPL 之后你会看到提示符。先敲两个最基础的命令感受一下help() help(modules)help()会列出 MicroPython 的帮助信息help(modules)会列出当前固件里可用的模块列表。想看看机器相关的功能就输入from machine import Pin help(Pin)这样一来你就能看到Pin对象支持哪些方法不用上网查文档也能摸清功能。REPL 里有几个很有用的技巧我强烈建议你记住Tab 键自动补全。输入p再按 Tab它会自动补全Pin或提示可选项这在记不全方法名时特别管用。CtrlE进入粘贴模式。当你准备粘贴多行代码时按CtrlE会看到paste mode; Ctrl-C to cancel, Ctrl-D to finish的提示然后粘贴代码按CtrlD结束执行。这能避免逐行执行缩进报错的问题。CtrlC中断程序。如果代码进入了死循环比如while True里没有 sleep按CtrlC就能回到 Python 提示符这是最常用的救命操作。CtrlD软复位。相当于热重启会重新执行boot.py和main.py但不会断电。我给新手的建议是别急着写完整程序先在 REPL 里敲一些简单的表达式比如1 2、micro python甚至print(dir(machine))看看模块里都有什么。REPL 就是你的试验场任何不确定的语法都可以丢进去试。实测下来的体验就是你对语言和外设的理解会快很多。3. 语法速通看懂和写出能跑的 MicroPython3.1 与标准 Python 的差异和注意事项MicroPython 是 Python 3 的子集绝大多数基础语法跟标准 Python 一模一样。但既然跑在资源有限的单片机上它有几处必须知道的差异第一内存模型不同。标准 Python 里你可以随便创建大列表、大字典但 ESP32 的 RAM 是论 KB 算的MicroPython 默认的垃圾回收机制也没标准 Python 那么激进。所以写代码时要养成节约内存的习惯比如能用tuple就别用list能用整型别用浮点型能用局部变量别用全局变量。第二数值类型受限。ESP32 上的 MicroPython 整数是 32 位范围大概是 -2147483648 到 2147483647超出范围会报错或溢出。浮点数默认是单精度你别指望它能精确表示很多小数位涉及精确计算时要反过来设计算法。第三标准库裁剪严重。像numpy、requests完整版、threading这种标准 Python 常用的库在 MicroPython 里都没有。网络请求要用urequests多线程要用_thread注意下划线开头而且是弱化版。需要什么功能之前先想想这个库在 ESP32 上存不存在。我得提醒你MicroPython 在语法层面允许你写出跟桌面 Python 一样的代码但实际能不能跑起来取决于内存和固件。比如在 REPL 里定义一个包含十万个元素的列表大概率直接报MemoryError这是正常现象不是代码写错了。3.2 数据类型、函数和异常处理的实用写法MicroPython 的基础数据类型包括int整数、float浮点数、str字符串、bytes字节串、bool布尔值、list列表、tuple元组、dict字典。这些跟 Python 完全一致但实际使用中要特别注意bytes和bytearray因为在处理串口数据、传感器数据时会频繁用到。函数定义、循环、条件判断这类语法我就不展开了跟 Python 没区别。我只提几个跟硬件编程强相关的点try-except必须学会用。硬件设备不可能永远正常传感器偶尔没插好、通信偶尔超时这都是常态。用异常处理包住这些不稳定操作程序才不会动不动崩溃。比如读取温湿度传感器 DHT11 时我都是这样写的import dht from machine import Pin import time sensor dht.DHT11(Pin(4)) while True: try: sensor.measure() temp sensor.temperature() hum sensor.humidity() print(温度:, temp, 湿度:, hum) except OSError as e: print(传感器读取失败, 重试中:, e) time.sleep(2)全局变量和局部变量要留心。在函数内部修改全局变量必须先声明global这是 Python 的老规矩MicroPython 也一样但很多人刚开始用 REPL 写代码容易忽视。内存回收要主动一点。用完大型对象后可以调用gc.collect()手动回收内存特别是循环里反复创建新对象时这能避免程序跑着跑着突然MemoryError。以下是一段综合了变量、函数、异常处理和机器模块的小示例你可以复制到 REPL 或存成文件跑一下import machine import time count 0 def print_status(message): global count count 1 print([{}] {}.format(count, message)) try: print_status(设备启动) time.sleep(1) print_status(工作正常) except KeyboardInterrupt: print(程序被用户中断)这段代码虽小但把global、异常、格式化字符串这些要点全带出来。你能完全理解它MicroPython 的基本语法就算过关了。4. 外设控制实战GPIO、PWM、ADC 和外部中断4.1 GPIO 输出点亮微雪板的板载 LED外设控制从点亮 LED 开始。别看点灯简单它背后串起了“引脚编号”“输入输出模式”“高电平低电平”这些最基本的概念。先看一段点灯代码from machine import Pin import time led Pin(2, Pin.OUT) while True: led.value(1) # 拉高电平点亮LED time.sleep(0.5) led.value(0) # 拉低电平熄灭LED time.sleep(0.5)这里的关键点是Pin(2, Pin.OUT)中的“2”。这个数字是 ESP32 的 GPIO 编号不是板子上的物理引脚序号。微雪 ESP32 开发板的板载 LED 一般接在 GPIO 2 或 GPIO 16不同型号可能不一样。我第一次用某款板子时默认用了 GPIO 2结果灯不亮查了原理图才发现那款板子把 LED 接到了 GPIO 16。怎么确定 LED 接在哪个引脚最直接的办法是看板子的原理图或丝印说明。如果没有文档可以在 REPL 里逐个测试from machine import Pin import time for i in (2, 4, 5, 16, 17, 18, 19, 21): led Pin(i, Pin.OUT) led.value(1) time.sleep(0.2) led.value(0)哪个引脚在闪烁就是板载 LED 的位置。这个排查方法在拿到一块陌生开发板时特别有用。需要注意ESP32 不是所有 GPIO 都能当输出用。GPIO 34 到 GPIO 39 是输入专用引脚不能输出GPIO 6 到 GPIO 11 被 flash 占用不能碰GPIO 0 通常用作 BOOT 按键检测GPIO 1 和 GPIO 3 是串口 TX 和 RX默认被 REPL 占用。这些限制是硬件层面的不是你代码的问题。4.2 按键输入和外部中断别再傻等轮询GPIO 输入最常见的应用是按键检测。新手最容易犯的错误是“轮询”也就是在while True里不断读引脚电平。这种方式代码简单但 CPU 一直被占用没法同时处理其它任务。更好的方案是用外部中断。ESP32 的 MicroPython 用Pin.IRQ实现中断我在多个项目里实测下来非常稳定。下面是一个按键触发中断的完整示例from machine import Pin import time led Pin(2, Pin.OUT) button Pin(0, Pin.IN, Pin.PULL_UP) # 通常BOOT按键接GPIO0 def button_callback(pin): led.value(not led.value()) button.irq(triggerPin.IRQ_FALLING, handlerbutton_callback) while True: time.sleep(0.1)这段代码的含义是按键按下时引脚从高电平变成低电平触发下降沿中断调用button_callback翻转 LED 状态。用中断要注意三个问题回调函数里不能做耗时操作。打印字符串、申请内存、调用sleep这些都尽量别写在回调里。正确的做法是只设置一个标志位在主循环里处理标志位对应的操作。必须启用上拉或下拉电阻。按键外设不会自己决定高电平还是低电平你得用Pin.PULL_UP或Pin.PULL_DOWN确定默认状态否则电平会悬空漂移。按键抖动导致误触发。机械按键按下和松开的瞬间会产生几十毫秒的电平毛刺一次按下可能被识别成多次触发。简单的解决方法是加一个软件延时过滤见下面这段last_time 0 def button_callback(pin): global last_time now time.ticks_ms() if now - last_time 50: last_time now led.value(not led.value())time.ticks_ms()是 MicroPython 里常用的毫秒计时函数配合时间差判断就能实现简单的“消抖”。这个“外部中断实战”的思路你可以直接套用到按键、门磁、红外传感器等几乎所有输入类设备上。4.3 PWM呼吸灯、调光和舵机控制PWM 的全称是脉宽调制本质上是控制一个引脚高低电平的占空比从而模拟输出“可变的电压”。LED 调的亮度、舵机转的角度、蜂鸣器发的声音底层都是 PWM 在干活。ESP32 上的 MicroPython PWM 用法如下from machine import Pin, PWM import time pwm_led PWM(Pin(2), freq1000, duty512) # 渐变效果: 从暗到亮再变暗 while True: for d in range(0, 1024, 8): pwm_led.duty(d) time.sleep_ms(10) for d in range(1023, -1, -8): pwm_led.duty(d) time.sleep_ms(10)这里有几个参数需要解释freq是 PWM 频率单位 Hz。调 LED 时用 1000Hz 就够了频率太低会出现肉眼可见的闪烁但控制舵机时需要 50Hz 左右的频率对应标准舵机的周期 20ms。duty是占空比取值范围 0 到 1023。0 代表完全关闭1023 代表全开512 大概就是一半亮度。这个范围和 Arduino 的 0 到 255 不一样别搞混。不是所有引脚都支持 PWM。ESP32 的 PWM 由 LEDC 外设提供总共 16 个通道理论上很多引脚能用但要避开输入专用引脚和 flash 相关引脚。如果不确定就在 REPL 里PWM(Pin(2))试一下报错就换个引脚。控制舵机是一个很典型的 PWM 场景。标准舵机的原理是给一个周期 20ms、脉宽 0.5ms 到 2.5ms 的方波信号舵机会转动到对应角度。但 MicroPython 的duty是 0 到 1023没法直接写脉宽的毫秒数你需要换算一下。假设频率 50Hz占空比 2.5% 对应脉宽 0.5ms那么duty大概就是 1024 × 0.025 ≈ 26。具体数值根据舵机型号略有差异这个计算思路就是处理“频率与占空比”换算的标准方法。4.4 ADC读取电压和温湿度传感器ADC 是模数转换器用来把模拟电压值读成数字。ESP32 上有两个 ADC 模块ADC1 接 GPIO 32 到 GPIO 39ADC2 接 GPIO 0、2、4、12 到 15 等引脚。先看最简单的读电压示例from machine import ADC, Pin import time adc_pin ADC(Pin(34)) adc_pin.atten(ADC.ATTN_11DB) # 量程约0~3.3V while True: raw adc_pin.read() voltage raw / 4095 * 3.3 print(原始值:, raw, 电压:, voltage) time.sleep(1)read()返回 0 到 4095 的原始值对应 0V 到 3.3V。atten设置衰减档位ATTN_11DB表示可测量最大约 3.3V 的量程这是最常用的配置。关于 ESP32 的 ADC有几个实际心得你必须知道ADC2 和 WiFi 冲突。只要 WiFi 在运行ADC2 的读数就会变得不准或者干脆失败。所以涉及模拟量采集优先用 ADC1也就是 GPIO 32 到 GPIO 39 这几个引脚。ADC 的低端非线性严重。ESP32 的 ADC 在接近 0V 的那一段非常不准确电压低于 0.1V 时读数基本没有参考价值。想测小信号需要外接放大电路或选带增益的传感器。不同板子之间存在差异。同一电压读出 4095 不代表满量程校准得准不同开发板的参考电压会有细微差别。要求高精度时要用标准电压源做两点校准并在代码里做补偿。如果你外接的是 DHT11、DHT22 温湿度传感器走的是单总线协议不需要 ADC 直接读取。MicroPython 固件里内置了dht模块接好线后直接用我前面 3.2 节里给的代码就能读取温度和湿度。微雪 ESP32 开发板的排针布局一般把可以用的 GPIO 都引出来了DHT11 接任意普通 GPIO 就行我这里习惯用 GPIO 4。5. 断电也能运行把脚本固化到开发板5.1 boot.py 与 main.py 的分工到这一步你肯定不想每次调试都开着电脑插着线。MicroPython 支持把脚本固化到开发板上上电之后自动运行这就是热搜里“esp32 micropython 断电运行”的答案。MicroPython 的 Flash 文件系统里有两个特殊文件boot.py开机最先执行通常用来做初始化。比如配置网络、设置 LED 状态等一般会先执行完再进入主程序。main.py紧接着boot.py执行这是你的主程序入口。只要main.py存在板子上电或复位后就会自动运行它。所以如果你的目标是“断电重连自动工作”核心就是把自己写的脚本保存为main.py上传到开发板。在 Thonny 里操作特别简单写好代码后菜单栏选“文件 → 保存副本”在弹出的对话框里选择“MicroPython 设备”文件名写成main.py保存完成。注意保存时要关注当前目录MicroPython 默认存储位置是/保存到其它路径可能导致开机找不到文件。稍等片刻给板子重新上电代码就会自动跑起来了。5.2 上传和固化脚本的几种方式除了 Thonny 图形界面的方式还有几种常用方法第一种使用命令行工具 ampy。它很适合脚本化操作ampy --port COM3 put main.py ampy --port COM3 run main.pyput是本地上传到开发板run是执行板子上的脚本ls可以查看板子的当前文件列表。第二种使用 rshell。它的特点是连接后会挂载一个虚拟目录用cp命令就能拷贝文件rshell --port /dev/ttyUSB0 cp main.py /pyboard/main.py第三种WebREPL。ESP32 连上 WiFi 后可以通过浏览器远程操作 REPL 和上传文件。这个适合已经组网的项目远程维护的时候非常方便。不过首次使用需要先在板子端启用 WebREPL流程稍微复杂一点我建议新手先掌握前面两种。不管用哪种方式文件传完后都建议手动复位一次确认main.py确实被自动执行了。5.3 开机自动运行的常见坑我在“断电运行”这件事上踩过不少坑挑几个典型的说。第一个坑是main.py里死循环导致无法进入 REPL。如果代码逻辑有问题比如while True内部没有sleep板子开机后就会疯狂执行虽然有 REPL 但很难抓住输入时机。解决办法是按CtrlC中断运行立刻回到 REPL然后修改代码重新保存。如果连CtrlC都来不及就只能按住 BOOT 键再按 EN 键复位可能还需要擦除 Flash。第二个坑是电平冲突导致无法启动。比如把某个 GPIO 配置成输出高电平但这个引脚连着一个按键或者传感器而且硬件上有下拉电阻。这样板子启动时MicroPython 的初始化流程可能因为这个电平状态异常卡住甚至进不了主程序。遇到这种情况检查启动阶段引脚初始化是否正确。第三个坑是boot.py出问题连累main.py。boot.py执行失败时main.py通常也不会执行。排查时先把boot.py改名或清空同时确认板子能不能进入 REPL然后再逐步排除。我的建议是前期调试阶段尽量不要把程序设为开机自启确认稳定后再固化成main.py。否则每次上电都跑起来发现问题又得反复复位效率很低。6. 实战中踩过的坑ESP32 MicroPython 问题排查6.1 串口、烧录和固件故障这个表格是我实际使用中整理出来的高频问题速查每一条都是我或者身边朋友切身踩过的现象可能原因解决思路电脑识别不到串口驱动未装、USB线只能充电装芯片驱动CP210x等换数据线重新插拔串口打开失败串口被其它软件占用关掉 Thonny、串口工具只保留一个软件占用端口REPL 输出乱码波特率不对把串口工具波特率改成 115200烧录时could not open port串口被占用或没选对端口确认端口号烧录前断开 REPL 连接烧录成功但上电没反应固件损坏或 boot 配置问题按住 BOOT 键重新烧录必要时先擦除 Flash 再烧代码里 print 没输出main.py 在自动执行死循环立刻按CtrlC中断找到卡死位置板子上电后指示灯不亮供电不足或硬件故障换 USB 口或换供电线检查板子背面是否有虚焊烧录过程里擦除 Flash 这个操作很关键。如果板子上刷过一些乱七八糟的固件或者出现了奇怪的启动异常先擦除 Flash 再烧录esptool.py --port COM3 erase_flash擦除完成后再烧录 MicroPython 固件。这个操作会清掉板子上所有数据包括之前上传的main.py属于终极修复手段但确实有效。如果连esptool.py都连接不上按住 BOOT 键不放再点烧录让芯片进入下载模式这是 ESP32 烧录时最经典的一招。6.2 引脚、内存和运行期问题除了环境问题运行期的坑更加隐蔽。我整理了几条一定要记住的硬件限制GPIO 6 到 GPIO 11 不能使用。这些引脚连接着外部 SPI Flash你用它们做任何操作都有可能导致系统崩溃或无法启动。GPIO 34 到 GPIO 39 只能作为输入。它们是输入专用引脚不能配成Pin.OUT一配就报错。GPIO 0 通常被板载按键占用使用时要小心。如果你想用外部按键改到其它空闲引脚更省心。ADC2 在 WiFi 开启时发疯。只需要记住一条经验所有模拟量采集尽量走 ADC1GPIO 32 到 GPIO 39把 ADC2 留给 WiFi 使用。PWM 通道不是无限多。ESP32 有 16 个 LEDC 通道但引脚复用关系在不同板子上有差异通道不够时先释放不用的 PWM调用pwm.deinit()或者重启。内存相关的问题也很常见我举一个最典型的例子data [] while True: data.append(1024 * [0]) # 反复创建大列表这个写法跑不了一会儿就会报MemoryError。ESP32 上可用 RAM 大约是 150KB 左右MicroPython 的堆存在其中如果代码无限积累数据或创建对象但从不释放内存必然耗尽。解决思路是用gc.collect()主动回收用固定大小的array模块代替list把不用的对象及时置为None传感器数据只保留最近 N 条用收集逻辑代替无限追加调试这类问题有一个实用方法在关键节点手动输出gc.mem_free()。如果可用内存在明显下降说明代码有内存泄漏找到泄漏源再优化。还有一个很容易忽略的点MicroPython 的浮点数是单精度1/3 的结果不是 0.3333333333...而是 0.33333334 这样有误差的值。如果你在做浮点比较就不要用是否相等而是判断差值绝对值是否小于一个容差。做传感器数据滤波时这个细节尤其重要。这篇文章从 REPL 交互讲到了外设控制最后落在断电自动运行和问题排查上基本覆盖了 MicroPython 入门阶段会碰到的所有核心知识点。我个人的习惯是拿到一块新板子先跑一遍 REPL 熟悉功能再点亮一个 LED 验证系统可用接着接传感器做数据采集最后固化成main.py做断电自动化。按这个顺序走下来能避开大多数坑。后面有空我再写一讲把 WiFi 联网、HTTP 请求和 MQTT 这整套物联网流程详细拆解。
阅读完成 · 觉得有帮助?