Python技术迷

用 Python 写 Arduino?这个开源神器让你告别 C++ 的折磨!

用 Arduino 写过代码的同学,大概率都经历过那种感觉: 本来就想点个灯、读个传感器,结果被各种 setup() / loop()、指针、类型转换折腾得头皮发麻。尤其是平时主力语言是 Python 的同学,切到 C++ 那一下,真有点“灵魂出窍”。

其实,现在已经完全可以用 Python 来“写 Arduino”了,只是方式跟你想象的不太一样。今天就把这个事儿从头到尾捋一遍,顺带给一套可以直接抄走的代码。

什么叫“用 Python 写 Arduino”

这句话其实有两层意思:

  1. 逻辑写在电脑上的 Python 代码里,Arduino 当“高级外设”,所有 I/O 都通过串口让 Python 去操控;
  2. 逻辑直接跑在板子上,比如 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, util

board = 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, util

board = 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;
  • 各种带 CircuitPython Logo 的开发板。

它们的体验更像“在一个很小的 Linux 上用 Python 写脚本”:

  • 板子插上电脑,会出现一个 U 盘;
  • 把 main.py 拖进去,重启就自动运行;
  • 通过串口 / WebREPL 还能进交互式 REPL,现场调试 import machine 之类的东西。

简单感受一下 MicroPython 上的“点灯”:

from machine import Pin, Timer

led = 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