Picodevil:LLM辅助开发的树莓派Pico W桌面设备 📅 发布时间:2026/8/28 10:04:04 👁 浏览次数: Picodevil 是一个基于树莓派 Pico W 的桌面小设备名字来自 Pico 和 Devil 的组合。这个项目最特别的地方在于我从需求拆分、代码编写到错误排查几乎每一步都在用 LLM 辅助完成而且设备本身也通过 Wi-Fi 调用 LLM 接口把云端或本地模型返回的文本显示在 OLED 屏幕上。也就是说Picodevil 既是用 LLM 造出来的又能调用 LLM 工作。这不是一篇只讲“AI 帮我写代码”的泛泛文章。要真正把 LLM 用到电子 DIY 项目里必须清楚哪些事情模型能帮你省时间哪些事情必须靠人判断。接下来会按照从需求拆解到环境准备、代码生成、烧录验证、问题排查、工程化改造的顺序走一遍。如果你正好想用 LLM 开发一个嵌入式小项目或者想把手边的 Pico W 变成带有简单 AI 交互的桌面摆件这条路可以照着走。需要先说明Picodevil 只是示例项目名功能定义按照我的场景展开。你可以保留同一套流程去构建自己的“某某 devil”或者其他桌面设备。1. Picodevil 到底要做什么把一个想法拆成 LLM 能理解的任务1.1 功能与硬件清单Picodevil 的交互定义不能太复杂。作为示例项目我把它限定为四件事用户按下按钮后Picodevil 连接 Wi-Fi。调用云端或本地 LLM 接口请求一句“恶魔但友善”的英文短句。把返回文本显示在 SSD1306 OLED 屏幕上。用一颗 NeoPixel LED 表示“正在思考”和“回答完成”两种状态。这个功能范围适合第一次做 LLM 加微控制器的项目。如果一开始就加语音识别、多轮对话、触摸屏LLM 生成的代码会变得很难验证排查问题也会同时面对硬件、网络、模型行为三条链路。硬件部分可以按这个清单准备组件规格/说明用途树莓派 Pico W带 Wi-Fi 的 RP2040 开发板主控SSD1306 OLED128x64I2C 接口常见地址 0x3C显示文本NeoPixel LED内置 WS2812 的灯珠信号引脚用 3.3V 电平即可状态指示按钮轻触开关按下接地触发交互面包板/杜邦线原型阶段连接电路搭建和调试外壳可选3D 打印或亚克力固定成品在真正向 LLM 提问之前先把这个清单和功能范围写清楚。LLM 不能在硬件层面替你做判断但它会根据你提供的引脚和协议生成更准确的代码。1.2 交互时序和数据流Picodevil 的交互时序看起来不复杂但写代码前需要把数据流固定下来。按钮按下 - 主循环检测到低电平 - NeoPixel 亮青色 - WLAN 连接 - urequests.post(API_URL) - 解析 JSON - OLED 显示文本 - NeoPixel 亮红色 - 恢复等待状态这个数据流在排查问题时非常重要。代码如果出问题要先看它卡在哪一步是 Wi-Fi 没连上是请求超时还是 JSON 字段路径不对。没有数据流就只能反复拔 USB 重启。1.3 给 LLM 的项目提示词模板生成代码前LLM 需要看到完整约束。我第一次生成的代码因为只写了“Picodevil 显示一句话”结果模型给了一段 PC 上才能运行的 Python 脚本里面还出现了requests库。这个教训说明提示词必须包含硬件环境、接口协议、功能要求和输出格式。我给 LLM 的提示词模板如下你是一名熟悉树莓派 Pico W 和 MicroPython 的嵌入式工程师。 请为一个名为 Picodevil 的桌面设备编写 main.py。 硬件 - Raspberry Pi Pico W - SSD1306 OLED 128x64I2C 使用 GPIO0(SDA) 和 GPIO1(SCL) - NeoPixel 接 GP16 - 按钮接 GP14按下为低电平 功能 - 开机后 OLED 显示 Press button - 按下按钮后通过 urequests 调用 OpenAI 兼容的 LLM 接口 - 得到英文短句后全文显示在 OLED 上 - NeoPixel 在请求期间显示青色完成后显示红色 - 请求失败时 OLED 显示 Error不能卡死 输出 - 只输出 main.py 代码不要输出解释 - 使用 ssd1306 库引用来自 ssd1306.py这个模板的核心作用是把“随机生成代码”变成“在约束下生成代码”。省略任何一项都很容易得到引脚错误、库不存在或者运行时崩溃的代码。2. 环境准备先让 Pico W 能被计算机控制2.1 硬件与工具准备在让 LLM 生成 main.py 之前先把开发环境跑通。环境不通生成的代码再好也只能在电脑上“看起来正确”。电脑端需要准备Python 3.8 或更高版本。mpremote命令行工具用于上传文件和进入交互命令。USB 数据线注意有些线只能充电不能传数据。MicroPython 官方发布的 .uf2 固件文件。不需要使用完整桌面 IDE。Thonny 也可以但命令行工具更适合写博客和脚本化操作。2.2 烧录 MicroPython 固件刷固件的步骤是固定的从 MicroPython 官方下载页选择 Raspberry Pi Pico W 对应的 .uf2 文件。按住 Pico W 上的 BOOTSEL 按钮再插入 USB 线。电脑上会多出一个名为 RPI-RP2 的 U 盘。把下载好的 .uf2 文件直接拖到 U 盘里。文件复制完成后Pico W 会自动重启。这一步非常容易出错的地方是选错固件。如果下载了普通 Pico 固件而不是 Pico W 固件后面代码里会出现network模块不存在或者 Wi-Fi 功能失效。固件文件名通常带有 PICO_W 字样下载前先确认。2.3 安装 mpremote 并检查串口连接安装 mpremotepython -m pip install mpremote查看是否识别到设备mpremote version mpremote info正常输出会显示 mpremote 版本和 Pico W 的板卡信息。如果电脑上有多个串口设备可以指定端口mpremote connect COM3 info在 Linux 下常见路径是 /dev/ttyACM0Windows 下常见 COM3 或 COM4。如果 mpremote 找不到设备优先检查 USB 线、BOOTSEL 状态和驱动。还可以用一条短命令确认 MicroPython 环境mpremote exec import sys; print(sys.platform, sys.implementation)这一步完成说明 Pico W 已经可以接受代码上传。之后再让 LLM 生成程序就不会出现“写完代码不知道怎么跑”的问题。3. 用 LLM 生成第一版 MicroPython 代码3.1 推荐生成方式分模块生成不要一次生成整个工程如果直接让 LLM 一次生成“完整可运行的 Picodevil”效果通常不理想。原因不是模型能力不够而是最终代码里既有 OLED 驱动、按钮逻辑又有网络请求和 JSON 解析一旦出错很难定位。推荐分四轮生成第一轮让 LLM 生成 OLED 显示测试代码。第二轮让 LLM 生成按钮输入代码并打印当前电平。第三轮让 LLM 生成 Wi-Fi 连接和 LLM 接口调用代码。第四轮把前两轮和第三轮的结果合并成 main.py。每一轮都有明确验证点。OLED 能显示说明接线和 I2C 地址正确按钮能打印电平说明 GPIO 配置正确能连 Wi-Fi说明网络环境正确。最后合并时代码即使报错问题也被限制在“集成”这一层。3.2 完整可运行示例代码下面是我最后用 LLM 生成的 main.py 示例。它依赖一个ssd1306.py驱动文件这个驱动需要和 main.py 一起上传到 Pico WmicroPython 固件默认不包含该文件。import time import network import urequests import ujson from machine import Pin, I2C from neopixel import NeoPixel try: from ssd1306 import SSD1306_I2C except ImportError: print(ERROR: upload ssd1306.py first) raise # 配置区 WIFI_SSID your-wifi-ssid WIFI_PASSWORD your-wifi-password API_URL http://192.168.1.100:8000/v1/chat/completions API_KEY your-api-key MODEL your-model-name # 引脚定义 I2C_SDA Pin(0) I2C_SCL Pin(1) OLED_ADDR 0x3C NEOPIXEL_PIN 16 BUTTON_PIN 14 # 初始化外设 i2c I2C(0, sdaI2C_SDA, sclI2C_SCL, freq400000) oled SSD1306_I2C(128, 64, i2c, addrOLED_ADDR) np NeoPixel(Pin(NEOPIXEL_PIN, Pin.OUT), 1) btn Pin(BUTTON_PIN, Pin.IN, Pin.PULL_UP) def oled_show(lines): oled.fill(0) y 0 for line in lines: if y 64: oled.text(line, 0, y) y 16 oled.show() def set_led(rgb): np[0] rgb np.write() def connect_wifi(): wlan network.WLAN(network.STA_IF) wlan.active(True) if wlan.isconnected(): return True wlan.connect(WIFI_SSID, WIFI_PASSWORD) for _ in range(20): if wlan.isconnected(): return True time.sleep(0.5) return False def ask_llm(prompt): headers { Content-Type: application/json, Authorization: Bearer API_KEY } payload { model: MODEL, messages: [ {role: system, content: You are Picodevil, a diabolical but friendly desk companion. Reply in one English sentence shorter than 20 words.}, {role: user, content: prompt} ], temperature: 0.8 } resp urequests.post(API_URL, headersheaders, dataujson.dumps(payload), timeout10) result ujson.loads(resp.text) resp.close() text result[choices][0][message][content].strip() return .join(text.split()) def wrap_text(text, max_chars16): lines [] while len(text) max_chars: space text.rfind( , 0, max_chars) if space -1: space max_chars lines.append(text[:space]) text text[space:].strip() if text: lines.append(text) return lines[:4] oled_show([Picodevil, Press button]) set_led((0, 0, 0)) while True: if btn.value() 0: oled_show([Thinking...]) set_led((0, 60, 60)) time.sleep(0.1) if not connect_wifi(): oled_show([WiFi failed]) set_led((60, 0, 0)) time.sleep(2) continue try: text ask_llm(Give me one devilish but friendly one-liner.) oled_show([Picodevil:] wrap_text(text)) set_led((60, 0, 0)) except Exception as err: oled_show([Error, str(err)]) set_led((60, 0, 0)) # 简单防抖松开按钮后才会再次触发 while btn.value() 0: time.sleep(0.05) time.sleep(0.05)代码中使用的 API 地址是本地或内网服务地址。如果换成云端 HTTPS 接口urequests可能需要处理证书问题响应过大也会占用更多内存这在排错时要留意。3.3 代码关键点解释network.WLAN(network.STA_IF)是 Pico W 的 Wi-Fi 站模式适合连接路由器。Pin(BUTTON_PIN, Pin.IN, Pin.PULL_UP)启用了内部上拉按钮另一端接地。这样按钮按下时读到 0代码用btn.value() 0判断逻辑上更符合物理接线。set_led接收一个 RGB 三元组。NeoPixel 写值后必须调用np.write()才会真正输出到灯珠。ask_llm使用ujson.dumps序列化请求体并用urequests.post发送。这里千万不能用 PC 端 Python 的requests库MicroPython 没有这个库。wrap_text按空格切分文本避免 OLED 每行只显示几个字符。因为 SSD1306 的默认字体不支持中文我让模型只返回英文短句。如果要做中文显示需要额外制作中文字库或改用支持全字库的屏幕。4. 烧录验证与日志排查4.1 上传文件到 Pico W在 main.py 所在的目录下执行mpremote cp ssd1306.py : mpremote cp main.py : mpremote reset:在这里表示 Pico W 的根目录。如果ssd1306.py不在当前目录mpremote cp会报源文件错误。上传完成后mpremote reset会让设备重启并自动运行 main.py。如果不想手动重启也可以进入 REPL 后按 CtrlD 执行软复位。4.2 运行验证与预期输出把 USB 串口接到电脑上执行mpremote repl正常现象应包含这几个阶段上电后 OLED 显示 “Picodevil” 和 “Press button”。按下按钮后OLED 变为 “Thinking...”NeoPixel 变成青色。请求成功后OLED 第一行显示 “Picodevil:”下面若干行显示英文短句NeoPixel 变成红色。松开按钮后系统回到等待状态。在除错阶段建议在ask_llm函数里临时加一句 DEBUGprint(llm response:, text)这样返回内容会同时打印到 REPL方便确认 OLED 显示是否正确。验证完成后可以去掉这行。4.3 从日志倒推问题的排查顺序Picodevil 集成的问题必须按“执行到哪一步失败”来排查。优先顺序是输入是否正确、文件是否上传、引脚是否接对、依赖是否上传、配置是否生效、日志有什么异常。现象可能原因检查方式上电黑屏I2C 地址错误、SDA/SCL 接反、ssd1306.py 未上传用 I2C scan 确认地址按下按钮无反应GPIO 引脚错误、按钮接线不对、上拉未生效打印btn.value()观察电平一直显示 WiFi failedSSID/密码错误、固件是普通 Pico 而非 Pico W、路由器拒绝连接打印wlan.status()API Error接口地址错误、密钥无效、超时、返回 JSON 结构不对先用 PC 端 curl 或 py 脚本测试接口MemoryErrorAPI 返回内容太大、OLED 或网络库占用过多缩短返回长度、换小模型、关闭无用外设I2C 扫描命令可以单独执行from machine import I2C, Pin i2c I2C(0, sdaPin(0), sclPin(1), freq400000) print(i2c.scan())如果扫描结果等于[60]说明 SSD1306 的 I2C 地址是 0x3C接线正确。如果扫描不到设备优先检查 SDA、SCL 是否插反以及 OLED 是否需要独立供电。5. 常见坑LLM 写嵌入式代码时最容易在哪里翻车5.1 五个高频问题速查表LLM 生成 MicroPython 代码时问题通常不是“模型不懂编程”而是“模型默认生成 PC 端 Python”。下面是几个高频问题问题现象根因解决方案import requests 失败MicroPython 没有 requests用 urequests并告诉模型环境是 MicroPythonPin(14) 被理解成物理引脚 14只写了“接引脚14”没写 GPIO 编号一律使用 GPIO 编号例如 GP14找不到 ssd1306 模块ssd1306.py 未上传到 Pico W从 micropython-lib 拷贝到项目目录并上传按钮按下后频繁触发没有防抖或循环速度太快采用松开检测和短延时API 返回字段解析报错写死了错误的 JSON 路径先打印原始返回再根据实际字段解析这五类问题几乎都能通过“更清楚的提示词约束 更小的验证步骤”避免。5.2 深入分析三个典型坑坑一把 PC 端 Python 代码当成 MicroPython 代码。这是遇到最多的坑。LLM 可能会生成import requests、from urllib.request import urlopen甚至建议你用pip install。MicroPython 是运行在微控制器上的精简解释器没有完整 Python 生态。正确的做法是在提示词里明确写出“只能使用 MicroPython 内置模块和 ssd1306、neopixel使用 urequests 发 HTTP 请求”。坑二引脚编号和物理引脚混淆。Pico W 的芯片引脚叫 GPIO0、GPIO1但开发板丝印可能标注成 GP0、GP1物理位置却在不同位置。LLM 如果只收到“用第 0 针和第 1 针”它可能真的给你写Pin(0)和Pin(1)但接线的人接在物理 pin 1 和 pin 2结果还是能对应。最容易出错的是生成 ESP32 风格代码例如Pin(14)在 ESP32 和 Pico W 上含义完全不同。