尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

基于BW16与ESP32-CYD的脑电无线采集与实时波形显示原型

发布时间:2026/9/30 0:24:49

资讯中心
01
ARTICLE

基于BW16与ESP32-CYD的脑电无线采集与实时波形显示原型

基于BW16与ESP32-CYD的脑电无线采集与实时波形显示原型
脑电采集这件事很多人第一反应是门槛高、设备贵、链路复杂。我最初也是这么想的直到手上攒了一块 BW16 模组和一块 ESP32-CYD就是那种带 2.4 寸屏、价格便宜到离谱的开发板琢磨着能不能把一条从脑电前端到屏幕、再到网页的无线链路给搭起来。折腾了大概两周中间踩了不少坑最后跑通了脑电模块通过串口把数据吐给 BW16BW16 用 BLE 把数据转发出去ESP32-CYD 一边接收一边在本地屏幕上画波形同时开一个轻量网页服务手机或电脑连上去就能看实时曲线。这条链路的价值在于原型验证四个字。它不追求医疗级精度也不追求工业级稳定性它解决的是我有个脑电模块想快速看到数据、验证算法、做个 demo这个具体问题。适合谁看做可穿戴设备早期验证的硬件工程师、想入门生物信号处理的学生、以及手上有一堆便宜模组想折腾点东西的爱好者。整条链路涉及 BLE 透传、UART 协议、EEG 去噪、屏幕刷新和网页实时推送几个核心点我会把每个环节的选择理由、实操步骤和踩坑经验都摊开讲。1. 为什么选 BW16 做无线桥接而不是直接上 ESP321.1 手头模组的现实约束与分工逻辑先说清楚我为什么没有让 ESP32-CYD 直接连脑电模块。理论上完全可以ESP32 有 UART接上脑电模块的 TX/RX 就能读数据然后自己开 BLE 或者 WiFi 往外发。但实际用下来有几个问题ESP32-CYD 的屏幕刷新和网页服务已经占了相当一部分 CPU 和内存如果再让它同时处理 BLE 协议栈和串口中断波形刷新会明显卡顿尤其在 250Hz 采样率下每 4ms 就有一帧数据进来任务调度压力不小。BW16 是瑞昱 RTL8720DN 方案的模组双频 WiFi 加 BLE5.0关键是它跑的是自己的固件串口透传这件事对它来说很轻。我把它当成一个无线串口线来用脑电模块的 UART 数据进来它原样通过 BLE 发出去不做任何解析。这样 ESP32-CYD 只需要专心做接收、显示和网页推送职责清晰调试也方便——哪一段出问题就单独查哪一段。提示BW16 的固件版本差异较大建议先用官方 AT 固件确认基础透传功能正常再考虑是否需要自己烧录定制固件。我一开始用的是某宝卖家给的固件BLE 服务 UUID 是固定的后来换了官方固件才拿到可配置的接口。1.2 BLE 透传与 UART 的速率匹配计算这里有个容易被忽略的点BLE 的实际吞吐和 UART 波特率要匹配。脑电模块我用的采样率是 250Hz每个采样点 24 位加上包头包尾大概 4 字节一帧算下来 250 × 4 1000 字节/秒。UART 这边设 115200 波特率理论 11520 字节/秒绰绰有余。但 BLE 这边要注意BLE 4.2 以上的实际应用层吞吐通常在 5-20KB/s 之间取决于连接间隔和 MTU。1000 字节/秒完全在舒适区内。如果你把采样率提到 1000Hz 甚至更高就要重新算这笔账了。我实测在 250Hz 下BW16 到 ESP32-CYD 的 BLE 链路延迟稳定在 20-40ms对于原型验证足够用。参数数值说明采样率250Hz单通道单帧字节4 字节含包头数据速率1000 B/s计算值UART 波特率115200余量充足BLE 实测延迟20-40ms连接间隔 15ms1.3 供电与共地最容易被忽视的硬件细节脑电模块、BW16、ESP32-CYD 三者供电如果不共地串口通信会出现随机乱码。我一开始用两个 USB 口分别给 BW16 和 ESP32-CYD 供电结果数据时不时丢包。后来把三者的 GND 全部连在一起问题消失。另外脑电模块对电源噪声敏感建议单独用 LDO 供电不要和 WiFi 模组共用一路 DCDC否则工频干扰会直接进信号。2. 脑电模块到 BW16 的 UART 链路搭建2.1 串口参数协商与帧格式约定脑电模块输出的是标准 UART 异步串口我用的配置是 115200-8-N-1。这里的关键是帧格式要提前约定好因为 BW16 做透传时不管内容ESP32-CYD 收到后需要知道怎么切分。我的帧格式是0xAA 0x55 开头然后 3 字节采样值24 位有符号最后 1 字节校验和。总共 6 字节一帧。为什么用 0xAA 0x55 做包头因为这两个字节在 24 位采样数据里连续出现的概率极低同步起来简单。校验和用简单的累加取反虽然不强但对于原型阶段够用。如果你要做更严谨的可以上 CRC16但会增加 MCU 负担。// 帧解析伪代码 uint8_t buf[6]; if (buf[0] 0xAA buf[1] 0x55) { int32_t sample (buf[2] 16) | (buf[3] 8) | buf[4]; if (sample 0x800000) sample | 0xFF000000; // 符号扩展 uint8_t checksum ~(buf[2] buf[3] buf[4]); if (checksum buf[5]) { // 有效采样 } }2.2 BW16 透传固件的配置要点BW16 出厂固件通常支持 AT 指令配置 BLE 透传。我用的流程是先通过 UART 发 AT 指令进入配置模式设置 BLE 名称、服务 UUID、特征 UUID然后切换到透传模式。这里有个坑不同批次的 BW16 模组 AT 指令集有差异有的用ATBLEUART有的用ATBLESER。建议先发ATHELP看支持哪些指令。配置完成后BW16 会把 UART 收到的数据自动通过 BLE 通知Notify发出去。ESP32-CYD 作为 BLE 客户端订阅这个特征即可。注意 BW16 的 BLE 默认连接间隔可能比较长比如 50ms如果延迟敏感要发指令改成 15ms 左右。2.3 实测中的丢包与重连处理透传链路最怕的是 BLE 断连。我实测在 2 米范围内基本稳定但隔一堵墙就会偶发断连。BW16 固件一般有自动重连机制但 ESP32-CYD 这边也要做处理检测到 BLE 断开后重新扫描并连接同时清空本地缓冲区避免旧数据污染新连接。另外 UART 这边如果脑电模块上电比 BW16 早BW16 可能已经进入透传模式但没收到数据这时候发几个 0x00 唤醒一下就行。我遇到过脑电模块上电后前 2 秒数据全是 0 的情况后来发现是模块内部初始化需要时间加个 2 秒延时再开始解析就好了。3. ESP32-CYD 端的 BLE 接收与屏幕波形刷新3.1 BLE 客户端连接与数据回调ESP32-CYD 这边我用的是 Arduino 框架下的 BLE 库。流程是扫描设备 - 找到 BW16 的 BLE 名称 - 连接 - 发现服务 - 订阅特征通知。数据到达时会在回调函数里触发我把数据先塞进一个环形缓冲区主循环再从缓冲区取出来处理。这样做的好处是回调函数里不做耗时操作避免阻塞 BLE 协议栈。// BLE 回调中只做入队 void notifyCallback(BLERemoteCharacteristic* chr, uint8_t* data, size_t len) { for (size_t i 0; i len; i) { ringBuffer[writeIdx] data[i]; if (writeIdx RING_SIZE) writeIdx 0; } }环形缓冲区大小我设的是 1024 字节按 1000 字节/秒算能缓冲 1 秒的数据足够主循环处理。3.2 屏幕刷新策略局部刷新与双缓冲ESP32-CYD 的屏幕是 240x320 的 TFTSPI 接口。如果每来一个采样点就全屏重绘刷新率根本上不去。我的做法是屏幕分成上下两部分上半部分显示实时波形下半部分显示数值和状态。波形区域用局部刷新只重绘变化的那一列。具体实现是维护一个 240 点的波形数组每来一个新采样就左移一位然后在最右侧画新点。但这样每次都要重绘整个波形区。更高效的做法是用双缓冲在内存里维护一个 240x100 的位图更新后一次性推送到屏幕。我实测局部刷新能跑到 30fps 以上双缓冲全推大概 15fps对于 250Hz 采样来说每 8 个点刷新一次屏幕就够了。注意ESP32-CYD 的屏幕 SPI 和 SD 卡共用引脚如果你插了 SD 卡屏幕刷新会变慢。原型阶段建议不插 SD 卡。3.3 采样数据的滑动平均与显示缩放脑电信号幅度通常在 10-100 微伏经过前端放大后输出范围在 0-3.3V 之间。直接显示的话基线漂移会让波形跑出屏幕。我加了一个简单的滑动平均去基线维护最近 250 个采样的平均值显示时减去这个均值。这样波形始终居中。缩放方面我设了一个自动增益如果波形幅度超过屏幕高度的 80%就自动缩小如果小于 20%就放大。这个逻辑用简单的阈值判断就能实现不需要复杂的 AGC 算法。实测下来对于睁眼闭眼产生的 alpha 波自动增益能很好地保持波形可见。4. 网页端实时波形展示的实现路径4.1 ESP32-CYD 上的轻量 WebSocket 服务ESP32-CYD 同时开 WiFi 和 BLE 是可以的但要注意内存。我用的是 ESP32 的 WebSocket 库在 ESP32-CYD 上跑一个简单的 WebSocket 服务器端口 81。网页端用 JavaScript 的 WebSocket API 连接收到数据后画到 Canvas 上。这里的关键是数据格式要精简。我一开始用 JSON发现解析开销大后来改成二进制每个采样点 2 字节16 位有符号一次发 10 个点就是 20 字节。网页端用 DataView 解析速度很快。// 网页端解析 ws.onmessage (event) { const data new Int16Array(event.data); for (let i 0; i data.length; i) { drawPoint(data[i]); } };4.2 网页 Canvas 绘图的性能优化Canvas 绘图如果每来一个点就 clearRect 重绘在 250Hz 下会卡。我的做法是Canvas 宽度设 800维护一个 800 点的数组新数据来了就左移然后只重绘变化的部分。或者更简单用两个 Canvas 交替一个显示一个绘制绘制完成后交换。我实测用 requestAnimationFrame 控制刷新率在 30fps每帧批量绘制积累的点流畅度很好。另外网页端也要做去基线处理和屏幕端逻辑一致。我直接在 JavaScript 里做滑动平均窗口大小 250 点。这样即使 ESP32 端不做处理网页端也能看到干净的波形。4.3 多设备同时访问时的带宽分配WebSocket 服务器如果同时有多个客户端连接每个客户端都要发一份数据带宽会成倍增加。我的做法是服务器端维护一个客户端列表每次收到新数据就遍历列表发送。在局域网内2-3 个客户端同时看没问题。如果客户端更多就要考虑降采样或者只发关键数据。实测在手机和电脑同时连接的情况下ESP32-CYD 的 CPU 占用会到 70% 左右波形刷新会稍微变慢。如果只是演示用建议只连一个客户端。5. 整条链路的联调与常见故障排查5.1 从串口到屏幕的逐段验证方法联调时不要一上来就全链路跑通要分段验证。我的顺序是先用 USB 转串口工具直接接脑电模块用串口助手看数据是否正常。这一步能确认脑电模块本身工作正常。把脑电模块接到 BW16用手机 BLE 调试助手连接 BW16看是否能收到数据。这一步验证 BW16 透传正常。ESP32-CYD 单独跑 BLE 客户端串口打印收到的数据。这一步验证 BLE 接收正常。加上屏幕显示看波形是否正常。最后加上 WebSocket 和网页。每一步都确认无误后再进行下一步这样出问题时能快速定位是哪一段的问题。5.2 数据错位与校验失败的典型原因数据错位最常见的原因是包头识别错误。如果脑电模块输出的数据里恰好出现了 0xAA 0x55解析就会错位。我的解决办法是解析时如果校验和不对就丢弃当前帧从下一个字节重新找包头。另外 UART 波特率偏差也会导致错位建议用示波器或者逻辑分析仪确认波特率准确。还有一个坑是 BLE 的 MTU 限制。如果一次 Notify 的数据超过 MTU默认 23 字节会被分片。ESP32 的 BLE 库一般会自动重组但如果你自己处理原始数据要注意这一点。我建议在 BW16 端就把数据切成小于 20 字节的包发送。5.3 屏幕闪烁与 WiFi 干扰的抑制屏幕闪烁通常是因为刷新时清屏和绘制不同步。用双缓冲可以解决。另外 WiFi 和屏幕 SPI 如果共用电源WiFi 发射时的电流波动会导致屏幕闪烁。我在屏幕电源脚并了一个 100uF 电容闪烁明显减轻。BLE 和 WiFi 同时工作时2.4G 频段会互相干扰。BW16 是双频的可以设成 5G WiFi但 ESP32-CYD 只支持 2.4G。我的做法是让 BW16 用 BLEESP32-CYD 用 WiFi两者错开频段使用。实测在 BLE 连接间隔 15ms、WiFi 信标间隔 100ms 的情况下丢包率在 1% 以下。故障现象可能原因解决办法串口乱码波特率不匹配确认双方都是 115200BLE 频繁断连距离过远或干扰缩短距离改连接间隔屏幕闪烁电源波动加滤波电容网页卡顿客户端过多限制连接数数据错位包头误识别加校验和错位后重新同步6. 原型链路的扩展方向与个人实操体会6.1 从单通道到多通道的数据打包目前跑的是单通道如果你要扩展到 4 通道或 8 通道帧格式要改。我的建议是包头之后加一个通道号然后是该通道的采样值。或者用 TLV 格式Type-Length-Value这样扩展性更好。但要注意 BLE 的吞吐上限8 通道 250Hz 就是 8000 字节/秒接近 BLE 的舒适区上限了可能需要降采样或者用 WiFi 透传。6.2 加入简单滤波提升波形可读性原型阶段我直接在 ESP32 端加了一个 50Hz 陷波滤波用简单的二阶 IIR 实现。虽然会引入一点相位失真但工频干扰明显减少。如果你要做更严谨的分析建议把原始数据也通过 WebSocket 发出去在网页端或者上位机做离线处理。6.3 我在调试中总结的几条经验第一BW16 的固件一定要先确认版本不同版本的 AT 指令和默认 UUID 可能不同我在这上面浪费了大半天。第二ESP32-CYD 的屏幕刷新和 BLE 接收最好分到两个核心上Arduino 框架下可以用 xTaskCreatePinnedToCore 把 BLE 处理放到核心 0屏幕和网页放到核心 1这样卡顿会少很多。第三网页端的 Canvas 尺寸不要设太大800x400 足够了太大反而影响性能。第四脑电模块的参考电极一定要贴好接触不良会导致波形全是噪声这个问题我一开始以为是电路问题查了半天才发现是电极没贴牢。这条链路目前跑下来从脑电模块到网页的端到端延迟大概在 100ms 左右对于原型验证和演示完全够用。后续如果要做产品化需要考虑低功耗、数据加密和更严格的信号链设计但那是另一个话题了。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。