基于RP2350B设计的无线画笔
该项目使用了Arduino、Python,实现了基于RP2350B设计的无线画笔的设计,它的主要功能为:通过倾斜手持的运动感应笔来控制光标,并在配套的电脑画布软件上进行绘画的系统,笔身集成的按钮可用于实时调整画笔的颜色与粗细,并通过集成的WS2812B灯珠实时预览。
标签
嵌入式系统
ADC
开发板
接口
qqice
更新2025-07-10
香港中文大学(深圳)
19



项目介绍:RP2350 无线绘图笔

该项目是一个基于 RP2350 微控制器和 nRF52840 蓝牙模块实现的无线数字绘图笔系统。它旨在提供一种直观、无拘无束的数字创作体验,将物理手势与数字画布无缝连接。用户可以通过倾斜和移动手中的笔,直接控制电脑屏幕上的光标进行绘画,并通过笔身上集成的多功能按键,在创作过程中不中断地调整画笔的颜色与粗细。

核心功能

  • 无线连接: 采用两块 nRF52840 开发板构建稳定的低功耗蓝牙(BLE)串口通信链路,将绘图笔与电脑分离,彻底摆脱线缆束缚,提供更大的创作自由度。
  • 体感控制: 内置 KXTJ3-1057 三轴惯性测量单元(IMU),实时捕捉笔身的倾斜角度,并将其精确转换为画布上光标的移动指令。
  • 参数即时调节: 用户可通过笔身上的按键随时进入设置模式,轻松调整画笔的粗细、以及红 (R)、绿 (G)、蓝 (B) 三色通道的数值。
  • 实时颜色预览: 笔头集成了两颗 WS2812B RGB LED 灯珠,能够实时显示当前选定或正在编辑的颜色,为用户提供最直观的视觉反馈。
  • 配套桌面应用: 使用 Python 和 Tkinter 库开发了一款简洁的跨平台画布应用。该应用在 Linux 系统上运行,负责接收并解析来自画笔的指令,实时渲染用户的笔触,并清晰地展示画笔的各项状态。

技术栈

  • 硬件:
    • 绘图笔控制器: 基于 RP2350 开发板,集成 KXTJ3-1057 IMU、WS2812B LED 及控制按键。
    • 无线发射端: 一块 nRF52840 开发板,烧录 bleuart 程序,通过引脚连接到 RP2350,作为蓝牙外设。
    • 无线接收端: 另一块 nRF52840 开发板,烧录 central_bleuart 程序,作为蓝牙中心设备,通过 USB 连接到 Linux 系统的电脑。
  • 软件:
    • 固件:
      • RP2350_Draw_Controller.ino: 运行在 RP2350 上,使用 Arduino (C/C++) 编写,负责传感器数据读取、按键逻辑处理和状态机管理。
      • bleuart.ino / central_bleuart.ino: 运行在 nRF52840 上,构建了 BLE 串口服务。
    • 桌面应用: 使用 Python 编写,Tkinter 库用于构建图形用户界面,PySerial 库用于与接收端进行串口通信。
  • 通信协议:
    • 笔内通信: RP2350 控制器与 nRF52840 发射端之间通过 UART 串口进行通信。
    • 无线通信: 两块 nRF52840 开发板之间通过 BLE UART 服务进行无线数据传输。
    • 有线通信: nRF52840 接收端与电脑上的 Python 应用之间通过 USB 虚拟串口进行 JSON 格式的数据交换。

通过这套系统,我们将传统的绘画姿态与现代数字技术相结合,创造出一款功能完整、交互自然且高度集成的无线绘图工具。


硬件介绍

  • RP2350: 项目的核心控制器,负责处理来自 IMU 的运动数据和用户的按键输入,并控制 LED 灯珠的状态。其强大的处理能力确保了数据流的实时性和稳定性。
  • nRF52840: 两块 nRF52840 开发板构成了无线通信的桥梁。一块作为发射端(Peripheral),与 RP2350 连接,将处理后的数据通过 BLE 发送出去;另一块作为接收端(Central),通过 USB 连接电脑,将接收到的 BLE 数据转发给 Python 应用。
  • KXTJ3-1057: 一款高灵敏度的三轴加速度传感器(IMU),用于检测笔身的倾斜姿态,是实现体感控制光标的关键。
  • WS2812B: 两颗可编程 RGB LED 灯珠,用于实时显示画笔颜色,为用户提供直观的视觉反馈。

方案框图和项目设计思路

系统方案框图

graph LR
subgraph "绘图笔 (Pen)"
RP2350["<b>RP2350</b><br/>- KXTJ3 IMU<br/>- Buttons<br/>- WS2812B LEDs"] -- UART --> NRF_P["<b>nRF52840 (Peripheral)</b><br/>bleuart"];
end

subgraph "接收端 (Receiver)"
NRF_C["<b>nRF52840 (Central)</b><br/>central_bleuart"];
end

subgraph "电脑 (Computer)"
PC["<b>PC (Linux)</b><br/>Python App"];
end

NRF_P -- BLE --> NRF_C;
NRF_C -- USB --> PC;

设计思路

本项目的核心设计思路是模块化用户体验优先

  1. 功能分离: 我们将核心的运动感应和逻辑处理(RP2350)与无线通信功能(nRF52840)分离开来。这种设计降低了单个微控制器的负担,使得代码逻辑更清晰,也便于未来对任一模块进行独立升级(例如,更换更强的无线模块或更精确的传感器)。
  2. 状态机管理: 笔刷的控制逻辑采用了有限状态机(Finite State Machine, FSM)。系统定义了“绘图”、“设置菜单”、“编辑数值”等多种状态。用户的按键操作(如长按、短按)会触发状态之间的切换。这种模式极大地简化了复杂的用户交互逻辑,避免了使用大量嵌套的 if-else 语句,使代码更易于维护和扩展。
  3. 标准化数据交换: 硬件与上位机之间采用 JSON 格式进行通信。这是一种轻量级、易于人类阅读和机器解析的数据格式。通过定义清晰的 actionpayload 字段,我们实现了可靠的指令传递,同时也保证了未来新增功能时,通信协议可以轻松扩展。

软件流程图和关键代码介绍

固件 (RP2350_Draw_Controller.ino)

软件流程图

graph TD
A[Start] --> B[Initialize<br/>- Serial<br/>- IMU, LEDs];
B --> C{Loop};
C --> D{currentState == STATE_DRAWING?};
D -- Yes --> E[handleIMU<br/>Read & Send Move Cmd];
D -- No --> F[handleSwitches<br/>Process Buttons via State Machine];
E --> F;
F --> G[updateStatusLEDs];
G --> C;

关键代码:状态机实现

handleSwitches() 函数是整个固件交互逻辑的核心。它通过一个 switch 语句实现了状态机,根据当前的 currentState 变量来决定如何响应用户的按键输入。

// file: RP2350_Draw_Controller.ino

void handleSwitches() {
// ... 读取按键 ...

switch (currentState) {
case STATE_DRAWING:
// 处理落笔、橡皮擦逻辑
// 检测长按“确认键”进入 STATE_SETTING_MENU
break;

case STATE_SETTING_MENU:
// 处理“+”/“-”在菜单项间导航
// 检测长按“确认键”进入对应的编辑状态 (e.g., STATE_EDITING_THICKNESS)
// 检测长按“取消键”返回 STATE_DRAWING
break;

case STATE_EDITING_THICKNESS:
case STATE_EDITING_COLOR_R: // ... and other editing states
// 处理“+”/“-”修改数值
// 在修改颜色时,调用 updateNeopixels() 实时预览
// 检测长按“确认键”保存修改,并返回 STATE_SETTING_MENU
// 检测长按“取消键”放弃修改,并返回 STATE_SETTING_MENU
break;
}
}

上位机 (canvas_app.py)

软件流程图

graph TD
subgraph Main Thread
A[Start] --> B[Initialize UI - Tkinter Window, Canvas, Labels];
B --> C[process_commands - runs every 100ms];
C --> D{Is queue empty?};
D -- No --> E[Get command from queue];
E --> F[Process action - move, set_color, set_device_state];
F --> G[update_status_bar];
G --> C;
D -- Yes --> C;
end

subgraph Background Thread
H[listen_for_serial] --> I{Serial Data Received?};
I -- Yes --> J[command_queue.put];
J --> I;
I -- No --> I;
end

关键代码:指令处理与状态更新

process_commands() 方法是上位机应用的核心。它以非阻塞的方式从队列中获取并处理来自硬件的 JSON 指令,并更新 UI 界面。

# file: canvas_app.py

def process_commands(self):
try:
while not self.command_queue.empty():
command = self.command_queue.get_nowait()
action = command.get("action")
payload = command.get("payload", {})

# ... (处理 move, set_color, set_thickness 等)

elif action == "set_device_state":
self.device_state = payload.get("state", "Unknown")
self.device_state_value = payload.get("value", "")
needs_status_update = True

if needs_status_update:
self.update_status_bar()

except queue.Empty:
pass
finally:
self.root.after(100, self.process_commands)

实物功能展示图及说明

硬件连线图:黑色:GND 红色:5V 绿色和黄色:分别连接RP2350B和NRF52840的TX和RX


切换颜色到紫色#7300a5,开发板上状态灯实时变为紫色;

切换画笔粗细到21,可见上位机端的画笔粗细有相应变化。

切换画笔颜色到青绿色#73f5a5,开发板状态灯同步变化。

  1. 绘图状态: 用户手持绘图笔,在空中倾斜。电脑屏幕上的 Python 画布程序中,一个十字光标会跟随笔的倾斜而移动。当用户按下“落笔”键时,光标走过的路径会留下由当前颜色和粗细决定的线条。此时,笔尖的 LED 灯珠显示着当前画笔的颜色(例如,红色)。Python 程序的状态栏显示 Device: Drawing
  2. 进入设置菜单: 用户长按“确认/修改”键后,进入设置模式。Python 程序的状态栏变为 Device: Thickness,表示当前可以对“画笔粗细”进行调整。用户短按“+”或“-”键,状态栏会依次切换显示 Device: Color RDevice: Color G 等,用于选择要修改的项目。
  3. 编辑数值与实时预览: 当菜单停在 Color R 时,用户再次长按“确认/修改”键,进入编辑模式。状态栏显示 Device: Red: 100。用户每按一次“+”键,数值增加 5,状态栏同步更新为 Device: Red: 105。最关键的是,笔尖的 WS2812B 灯珠颜色会实时地根据临时的 R、G、B 值进行变化,直观地展示出正在调配的颜色。长按“确认”保存后,状态栏返回菜单模式,LED 灯珠保持新颜色。若长按“取消”,则状态栏返回菜单模式,LED 灯珠恢复到修改前的颜色。

项目中遇到的难题和解决方法

  1. 难题:复杂的按键逻辑管理
    • 问题描述: 项目初期,需要处理“修改/确认”、“取消”、“+”、“-”四个按键的多种操作(长按、短按、重复触发),并根据当前是“绘图”还是“设置”状态做出不同响应。使用传统的 if-else 结构会导致代码逻辑极其混乱、难以维护。
    • 解决方法: 引入了有限状态机(FSM) 的设计模式。通过将程序流程划分为明确的几个状态(STATE_DRAWING, STATE_SETTING_MENU, STATE_EDITING_...),并在每个状态下独立处理按键逻辑,极大地简化了代码结构。这使得添加新功能或修改现有逻辑变得清晰而高效。
  1. 难题:UI 布局与画布可用空间冲突
    • 问题描述: 在 Python 的 Tkinter 应用中,底部的状态栏不断增加新的信息标签后,挤占了主画布的垂直空间,但画布的逻辑尺寸并未改变。这导致光标无法到达画布的下边缘区域,影响了使用体验。
    • 解决方法: 调整了画布的初始尺寸。将分辨率从 800x600 更改为更宽的 1280x720 (720p)。这个简单的改动不仅为状态栏提供了充足的空间,也为用户提供了更舒适的绘图区域,解决了画布不可达的问题。
  1. 难题:颜色调节缺乏直观反馈
    • 问题描述: 在最初的版本中,用户在编辑 R、G、B 值时,只有在按下“确认”键之后,笔尖的 LED 灯珠颜色才会更新。这使得颜色调节过程完全依赖于想象和猜测,非常不直观。
    • 解决方法: 对固件逻辑进行了优化。在进入颜色编辑状态时,将当前的颜色值存入临时变量。在用户按“+”或“-”调节数值时,立即使用临时颜色值去更新 WS2812B 灯珠。如果用户最终选择取消,则用保存的原始颜色值恢复灯珠显示。这一改进提供了实时颜色预览功能,极大地提升了用户体验。



附件下载
RP2350_Draw.zip
团队介绍
null
团队成员
qqice
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号