ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

无线脑电采集显示系统:基于BW16与ESP32-CYD的完整实现

2026/10/6 18:31:02 拓冰建站 浏览量
无线脑电采集显示系统:基于BW16与ESP32-CYD的完整实现 1. 先把整条链路想清楚最近手头有个脑电采集的小需求把干电极脑电模块的原始波形、注意力值和冥想度搬到屏幕上最好还能在手机浏览器里实时看。一开始我图省事想把脑电模块直接接到 ESP32-CYD 上屏幕、触摸、WiFi 全塞在一颗芯片里跑结果波形一刷就卡成 PPTTCP 缓冲还时不时溢出。后来我把采集和传输拆开BW16 专门读脑电模块并负责无线发送ESP32-CYD 只做接收、显示和网页推送这条无线 EEG 原型链路一下子清爽了很多。这篇博文就完整记录这套方案的思路、接线、代码关键片段和调试过程中踩过的坑。适合手里有 TGAM 这类单通道脑电模块、想绕开 USB 线做无线演示的朋友也适合想把 BW16 和 ESP32-CYD 这对低成本 WiFi 组合用起来的玩家。看完你至少能搭出一条“脑电模块 → BW16 → WiFi → ESP32-CYD → 屏 网页”的可复现链路。1.1 这条链路到底要解决什么脑电模块本身几乎都是 UART 输出最常见的是 NeuroSky 体系的 TGAM57600 波特每秒 512 个原始采样点同时给出 0-100 的 attention注意力和 meditation冥想度还有一个 0-200 的信号质量值。模块本体很小功耗也不高但线缆是最大的痛点。实验时人要在电脑旁边坐着姿势稍微一动线就松信号质量跟着崩。我最初的需求很直接把脑电数据无线化三米内不断流然后同时输出到两个地方——一块本地 TFT 屏幕一个手机浏览器页面。这样不管是自己调试还是给别人演示都不用拽着 USB 线了。所以这条链路的核心不是“解析脑电有多难”而是“怎么把 512Hz 的实时流稳定地从模块送到屏幕和网页”。1.2 为什么拆成 BW16 和 ESP32-CYD 两颗芯片第一版方案是“一颗 ESP32-CYD 全包”。ESP32-2432S028R 这块板子自带 2.8 寸 ILI9341 屏和 XPT2046 触摸看起来万事俱备。但实际跑起来问题很明显屏幕刷新本来就吃 CPUWiFi 协议栈也要占核再叠加 57600 波特串口中断读 TGAM 数据loop 里稍微慢一点串口 FIFO 就溢出波形出现锯齿状跳变。CYD 的 GPIO 也被屏幕和触摸占得差不多了剩下能用的引脚位置别扭飞线很难受。第二版想的是“ESP32-CYD 直连 WiFi 读一个串口透传模块”比如用 AT 固件的 ESP8266 当无线串口。这个方向对但 ESP8266 只有一个硬件 UART接 TGAM 没问题可它 2.4G 频段在演示现场经常被干扰。而且 AT 指令透传有隐性延迟小包数据被 Nagle 算法攒着实时性不好控制。最后选了 BW16。这颗模块基于 RTL8720DN双频 WiFi 加 BLE价格和 ESP8266 差不多但硬件 UART 有两个GPIO 也够用还能直接用 Ameba Arduino 框架写代码。于是分工变成BW16接 TGAM拆包解析连 WiFi把数据用 TCP 发给 ESP32-CYDESP32-CYD开 AP 和 TCP 服务端收数据画屏幕同时开 HTTP 和 WebSocket 服务把数据推给浏览器。这样两颗芯片各干各的显示再卡也不会影响采集端采集端掉线也不会把屏幕搞崩。原型阶段这种“采集与展示物理隔离”的思路能省下大量联调时间。1.3 拓扑与协议选择拓扑上我选了“ESP32-CYD 开 APBW16 当 STA 连进来”的方式而不是两台设备都连家里路由器。理由很实在嵌入式设备过企业 WiFi 认证页面非常痛苦校园网、场馆 WiFi 一般都有 Portal 认证BW16 这种小模块根本弹不出登录页。自己开一个 SSID 叫 EEG-Link 的 AP手机和 BW16 都连它现场演示完全不受外部网络影响。代价是手机连上后上不了外网但对演示场景来说完全可接受。传输协议用了 TCP JSON 分块行。为什么不直接用 UDP因为是原型调试TCP 的确认重传能让“丢数据”这件事至少在链路层可控排查问题时也方便用 Wireshark 抓包看行内容。为什么不一条一条发 512Hz 的原始点那样每秒 512 个 JSON 包包头开销太大WiFi 小包容易被攒包延迟。我采取的是“攒 64 个原始采样点打成一行 JSON 再发”每秒大约 8 行每行约 400 字节带宽只有 3KB/s 左右非常宽裕。{a:67,m:55,q:0,w:[12,-8,3,25,-17,42,...]}字段含义分别是 attention、meditation、信号质量 q0 为良好、原始波形数组 w。协议定得越简单后面网页端解析就越省事。2. 硬件准备与接线硬件层面的坑比想象中多。物料本身不贵但如果你照着我最初“一把线怼上去”的接法大概率会花一晚上查信号质量为什么一直 200。2.1 物料清单我实际用到的物料如下列出来方便你照着备货物料型号/说明数量备注脑电模块TGAM 风格单通道模块1串口 57600输出原始波和 eSense无线模块BW16RTL8720DN1双频 WiFi BLEAmeba Arduino 可编程带屏开发板ESP32-2432S028RCYD12.8 寸 ILI9341 屏XPT2046 触摸干电极前额电极 耳夹电极1 套佩戴接触质量直接影响信号质量锂电池/充电宝5V 输出移动电源1整套系统可脱离电脑供电3.3V LDOAMS1117-3.31给 TGAM 和 BW16 供干净电源杜邦线母对母、母对公若干若干接线用TGAM 这类模块市面上有两种一种带耳夹电极一种需要自己焊参考电极。我的经验是耳夹式最省事前额电极贴眉心上方约 2 厘米处耳夹夹耳垂地线自然就通了。别小看这一步模块的“信号质量”数值 200 多数时候不是代码问题而是电极没夹好。2.2 脑电模块与 BW16 的接线TGAM 模块引脚极简VCC、GND、TUART TX。部分板子还有 RUART RX但 TGAM 只发不收RX 可以不接。接线如下TGAM VCC → 3.3V必须干净电源TGAM GND → BW16 GND同时要和 ESP32-CYD 共地TGAM T → BW16 的 Serial1 RX 引脚注意三点TGAM 是 3.3V 逻辑千万别拿 5V 的 Arduino 板去接轻则数据全乱重则烧模块。三个板子必须共地。无线链路一开地线不共就会出现偶发乱码排查起来非常诡异。BW16 用硬件 UART 而不是 SoftwareSerial。我一开始偷懒用了软件串口57600 波特下中断抖动导致丢字节拆包成功率只有六成。换到硬件串口后拆包成功率基本 100%。电源上充电宝 5V 出来先过 AMS1117-3.3 再给 TGAM 和 BW16。ESP32-CYD 可以直接用充电宝的 5V 供电板上自带 USB 转串口和电源管理。不要图省事从 CYD 的 3.3V 引脚拉电给 TGAM 和 BW16 一起用CYD 板载 LDO 电流余量不大三个模块峰值叠加容易把电压拉垮导致屏幕闪、无线重连。2.3 ESP32-CYD 的确认与烧录前检查ESP32-CYD 官方没有统一命名市面上常见丝印是 ESP32-2432S028R。买回来先确认三件事芯片是 ESP32-WROOM-32 或 32E4MB Flash屏幕驱动是 ILI9341触摸是 XPT2046背光引脚是 GPIO21屏幕 CS 是 GPIO15DC 是 GPIO2RST 是 GPIO4SPI 引脚是 SCK18、MOSI23、MISO19。在 Arduino IDE 里选“ESP32 Dev Module”Flash 大小选 4MB分区方案用默认的 “4MB with spiffs” 或者最小分区都行。烧录前按住板上的 BOOT 键再插 USB或者用模块上丝印标好的烧录键组合进入下载模式。新版本 esptool 一般能自动复位进下载模式我第一次烧录失败就是因为手没按住 BOOT。TFT_eSPI 库需要在User_Setup.h里启用 CYD 对应的配置。库的新版本内置了一个 Cheap Yellow Display 的配置模板你只要取消注释对应行即可如果用老版本就手动写上上面那组引脚。这里最容易踩的坑是把触摸的 CSGPIO33和屏幕的 CSGPIO15搞混屏幕能亮但触摸没反应反之亦然。3. BW16 端拆包、组帧、发无线BW16 是整条链路的“采集端”。它做的事情不复杂从串口读 TGAM 帧解析出 attention、meditation、信号质量和原始波形数组每攒够 64 个原始点组一行 JSON通过 WiFi TCP 发给 ESP32-CYD。3.1 固件准备从 AT 到 Ameba Arduino买回来的 BW16 一般默认烧的是 AT 固件直接接别的 MCU 用串口发 AT 指令也能用。但我要在 BW16 上直接跑 TGAM 解析逻辑所以刷成了 Ameba Arduino 固件。在 Arduino IDE 里BW16 不会出现在默认板型列表需要手动添加 Realtek 的板管理器源。具体地址官方仓库 ambd_arduino 有写添加后搜索 RTL8720DN 安装板型即可。烧录时 BW16 板子要按它的烧录键组合进入下载模式具体按键位置看丝印一般是一个标着“RST”加一个标着“DL”的小按钮。固件刷好后开发体验和 ESP32 几乎一致WiFi.begin、WiFiClient、Serial1 都是现成的。需要注意 BW16 的Serial默认是调试日志口接了 USB 转串口才能看到 print 输出和 TGAM 通信要用Serial1。3.2 TGAM 数据包解析的完整逻辑TGAM 的帧格式本身不复杂帧头两个 0xAA接着一个长度字节 LEN然后是 LEN 个字节的 payload最后 1 字节校验。校验算法是 payload 所有字节求和取低 8 位再取反即(~sum) 0xFF。payload 里是“类型-数据”的键值对我实际用到的类型如下类型码含义数据长度0x02信号质量0-2000 最好1 字节0x04attention0-1001 字节0x05meditation0-1001 字节0x80原始波形有符号 16 位大端2 字节0x838 个频段的功率谱24 字节解析时用状态机比逐字节硬搜稳定得多。核心逻辑如下byte state 0; byte len 0, sum 0, chk 0; byte payload[128]; int pi 0; void parseTGAM(byte b) { switch (state) { case 0: if (b 0xAA) state 1; break; case 1: if (b 0xAA) state 2; else state 0; break; case 2: len b; pi 0; sum 0; state 3; break; case 3: sum b; if (pi len) payload[pi] b; if (pi len) state 4; break; case 4: chk b; if (chk ((~sum) 0xFF)) handlePayload(payload, len); state 0; break; } }handlePayload里就是遍历 payload 键值对void handlePayload(byte* p, int len) { int i 0; while (i len) { byte code p[i]; switch (code) { case 0x02: sig p[i]; break; case 0x04: att p[i]; break; case 0x05: med p[i]; break; case 0x80: raw (int16_t)((p[i] 8) | p[i1]); i 2; break; // 0x83 功率谱用于扩展功能详见正文 default: i codeLength(code); // 按文档跳过 break; } } }一个小经验TGAM 原始波形是 512Hz 的有符号 16 位值范围通常在 ±800 以内但信号质量差时会出现 ±1500 以上的尖峰。显示端映射到屏幕高度时一定要做限幅否则波形直接飞出去。0x83 的功率谱数据是 8 组 3 字节的浮点式编码首字节高 4 位是指数其余 20 位是尾数折算公式在 NeuroSky 官方文档里有。原型阶段我只关心注意力、冥想度和原始波频谱功能留着后面扩展。3.3 无线发送策略分块、去抖、反粘包发送端最蠢的写法是每收到一个 0x80 原始点就client.print一次。512Hz 下每秒 512 个 TCP 小包BW16 的协议栈会疯狂而且 Nagle 算法会把小包攒到一起延迟忽高忽低网页端画出来一卡一卡的。我采取的是“攒 64 点发一行”的策略void loop() { if (!client.connected()) { client.connect(serverIP, 9000); delay(1000); return; } while (Serial1.available()) { parseTGAM(Serial1.read()); if (rawReceived) { rawBuf[rawCnt] raw; if (rawCnt 64) { sendChunk(); rawCnt 0; } } } }sendChunk把 attention、meditation、信号质量和 64 个原始点拼成一行 JSON末尾带\n。TCP 是字节流接收端拿\n做行分隔就能天然拆包不用自己处理复杂的帧定界。这一点对后面 ESP32-CYD 和网页端都非常友好。另外两端都把client.noDelay(true)打开关掉 Nagle 算法端到端延迟能从几十毫秒降到个位数毫秒。对脑电这种对实时性敏感但带宽极低的数据流这个开关很值。4. ESP32-CYD 端接收、画屏、反向推送ESP32-CYD 这边承担三件事开 AP 让 BW16 和手机连进来、维护 TCP 服务端收 JSON 行、把同样的数据画到 TFT 上并通过 WebSocket 推给浏览器。4.1 建 AP、收数据、拆 JSONAP 部分代码很常规WiFi.mode(WIFI_AP); WiFi.softAP(EEG-Link, 12345678); IPAddress myIP WiFi.softAPIP(); // 192.168.4.1注意 ESP32 的 AP 只支持 2.4G频率固定 2.4GHz。BW16 支持双频但 STA 模式连 2.4G 的 AP 完全没问题。演示现场如果 2.4G 信道拥挤可以在softAP之前用esp_wifi_set_channel换信道或者直接在代码里指定WiFi.softAP(ssid, pass, channel)我一般选 6 或 11 信道避开水星满天飞的信道 1。TCP 服务端用WiFiServer server(9000)监听在 loop 里循环server.available()。因为 BW16 是连接发起方ESP32-CYD 这边只要维护一个客户端连接就行void loop() { WiFiClient c server.available(); if (c !c.connected()) c.stop(); if (c c.connected() !tcpPort) tcpPort c; // 只保留第一个 client while (tcpPort tcpPort.connected() tcpPort.available()) { String line tcpPort.readStringUntil(\n); if (line.length() 0) parseChunk(line); } // 其他任务 }解析 JSON 用 ArduinoJson 库60 个元素的数组反序列化很轻松DynamicJsonDocument doc(1024); deserializeJson(doc, line); int att doc[a] | 0; int med doc[m] | 0; int sig doc[q] | 200; JsonArray arr doc[w].asJsonArray();这里有个小坑TCP 断线重连后server.available()返回的是同一个WiFiClient对象还是新对象取决于你调不调stop()。我的经验是每次available()非空就先把旧的 TCP 引用清掉防止一个断开连接句柄反复触发读取导致readStringUntil卡死。4.2 TFT 波形绘制Sprite 只清整列屏幕是 240x320 竖屏我把波形区域放在上半部分 180 像素高下半部分放数值、柱状图和信号质量。横轴 320 像素。最初我用了全屏fillScreen 重绘刷新一忙就花屏。后来换成“Sprite 单列擦除”的思路建一个 320x180 的TFT_eSprite作为画布每来一个显示点先在当前 x 列画一条竖线擦掉旧波形再画新点只把这一列pushImage到屏幕而不是整幅图刷。TFT_eSprite spr TFT_eSprite(tft); spr.createSprite(320, 180); void drawWave(int16_t raw) { int y map(raw, -800, 800, 175, 5); y constrain(y, 5, 175); spr.drawFastVLine(x, 0, 180, TFT_BLACK); spr.drawPixel(x, y, (sig 0) ? TFT_GREEN : TFT_RED); spr.pushImage(x, 0, 1, 180, spr.getPointer()); // 实际用 pushSprite 局部推送 x; if (x 320) x 0; }这里有个细节TGAM 原始波是 512Hz屏幕只有 320 列直接每点画一列波形会跑到每秒刷 512 列视觉上太快看不清。我的做法是每 5 个原始点取一次峰峰值画一列显示刷新率约 102Hz看起来既不卡也不糊。如果想保留全部细节就把横向改成环形缓冲从头覆盖但演示场景下没有这个必要。下方数值区用tft.fillRoundRect画柱状图attention 和 meditation 各一个条信号质量用一个红黄绿的小圆点表示。这些 UI 不需要高频刷新值变了再更新对应矩形即可避免整屏重画造成闪烁。4.3 WebSocket 服务与数据推送屏幕和网页共用一个数据源parseChunk收到一行 JSON 后一方面转给屏幕缓存另一方面原封不动推给浏览器。这里我直接用了 Arduino WebSockets 库绑定 81 端口WebSocketsServer ws(81); ws.onEvent(webSocketEvent); ws.begin();webSocketEvent里处理客户端连接和断开日志。推送就在parseChunk末尾String out; serializeJson(doc, out); ws.broadcastTXT(out);浏览器连接ws://192.168.4.1:81/就能收到同样的 JSON 行。这样网页端不用关心 TCP 解析逻辑和屏幕端拿到的是同一条数据流联调时对图非常方便。还有一个隐藏好处ESP32-CYD 的 AP 模式下手机或电脑连上 EE-Link WiFi浏览器打开http://192.168.4.1加载页面WebSocket 连192.168.4.1:81完全不需要配置跨域因为页面和 ws 服务同源。这是自建 AP 拓扑最大的省心点。5. 网页端没有客户端也能看脑电网页端是我整套原型里最“加分”的部分因为它让任何人拿手机连上 EEG-Link 的 WiFi 就能看到脑电波形不需要装 App也不需要开无线调试。5.1 浏览器页面结构页面就是一个单 HTML 文件内联 CSS 和 JavaScript由 ESP32-CYD 的 80 端口以字符串形式提供。结构分三块一个canvas画波形两个数字展示注意力/冥想度一个信号质量指示灯。WebSocket 连接代码极简const ws new WebSocket(ws:// location.hostname :81/); ws.onmessage (e) { const d JSON.parse(e.data); appendSamples(d.w); setValue(att, d.a); setValue(med, d.m); setQuality(d.q); };浏览器收到一行 JSON 里带着 64 个原始点。默认方案是直接把 64 个点画到 canvas 上刷新率约 8 帧/秒够看趋势但不顺滑。想要丝滑显示的话可以加一个定时器按 2ms 间隔从数组里逐点播放网页端就模拟出了接近 512Hz 的视觉效果。5.2 Canvas 波形绘制与播放逻辑Canvas 绘制思路和 TFT 端一样用“擦旧列画新列”而不是整幅重绘const W 480, H 160; let cx 0; let lastY H / 2; function drawWave(v) { const y Math.max(4, Math.min(H - 4, H / 2 - v / 8)); ctx.fillStyle #1e1e1e; ctx.fillRect(cx, 0, 1, H); // 擦旧列 ctx.fillStyle q 0 ? #22ff88 : #ff4444; ctx.fillRect(cx - 1, lastY, 2, y - lastY); // 连线 lastY y; cx (cx 1) % W; ctx.fillStyle q 0 ? #22ff88 : #ff4444; ctx.fillRect(cx, y, 1, 1); }注意 Canvas 的fillRect在横轴方向画竖线时如果跨两列会产生明显的 2px 粗线所以我在连线时统一用(cx-1)到cx的 2px 宽度保证不会出现单列断线。这个细节是我对比后确定的单像素线在移动端浏览器上会因为设备像素比渲染发虚。数值面板我用了很基础的 DOM 更新不引框架页面体积控制在几 KB 以内ESP32-CYD 的内存压力很小。信号质量 q 非零时波形变红同时显示一行提醒“检查电极接触”这对现场演示很有用——观众一眼就知道是佩戴问题而不是设备坏了。网页还能顺手做一个 10 秒的注意力趋势图用 attention 数值画一个小柱状图。这一块不做实时推送浏览器端自己攒数据就行完全不影响主链路。6. 实际调试中踩过的坑这套链路我前后调了两周一半时间耗在排查“看起来像代码问题、实际上是硬件或协议细节”的怪毛病上。以下问题都真实发生过按出现频率排序。6.1 BW16 连不上 AP 或反复掉线BW16 连 ESP32-CYD 的 AP 失败最常见原因是 AP 信道和 BW16 的扫描机制冲突。ESP32softAP默认信道是 1而 BW16 在 STA 模式下扫描会优先找信号最强信道偶尔出现“看得到 SSID 但连不上”的情况。排查步骤先用手机连 EEG-Link确认 AP 本身正常在 BW16 代码里固定连接时打印WiFi.status()看是WL_DISCONNECTED还是WL_CONNECT_FAILED如果一直连不上在WiFi.begin前加WiFi.disconnect()避免残留上次的 AP 缓存换 AP 信道到 6 或 11重新试。掉线问题则多是供电引起。BW16 发射瞬间峰值电流可以到 300mA劣质 LDO 电压一抖就重连。我最后把 TGAM 和 BW16 分开供电TGAM 用一个独立 3.3V LDOBW16 用另一个地线保持共地掉线率明显下降。6.2 TGAM 串口数据乱码现象是解析出的 attention 值乱跳、原始波呈毛刺状用逻辑分析仪看反而一切正常。这类问题九成出在 UART 配置上要么实际波特率不是 57600要么用了软件串口。我的经验总结TGAM 模块上电后有约 2 秒的自检输出会输出一段非标准帧解析状态机如果没做好同步会被带偏建议上电后等 3 秒再开始解析不要用 SoftwareSerial 跑 57600中断抖动在 WiFi 发射时会加剧丢字节率直线上升校验失败时宁可丢帧也不要强行解析(~sum) 0xFF匹配再处理 payload如果信号质量一直 200先检查电极而不是代码。前额一定要清洁耳夹要夹在耳垂肉上而不是头发上。6.3 屏幕闪烁、刷新卡顿、偶发花屏CYD 的 ILI9341 用 SPI 驱动全屏刷新在 40MHz SPI 下理论也就 30fps 上下但你要同时跑 WiFi 和 WebSocket整屏重绘就扛不住了。我的解决方法是前面说的“Sprite 单列局部推送”。还有一个容易忽略的坑CYD 的背光引脚 GPIO21 如果不初始化屏幕可能亮度极低甚至黑屏。TFT_eSPI 初始化之后记得pinMode(21, OUTPUT); digitalWrite(21, HIGH);花屏则多半是 SPI 速率过高TFT_SPI_FREQUENCY默认 40MHz 在部分 CYD 批次上会不稳定降到 27MHz 或 20MHz 可以解决。波形显示对刷新率要求不高20MHz 足够。6.4 网页端 WebSocket 打不开常见现象浏览器能打开http://192.168.4.1但ws://192.168.4.1:81/一直连接失败。排查要点WebSocketsServer 的构造函数第二个参数是 URL 路径如果用WebSocketsServer(81, /)客户端就要连ws://ip:81/ESP32-CYD 的内存不足时WebSocketsServer 无法处理握手可以先注释掉 TFT 绘制任务看能否连上手机浏览器如果开着省电模式WebSocket 长连接可能被系统杀演示时建议把手机省电模式关掉如果页面是从其它 IP 加载的WebSocket 也会出现跨域问题。最稳妥就是走 CYD 自己 AP 的 IP页面和 ws 同源。6.5 供电与地线问题合集最后把供电踩过的坑总结一下充电宝直接给 CYD 供电没问题但 CYD 的 5V 引脚和 USB 是直通的插着 USB 调试时再给 BW16 供 3.3V容易出现地环路TGAM 对电源纹波敏感纹波大时原始波会出现 50Hz 工频干扰的痕迹。AMS1117 输入输出各加 10uF 和 0.1uF 电容效果立竿见影整套系统用充电宝供电现场演示能跑两三个小时。如果做长时间采集建议换 18650 电池组加稳压模块容量更可靠。7. 关于这套原型我想多说两句调试完这条链路后我最深的体会是脑电协议解析本身不难真正难的是让无线链路、屏幕刷新、网页推送这三个子系统在资源有限的嵌入式环境里和平共处。拆分 BW16 和 ESP32-CYD 之后每个模块的任务单一出问题定位也快——采集端有问题看 BW16 的串口日志传输有问题抓 ESP32 的 TCP 包显示问题只看 CYD 的绘制逻辑。最后再分享一个非常实用的小技巧把 ESP32-CYD 的 AP 名字固定为项目相关名称、密码固定写死在代码里演示前提前开好电。现场观众问“这是怎么做到的”时直接让他们掏出手机连 WiFi 打开浏览器十秒钟就能看到自己的注意力数值在页面跳动比任何 PPT 都更有说服力。后续如果要做多通道、想离线分析频段功率你可以把这份 JSON 行的协议平移到更多采集节点或者把 0x83 功率谱完整解析出来在网页端画实时频谱。源定位、最小范数估计这类分析通常需要多通道数据单通道能做的是波段功率和注意力训练反馈。这套链路的价值在于把“数据怎么从电极走到屏幕和网页”这个问题彻底打通了之后再往上堆任何应用都不需要回头改传输层。