ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

BW16+ESP32-CYD:脑电EEG数据无线采集与实时波形显示方案

BW16+ESP32-CYD:脑电EEG数据无线采集与实时波形显示方案 1. 项目缘起与整体链路设计脑电采集这件事早年给人的印象就是一堆线缆加一台笨重放大器被采集的人只能老老实实坐在椅子上。这几年消费级 EEG 模块越来越小成本也压到了个人开发者能承受的区间于是把脑电数据无线传出来、在屏幕和网页上实时看到波形就成了一个很自然的折腾方向。我手上正好有一块 BW16 模块和一块 ESP32-CYD就是那种自带 2.4 寸彩屏的开发板就想着把这条链路搭起来脑电模块负责采集BW16 负责无线透传ESP32-CYD 负责接收、显示同时再推一份数据到网页端。这条链路的核心关键词是BW16、ESP32-CYD、EEG、BLE、UART。简单说脑电模块通过 UART 把采样数据吐给 BW16BW16 用 BLE 把数据广播/透传出去ESP32-CYD 作为接收端拿到数据后一边在本地屏幕上画波形一边通过它自己的 Wi-Fi 把数据转发到网页。整套东西解决的问题是让脑电数据摆脱线缆束缚同时提供本地屏幕 远程网页两种观察方式方便调试和演示。适合谁来参考如果你手上有消费级脑电模块、玩过 Arduino 或 ESP-IDF、对 BLE 和串口通信有基本概念那这篇内容基本可以照着复现。如果你是完全的新手也不用慌我会把 UART 协议、BLE 连接、数据解析这些环节拆开讲清楚尽量做到看完能动手。先说清楚一个前提脑电信号本身非常微弱通常在微伏级别而且极易被工频干扰、肌电、眼动污染。所以这条链路里数据链路的稳定性比采样精度更值得先关注——因为如果无线链路丢包、串口波特率对不上、BLE 连接频繁断开你后面做再多的去噪算法都是白搭。我踩过的第一个坑就是一开始只盯着波形好不好看结果发现是串口丢包导致的波形断裂白白怀疑了半天电极。整条链路我画成文字版是这样的脑电模块采集 模数转换→ UART 串口 → BW16BLE 透传BW16 → BLE 无线 → ESP32-CYDBLE 客户端ESP32-CYD → 本地 TFT 屏幕绘制波形ESP32-CYD → Wi-Fi → 网页端WebSocket 或 HTTP 轮询这个设计里BW16 和 ESP32-CYD 各自承担了不同角色BW16 是无线串口把 UART 数据原封不动搬到 BLE 上ESP32-CYD 是数据枢纽既做本地显示又做网络转发。为什么不让 BW16 直接连 Wi-Fi 推网页因为 BW16 的定位更偏向 BLE 透传而 ESP32-CYD 自带屏幕和 Wi-Fi做枢纽更合适。这个分工是我试过几种组合后定下来的后面会详细说。2. 硬件选型与核心模块解析2.1 BW16 为什么适合做 BLE 透传BW16 是基于 RTL8720DN 的模块双频 Wi-Fi 加 BLE 5.0社区里常被拿来当无线串口用。它的优势在于固件生态相对成熟AT 指令集能直接配置 BLE 透传模式不需要你从零写 BLE 协议栈。对于脑电这种数据流持续、包不大、要求低延迟的场景BLE 透传比自定义 GATT 服务省事得多。我选它的理由很直接脑电模块输出的是连续字节流我只需要一个管道把它搬到空中不需要在 BW16 上做任何解析。BW16 的 AT 透传模式正好干这个。相比之下如果用一个纯 BLE 芯片自己写服务光是处理连接参数、MTU、通知使能就要花不少时间对原型验证来说不划算。不过要注意BW16 的 AT 固件版本差异挺大不同批次的默认波特率、指令集细节可能不一样。我手上这块默认 UART 波特率是 115200但有的版本是 9600。上手第一件事就是用串口助手确认它能正常回 AT并查清当前波特率否则后面全是玄学问题。2.2 ESP32-CYD 的角色与屏幕优势ESP32-CYD 的CYD是Cheap Yellow Display的缩写典型配置是 ESP32-WROOM 加一块 2.4 寸 ILI9341 SPI 屏还带触摸部分版本。它最大的价值是把屏幕、主控、Wi-Fi/BLE 集成在一块板子上省去了单独接屏的布线麻烦。对脑电波形显示来说本地屏幕能让你在不打开电脑的情况下快速判断信号质量这个体验比纯网页端好太多。ESP32-CYD 在这条链路里同时干三件事作为 BLE 客户端接收 BW16 的数据、驱动 TFT 屏画实时波形、通过 Wi-Fi 把数据转发给网页。三件事并行对任务调度有要求所以后面我会用 FreeRTOS 的任务划分来拆开处理避免 BLE 回调和屏幕刷新互相阻塞。2.3 脑电模块与 UART 接口脑电模块这块市面上常见的消费级方案输出格式大多是包头 数据 校验的二进制帧波特率常见 57600 或 115200。我用的模块输出的是固定长度帧每帧包含若干通道的采样值。这里的关键是先搞清楚模块的帧格式包头字节是什么、每帧多少字节、采样值是大端还是小端、有没有校验位。这些信息通常在模块的数据手册里如果没有手册就得用串口助手抓原始数据自己分析。UART 本身是异步串行通信靠起始位和停止位同步不需要时钟线。它的时序是空闲高电平 → 起始位拉低 → 8 位数据LSB 先发→ 可选校验位 → 停止位拉高。理解这个时序对排查问题很有帮助比如波特率不匹配时你会看到满屏乱码本质就是采样点错位。热词里提到的 16550 是经典的 UART 控制器标准STM32 的 HAL 库、ESP32 的 UART 驱动本质上都是对这类硬件的封装理解了底层时序用哪家的库都不慌。2.4 关键参数对照环节模块关键参数我的取值采集输出脑电模块波特率 / 帧长115200 / 固定帧无线透传BW16BLE 透传 / 波特率AT 透传 / 115200接收显示ESP32-CYDBLE 客户端 / SPI 屏GATT 通知 / ILI9341网络转发ESP32-CYDWi-Fi / 协议WebSocket这张表是我实际跑通后的配置你可以作为起点但波特率一定要以你手上模块的实际值为准不要照抄。3. 核心细节解析与实操要点3.1 UART 数据帧的解析思路脑电模块吐出来的数据不是给人看的是二进制流。解析的核心是找包头、定帧长、取数据、验校验。我用的模块帧结构大致是1 字节包头比如 0xAA、1 字节长度、N 字节数据、1 字节校验和。解析时用一个环形缓冲区接收然后逐字节扫描找包头找到后判断缓冲区里是否够一帧够了就取出来校验校验通过才交给上层。这里有个容易忽略的点串口数据是流式的可能一帧被拆成两次收到也可能一次收到一帧半。所以不能用收到一次就读一帧的写法必须用缓冲区累积。我一开始就是直接在串口回调里解析结果数据一快就丢帧后来改成环形缓冲区加状态机才稳定。状态机大概是这几个状态等待包头 → 读取长度 → 读取数据 → 校验。每个状态只处理一个字节收到完整帧后触发一次帧就绪事件。这种写法对任何 UART 协议都通用热词里那些uart 传输通信时序uart 串口通信讲的就是这套底层逻辑。3.2 BLE 透传的连接与通知机制BW16 配成 BLE 透传后它对外表现为一个 BLE 从设备ESP32-CYD 作为主设备去连接它。连接建立后数据通过 GATT 通知Notify推送给主设备。这里的关键参数是MTU 和连接间隔。MTU 决定单次能传多少字节默认 23 字节实际有效载荷 20 字节连接间隔决定数据传输的频率单位是 1.25ms。脑电数据量大如果 MTU 太小一帧数据要拆成好几个通知包延迟和丢包风险都上升。我的做法是协商更大的 MTU比如 247这样一帧能塞进一个通知包。连接间隔我设成 15ms 左右兼顾延迟和功耗。这些参数在 ESP32 侧通过esp_ble_gatt_set_local_mtu和连接参数更新来设置。注意BLE 连接参数不是你想设多少就多少主从双方要协商最终以双方都支持的范围为准。如果从设备BW16不支持你请求的参数它会拒绝或给一个折中值。3.3 屏幕波形绘制的性能取舍ESP32-CYD 的屏幕是 SPI 接口刷屏速度有限。如果每收到一个采样点就重绘整屏帧率会惨不忍睹。我的做法是只重绘变化区域维护一个波形缓冲区新数据进来后只擦除并重绘波形移动的那一列或那几列。这样刷屏量大幅下降波形滚动也流畅。另一个技巧是降采样显示。脑电采样率可能几百 Hz但屏幕宽度只有 320 像素没必要每个点都画。我按屏幕宽度做抽取比如每 4 个采样点取一个显示视觉上完全够用还省了 CPU。这个取舍在嵌入式显示里很常见核心思想是显示是给人看的不是给示波器看的。3.4 网页端的数据通道选择网页端我一开始用 HTTP 轮询ESP32-CYD 起一个简单的 HTTP 服务网页定时请求最新数据。但轮询延迟高、请求频繁体验一般。后来换成 WebSocketESP32-CYD 作为服务端网页连上后数据主动推送延迟明显降低。WebSocket 在 ESP32 上有现成库比如esp_websocket_client或 Arduino 的WebSocketsServer配置不复杂。数据格式我用的是简单的文本协议每个采样点用逗号分隔网页端用 JavaScript 解析后画到 Canvas 上。为什么不用二进制因为原型阶段可读性比带宽更重要文本方便调试等稳定了再换二进制也不迟。4. 实操过程与核心环节实现4.1 第一步确认脑电模块的串口输出动手第一件事不是写代码是用 USB 转串口工具把脑电模块的输出抓到电脑上看。热词里提到的 FT231X、FT232R 都是常见的 USB 转 UART 芯片装好驱动后用串口助手以模块标称的波特率打开看能不能收到数据。如果收到的是乱码先怀疑波特率不对逐个试常见值9600、57600、115200。如果完全没数据检查接线模块 TX 接转换器 RX模块 RX 接转换器 TX共地。这一步确认了后面才有基础。我当时抓到的数据是一串十六进制对照手册确认了包头是 0xAA帧长 33 字节心里就有底了。4.2 第二步配置 BW16 进入 BLE 透传模式BW16 用 AT 指令配置。典型流程是先发AT确认通信正常然后设置串口波特率与脑电模块一致再配置 BLE 名称、透传模式、广播参数。具体指令因固件版本而异但思路一致让 BW16 上电后自动进入透传脑电模块的数据从 UART 进来直接从 BLE 出去。配置时我建议把参数写进 BW16 的 flash这样断电重启后不用重新配。配置完成后用手机上的 BLE 调试 App 搜一下能看到 BW16 广播的名字连上后订阅通知应该能看到脑电数据流。这一步是很好的中间验证点能确认采集到无线这段链路是通的。4.3 第三步ESP32-CYD 作为 BLE 客户端接收ESP32-CYD 这边用 ESP-IDF 或 Arduino 都行我用的是 Arduino 框架BLE 库用NimBLE因为它比原生 BLE 库省内存。核心流程是扫描 → 找到 BW16 的设备名 → 连接 → 发现服务 → 找到通知特征 → 使能通知 → 在回调里收数据。收数据的回调里我把字节流喂给前面说的环形缓冲区加状态机解析出完整帧后把采样值放进一个队列交给显示任务和网络任务消费。这里用队列是为了解耦BLE 回调只管收显示和网络各自按自己的节奏取互不阻塞。// 伪代码示意BLE 通知回调里只做入队 void onNotify(BLERemoteCharacteristic* chr, uint8_t* data, size_t len) { for (size_t i 0; i len; i) { ringBuffer.push(data[i]); // 喂给环形缓冲区 } // 状态机在另一个任务里跑解析出帧后 push 到队列 }4.4 第四步本地屏幕绘制波形显示任务从队列取采样值更新波形缓冲区然后重绘变化区域。我用的是 TFT_eSPI 库它支持局部刷新配合前面说的降采样帧率能稳定在可接受范围。波形缓冲区我做成环形新数据覆盖最旧的数据屏幕上就是一条向左滚动的波形。这里有个细节屏幕刷新和 BLE 接收如果放在同一个任务里BLE 一忙屏幕就卡。所以必须分任务。我用 FreeRTOS 起了三个任务BLE 接收任务、显示任务、网络任务通过队列通信。任务优先级上BLE 接收最高显示次之网络最低保证数据不丢。4.5 第五步Wi-Fi 转发到网页网络任务从队列取数据通过 WebSocket 推给网页。ESP32-CYD 先连 Wi-Fi然后起 WebSocket 服务端。网页端用一段简单的 HTML JavaScript连上 WebSocket 后接收数据用 Canvas 画波形。// 网页端示意 const ws new WebSocket(ws://192.168.x.x:81); ws.onmessage (evt) { const values evt.data.split(,).map(Number); drawWaveform(values); // 画到 Canvas };网页端的好处是可以多设备同时看手机、电脑都能打开适合演示。而且网页端可以做更复杂的处理比如加个简单的滤波、显示频谱这些在 ESP32 上算力不够放浏览器里就轻松了。4.6 关键参数计算示例以采样率 250Hz、单通道 2 字节为例数据率是 250 × 2 500 字节/秒。加上包头校验约 550 字节/秒。BLE 在连接间隔 15ms、每包 20 字节有效载荷下理论吞吐约 20 / 0.015 ≈ 1333 字节/秒够用。但如果 MTU 协商到 247单包有效载荷约 244 字节吞吐大幅提升余量更足。这就是为什么我强调要协商大 MTU——算一下就知道默认 MTU 下多通道高采样率会顶到天花板。5. 常见问题与排查技巧实录5.1 波形断裂或丢帧这是最常见的现象。排查顺序先看串口端有没有丢用串口助手对比再看 BLE 有没有丢看通知回调频率最后看显示任务有没有来不及消费队列。我遇到的一次是队列太小显示任务一慢就溢出把队列加大就好了。另一次是 BLE 连接间隔太长数据积压缩短间隔解决。5.2 BLE 连不上或频繁断开先确认 BW16 是否在广播用手机 App 搜一下。如果搜不到可能是 BW16 没进透传模式或供电不足。如果能搜到但连不上检查 ESP32 侧的服务 UUID 和特征 UUID 是否匹配。频繁断开通常是连接参数太激进或信号干扰把连接间隔放宽一点或者让两个模块离近些。5.3 串口乱码九成是波特率不匹配。剩下的一成是接线错误TX/RX 没交叉或电平不匹配3.3V 对 5V。BW16 和脑电模块都是 3.3V 电平直连没问题但如果中间接了 5V 的转换器就要注意电平转换。5.4 网页端延迟高如果是 HTTP 轮询换成 WebSocket。如果已经是 WebSocket 还延迟高检查是不是在 ESP32 侧做了太多处理把重活放到网页端。另外 Wi-Fi 信号差也会导致延迟尽量让 ESP32-CYD 离路由器近些。5.5 问题速查表现象可能原因排查动作波形断裂队列溢出 / 连接间隔长加大队列 / 缩短间隔连不上 BLE未广播 / UUID 不匹配手机搜设备 / 核对 UUID串口乱码波特率 / 接线试波特率 / 检查 TX-RX网页延迟轮询 / 信号差换 WebSocket / 靠近路由屏幕卡顿任务未分离拆 FreeRTOS 任务5.6 几条踩坑心得第一先通链路再谈算法。我见过太多人一上来就研究 EEG 去噪、源定位最小范数估计结果数据链路都不稳算法再好也是空中楼阁。第二每个环节都要有独立的验证手段串口用串口助手验BLE 用手机 App 验网页用浏览器控制台验。这样出问题时能快速定位是哪一段。第三参数不要照抄波特率、MTU、连接间隔都要根据自己硬件实测调整。6. 链路扩展与个人体会这条链路跑通后能扩展的方向不少。比如在 ESP32-CYD 上加个 SD 卡把数据存下来做离线分析或者在网页端加实时滤波和频谱显示把浏览器当成简易分析工具再或者把 BLE 换成 Wi-Fi 直连进一步降延迟。热词里提到的 EEG 去噪、源定位这些都可以在数据稳定之后放到 PC 或网页端去做不必压在嵌入式端。我个人在实际操作中的体会是原型阶段最值钱的不是功能多而是链路稳、可观测。屏幕和网页这两个观察窗口一个负责即时判断一个负责详细分析配合起来调试效率高很多。BW16 加 ESP32-CYD 这个组合成本不高资料也够适合拿来练手无线数据链路。真正难的不是把数据传出去而是传出去之后你信不信它——所以每个环节的验证手段比代码本身更重要。
返回列表