用 Python 写 Arduino?这个开源神器让你告别 C++ 的折磨!
用 Arduino 写过代码的同学,大概率都经历过那种感觉: 本来就想点个灯、读个传感器,结果被各种 setup() / loop()、指针、类型转换折腾得头皮发麻。尤其是平时主力语言是 Python 的同学,切到 C++ 那一下,真有点“灵魂出窍”。
其实,现在已经完全可以用 Python 来“写 Arduino”了,只是方式跟你想象的不太一样。今天就把这个事儿从头到尾捋一遍,顺带给一套可以直接抄走的代码。
什么叫“用 Python 写 Arduino”
这句话其实有两层意思:
逻辑写在电脑上的 Python 代码里,Arduino 当“高级外设”,所有 I/O 都通过串口让 Python 去操控; 逻辑直接跑在板子上,比如 ESP32、树莓派 Pico 这类板子刷 MicroPython / CircuitPython 固件。
传统 Arduino(UNO、Nano 这类 AVR 芯片)本身是跑不了 Python 的,所以更现实、也更通用的方案是第 1 种:在板子里刷一个通用固件(Firmata),逻辑全部写在你熟悉的 Python 里。
这篇主要讲这一套,因为它对“老 Arduino + 会点 Python”的组合最友好。
开源神器:Firmata + pyFirmata 这对组合
思路特别简单粗暴:
Arduino 上只写一段通用协议固件(Firmata),负责“听命令、做 I/O”; 电脑上跑 Python,用 pyFirmata这个库,通过串口发指令让 Arduino 干活。
可以把 Arduino 想象成一个“被远程控制的 GPIO 盒子”,而 Python 负责所有业务逻辑、算法、数据处理,甚至可以直接跟数据库、Web 接口打交道。
先准备几样东西:
一块 Arduino UNO / Nano / Mega 都行; 一台能装 Python 的电脑(Windows / macOS / Linux); 一根 USB 线。
1)给 Arduino 刷 Firmata 固件(只用刷一次)
打开 Arduino IDE,菜单里找这两个:
文件 → 示例 → Firmata → StandardFirmata
选中你的开发板和串口,直接上传。 成功以后,这块板子以后就不需要再写 C++ 了,它永远以“Firmata 模式”启动,随叫随到。
2)在电脑上装 Python 依赖
pip install pyfirmata pyserial
装好就可以写第一段“Python 版点灯”了。
第一个例子:用 Python 控制 13 号口 LED 闪烁
新建一个 blink.py,写这样一段:
from time import sleep
from pyfirmata import Arduino, util# 把这里的串口改成你自己的
# Windows 通常是 COM3 / COM4 这种
# Mac / Linux 通常是 /dev/ttyACM0 或 /dev/ttyUSB0
board = Arduino('COM3')
print("连接成功,开始闪灯...")
try:
whileTrue:
board.digital[13].write(1) # 开
sleep(0.5)
board.digital[13].write(0) # 关
sleep(0.5)
except KeyboardInterrupt:
print("收到中断信号,关闭板子")
finally:
board.exit()
运行:
python blink.py
如果板子连的是对的串口,很快你就能看见 13 号口小灯在闪。 这里有几个点值得说一嘴:
Arduino('COM3')这行就是通过串口打开板子;board.digital[13].write(1)等价于 C++ 里的digitalWrite(13, HIGH);board.exit()很重要,退出时把串口收拾干净,不容易出各种“占用中”的玄学问题。
这件事如果用 C++ 写,最少得写:
voidsetup(){
pinMode(13, OUTPUT);
}voidloop(){
digitalWrite(13, HIGH);
delay(500);
digitalWrite(13, LOW);
delay(500);
}
而且改业务逻辑还得重新编译、重新烧录。 现在你改 Python 代码、保存再跑一遍就能测试,体验完全不是一个量级。
玩一点真的:读电位器、控制 LED 亮度
只会 HIGH / LOW 有点浪费 Arduino 的模拟能力。搞个经典小例子:电位器控制 LED 亮度。
硬件接法(随便说一下):
电位器中间脚接 A0,两侧一个接 5V、一个接 GND; 一个 LED 串个限流电阻接在 9 号 PWM 口和 GND 之间。
然后看 Python 代码:
from time import sleep
from pyfirmata import Arduino, utilboard = Arduino('COM3')
# 启动一个后台线程,持续读取模拟输入的值
it = util.Iterator(board)
it.start()
# 告诉 pyFirmata:A0 这路要当输入来用
analog_0 = board.get_pin('a:0:i') # a: analog, 0: A0, i: input
led_9 = board.get_pin('d:9:p') # d: digital, 9: D9, p: pwm
print("开始根据电位器调节亮度(Ctrl+C 退出)")
try:
whileTrue:
value = analog_0.read() # 0.0 ~ 1.0 之间
if value isnotNone:
led_9.write(value) # PWM 亮度直接用这个值
print(f"当前电位器值:{value:.3f}", end='\r')
sleep(0.02)
except KeyboardInterrupt:
print("\n退出")
finally:
led_9.write(0)
board.exit()
这里几个细节挺“Python 味儿”的:
get_pin('a:0:i')这种字符串描述方式,很直观:a表示模拟口、0表 A0、i是输入(input);get_pin('d:9:p')里p是 PWM 输出(PWM);查询电位器值得到的是 0.0 ~ 1.0的浮点,直接喂给 PWM 就能实现线性亮度变化。
如果你希望再“稳一点”,可以在循环里加个简单的移动平均,把抖动滤掉,这种逻辑用 Python 写就特别自然。
再上一步:在 Python 里写“Arduino 业务逻辑”
有了这种“Python 控制 I/O”的能力,其实可以把 Arduino 当成一个纯硬件抽象层,剩下全是正常写 Python 应用的事。
比如做一个简单的“温度监控 + 日志记录”:
Arduino 负责采集温度传感器(比如模拟口的 NTC 热敏电阻、模拟温度模块等);
Python 负责:
定时读取数据; 写入本地 CSV / SQLite; 超过某个阈值时发一个钉钉 / 企业微信机器人消息。
伪代码长这样:
import csv
import time
from datetime import datetime
from pyfirmata import Arduino, utilboard = Arduino('COM3')
it = util.Iterator(board)
it.start()
temp_sensor = board.get_pin('a:1:i') # 假设温度模块接在 A1
defread_temperature():
value = temp_sensor.read()
if value isNone:
returnNone
# 假设传感器 0~1 对应 0~50 度,实际用时按模块文档换算
return value * 50.0
defnotify_high_temp(temp):
print(f"[告警] 温度过高:{temp:.2f} ℃")
# 这里可以加 HTTP 请求发到告警系统
with open('temp_log.csv', 'a', newline='', encoding='utf-8') as f:
writer = csv.writer(f)
writer.writerow(['timestamp', 'temperature'])
try:
whileTrue:
temp = read_temperature()
if temp isNone:
time.sleep(0.5)
continue
ts = datetime.now().isoformat(timespec='seconds')
writer.writerow([ts, f"{temp:.2f}"])
f.flush()
print(f"{ts} 当前温度:{temp:.2f} ℃", end='\r')
if temp > 40:
notify_high_temp(temp)
time.sleep(2)
except KeyboardInterrupt:
print("\n停止监控")
finally:
board.exit()
你会发现,从这里开始,它已经更像一个普通的 Python 后台小服务,而不是“嵌入式开发”。
你可以很顺畅地把各种库拼进来:pandas 做分析、matplotlib 画图、fastapi 做个小 Web 控制台等等。
Python 真的能完全替代 C++ 吗?
说句实在话:不能完全替代,但很多日常项目够用了。
大概可以这么划个边界:
更适合用 Python 的场景:
需要和“软件世界”打交道:数据库、接口、Web、定时任务; 逻辑复杂,但实时性要求一般,比如实验设备、教学 Demo、家里搞点自动化; 你就是懒得切 C++,只想快速试东西。 仍然建议用 C++ / 原生 Arduino 的场景:
要省电、省资源、长时间离线跑; 需要严格的实时性(比如高频 PWM、精确时序控制); 成本敏感,要把一块板做到极致。
不过很多小项目,完全可以这样折中:先用 Python + Firmata 把玩法验证通了,再把核心逻辑(如果有必要)慢慢迁回 C++。前期探索阶段就别急着跟寄存器死磕了,用最顺手的方式保证自己能动手、能试、能重构。
真正在板子上跑 Python 的 MicroPython
上面讲的是“Python 在电脑上,Arduino 当外设”。 还有一条路,是让板子自己跑 Python,比如这些:
ESP32 / ESP8266 + MicroPython; 树莓派 Pico(RP2040)+ MicroPython; 各种带 CircuitPythonLogo 的开发板。
它们的体验更像“在一个很小的 Linux 上用 Python 写脚本”:
板子插上电脑,会出现一个 U 盘; 把 main.py拖进去,重启就自动运行;通过串口 / WebREPL 还能进交互式 REPL,现场调试 import machine之类的东西。
简单感受一下 MicroPython 上的“点灯”:
from machine import Pin, Timerled = Pin(2, Pin.OUT) # 以 ESP32 板载 LED 为例
deftoggle_led(timer):
led.value(not led.value())
timer = Timer(0)
timer.init(period=500, mode=Timer.PERIODIC, callback=toggle_led)
这就已经跟正常写 Python 非常接近了。 唯一要注意的是:经典的 Arduino UNO 这类 AVR 板子,一般不支持 MicroPython,所以如果你想走这条路,建议一开始就选一块支持 MicroPython / CircuitPython 的板子。
-END-
我为大家打造了一份RPA教程,完全免费:songshuhezi.com/rpa.html
虎哥作为一名老码农,整理了全网最全《python高级架构师资料合集》,总量高达650GB