前阵子朋友问我:能不能用蓝牙把手机里的内容发到一块小屏幕上,做个像模像样的“电子留言板”?我想了想,这不就是一块 Arduino、一个蓝牙模块、一块 LCD 屏的组合嘛。接线图网上随便搜都有一大堆,但真拿到手就会发现:配对不上、乱码、显示错位、供电不足重启……一堆问题等着你。索性把这次从零搭到跑通的完整过程写出来,顺便把那些没人告诉你的坑也一并填了。这篇主要解决三个问题:怎么把蓝牙收发的数据正确接到 Arduino 上、怎么解析出一行干净可读的内容、怎么让 16 列 I2C 屏优雅地把长内容展示出来。无论你是第一次摸 Arduino 的纯新手,还是想拿这套组合做个快速原型的老手,都可以直接对着下面的步骤抄作业。
1. 项目到底在解决什么问题:手机到屏幕的“最后一跳”
先把这个项目的定位说清楚。它本质上是一个低成本无线数据可视化终端:手机(或其他蓝牙设备)作为数据源,通过无线链路把文本信息发送出去,Arduino 作为接收端把数据解析出来,再驱动 LCD 屏完成显示。听起来简单,但拆开来看,这里面其实有四个独立的环节,每个环节都可能成为整个链路的“断点”:
- 发射端:手机上的蓝牙调试助手、自写的 APP 或者其他蓝牙透传设备,负责把文本字符串按字节发送出去。
- 传输链路:蓝牙模块(HC-05/HC-06/JDY-31 等)在中间做的是“无线转串口”的工作,也就是所谓的透传。手机发来的数据,模块从蓝牙协议栈里解出来,然后从 TX 引脚按串口协议一位一位地送出去。
- MCU 接收端:Arduino 通过串口(硬件串口或 SoftwareSerial 模拟串口)读取这些字节,按预定的协议帧把它们拼装成完整的消息。
- 显示端:LCD 屏接收 MCU 的控制指令,把消息渲染在人眼可见的 16×2 或 20×4 字符点阵上。
我之所以要专门把这“最后一跳”拎出来说,是因为很多人在这个项目上栽跟头,并不是某个环节的技术有多难,而是没搞清楚数据在这条路径上是什么形态。手机发出去的是字符串,但蓝牙模块解出来的是一串不保证边界的数据流——它不知道这一帧消息到哪里结束,下一帧从哪里开始。如果你只是简单地在 Arduino 的 loop 里读串口、读到啥就显示啥,那至少会遇到三类问题:
- 半包问题:一帧消息超过几十字节时,蓝牙的底层传输会把数据拆成多个包,Arduino 一次 read 只能拿到一部分,直接显示就会看到半行字。
- 粘包问题:两条消息连续发送时,由于没有帧间隔,Arduino 可能把它们当成一条长消息处理。
- 脏数据问题:模块上电瞬间、AT 命令回显、调试信息等都可能混入数据流。
所以,这个项目的核心难点不在于“把线接对”,而在于给串口数据定义一套可靠的帧格式,让接收端能准确切分出每条完整消息。下面的内容就围绕这个主线展开。
2. 硬件选型的一个关键分岔路:HC-05 还是 HC-06,LCD 选 I2C 版还是并口版
2.1 蓝牙模块不同型号的“性格”
淘宝上最常见的蓝牙透传模块有两种:HC-05 和 HC-06。另外近年 JDY-31 和 HM-10(BLE 4.0)也逐渐流行。放在这个项目里,我认为选择的核心依据是:你需不需要配置主从角色和 AT 指令深度定制。
- HC-05:主从一体模块,可做主、可做从,支持比较完整的 AT 命令集。这意味着如果你希望“开发板主动去找手机连接”,而不是等手机来连它,就必须选 HC-05。它的缺点也很明显:模块体积稍大,默认波特率是 9600,初次上电时状态引脚的电平处理容易让人迷惑。
- HC-06:纯从机模块,出厂固件只允许手机主动连接它。优点是稳定、便宜、配置简单;缺点是你无法让 Arduino 主动发起连接,也只能通过 AT 指令调整波特率和配对码,改不了角色。
- HM-10:BLE 4.0 低功耗模块,用 Nordic 的芯片。如果你的项目将来要跑在纽扣电池上,或者需要和微信小程序、iOS 的 CoreBluetooth 打交道,HM-10 是更现代的选择。但它传输的不是标准串口透传那么简单,需要按 BLE 的 GATT 特征值写数据,对新手并不友好。
- JDY-31:这年头非常流行的低成本替代品,同样走 AT 指令,工作在经典蓝牙 SPP 协议上,透传体验和 HC-05 几乎一致。它的优势之一是用 USB 转 TTL 模块直接连电脑就能配置,不需要像 HC-05 那样额外接 enter 按键进入 AT 模式。
就“Arduino + 蓝牙 + LCD 显示手机信息”这个需求而言,我建议新手直接选HC-06 或 JDY-31,原因很简单:你只需要“等手机过来连”,不需要“板子去找手机”。这种情况下主从一体纯粹是浪费。我更倾向于 JDY-31,因为它的 AT 配置体验比 HC-06 顺畅,而且透传模式下丢包率在实测中更低。
2.2 如果 LCD 带 I2C 适配器,你的烦恼能少一半
LCD 屏采取两个主流方案:原生并口的LCD1602(16 字符×2 行),以及带有PCF8574/I2C 转接板的版本。我的建议非常明确:能选 I2C 就绝不选并口。原因如下:
- 并口版本至少需要 6 个 GPIO(RS、E、D4-D7),如果再加上蓝牙模块的 RX/TX,Arduino Uno 的 IO 会被占掉一大半,后续想加 LED、蜂鸣器、按键都得抠引脚。
- 并口连线多,接触不良的概率呈指数上升。杜邦线松一根,显示就出现乱码,排查起来极其痛苦。
- I2C 版本只占用 SDA(A4)、SCL(A5)两根线加电源,LCD 本身自带 PCF8574 芯片,MCU 端只需要调用
LiquidCrystal_I2C库就能完成所有操作。
唯一需要留神的是:I2C 地址。
绝大多数 PCF8574 模块的地址是0x27,但也有部分是0x3F。第一次接上之后如果屏幕什么都没显示,用 I2C 扫描程序跑一遍就能确认,不要直接怀疑屏坏了。
如果你手头只有并口屏,也不是不行,后面我会给一套并口版本的接线和代码。只是从“少踩坑”的角度,I2C 版实在是太省心了。
3. 实际接线与电平匹配:99% 的新手都会忽略 RX/TX 交叉和 3.3V 逻辑电平
接线是这个项目最容易出错、但又是很多人最不爱细看的部分。先说最核心的两个原则:
原则一:收发交叉。蓝牙模块的 TX 要接 Arduino 的 RX(接收端),蓝牙模块的 RX 要接 Arduino 的 TX(发送端)。平时模块上标着 TX/RX,很容易让人想当然地“TX 对 TX、RX 对 RX”直连——那叫环回,自己跟自己对上了,数据根本进不到 MCU。
原则二:留意电平区间。HC-05、HC-06 的串口逻辑电平是3.3V,不是 5V。Arduino Uno 的引脚是 5V 逻辑。如果你把 Arduino 的 TX(5V 高电平)直接怼到蓝牙模块的 RX(3.3V 逻辑),短期看不出问题,但长期会给模块内部的电平转换电路带来额外压力。稳妥方案是用一个 1kΩ + 2kΩ 的分压电阻把 Arduino TX 输出的 5V 降到 3.3V 左右再接模块 RX。模块的 TX 到 Arduino RX 通常可以直接连,因为 3.3V 的高电平已经能稳定被 Arduino 识别。
如果你用的是 JDY-31,它的串口逻辑电平往往是 3.3V 兼容多种输入,但同样不建议长期 5V 直灌。
下面是 I2C 液晶屏方案的完整接线表:
| 蓝牙模块引脚 | Arduino Uno 引脚 | 说明 |
|---|---|---|
| VCC | 5V | 多数蓝牙模块支持 5V 供电(内部带稳压) |
| GND | GND | 必须共地,否则串口电平无参考 |
| TX | RX(Pin 0)或 SoftwareSerial RX | 模块发数据给 Arduino |
| RX | TX(Pin 1)或 SoftwareSerial TX(经分压 3.3V 接入) | Arduino 可发 AT 指令、也可透传数据 |
| I2C LCD 引脚 | Arduino Uno 引脚 | 说明 |
|---|---|---|
| VCC | 5V | 背光和逻辑均用 5V |
| GND | GND | 与蓝牙模块共地 |
| SDA | A4 | I2C 数据线 |
| SCL | A5 | I2C 时钟线 |
有一种特殊情况需要注意:如果你用蓝牙模块的 TX 接到了 Arduino 的 Pin 0(硬件串口 RX),那么烧录程序时要先拔掉蓝牙的 TX 线。因为 Arduino IDE 烧录时也需要占用硬件串口,蓝牙模块如果还连在 RX 上,会把 AT 指令回显或者噪声混入引导加载过程,导致烧录失败,或者烧录成功了但程序莫名复位。这一点经常让人反复折腾很久,却不知道是接线惹的祸。
所以,更推荐的方式是:给蓝牙模块单独分配一对软串口引脚。比如:
- Arduino Pin 2 → 蓝牙模块 RX(经分压到 3.3V)
- Arduino Pin 3 → 蓝牙模块 TX
软件里用SoftwareSerial 或 AltSoftSerial把它们做成串口 2。这样做的好处是:烧录程序时完全不用动接线,硬件串口留给 USB 调试,两者互不干扰,排错时还能同时在串口监视器里看日志。缺点只有一个:SoftwareSerial 在高速率下不够稳定,但我们的项目只跑 9600 波特率,完全够用。
下面是一份可以直接抄的接线清单(软串口方案):
- 蓝牙 VCC 接到 Arduino 5V。
- 蓝牙 GND 接到 Arduino GND。
- 蓝牙 RX 串联一个 1kΩ 电阻后再接 Arduino Pin 2;在蓝牙 RX 端与 GND 之间并一个 2kΩ 电阻,形成分压。
- 蓝牙 TX 直接接 Arduino Pin 3。
- I2C LCD 的 VCC/GND 分别接 Arduino 5V/GND,SDA 接 A4,SCL 接 A5。
接完之后建议先用万用表量一下蓝牙 RX 引脚的电压(在 Arduino 上电状态下),通电后它应该约等于 0V(空闲态),发送数据时才会有 3.3V 左右的脉冲。如果量到 5V,说明分压有问题,立刻检查电阻连接。
4. 软件部分的核心:帧协议设计、串口读取与 LCD 渲染
4.1 帧格式:让数据有始有终
串口是一个字节一个字节流的,接收端需要知道到什么地方才算是“一条完整消息”。我在这类项目里习惯用非常朴素的帧头 + 帧尾方案:
- 帧头:
0xAA(也就是'ª'的字节码) - 帧尾:
0x55('U') - 有效载荷:UTF-8 编码的文本字节
手机端发送时,把字符串封装成AA + 文本内容 + 55的形式。Arduino 端不断在串口缓冲区里查找AA(帧头),一旦找到,就开始把后续字节写入临时数组,直到遇到55或长度上限为止。这样设计简单实用,几乎不会出现切分错乱,而且每一帧对应一条消息,天然规避了粘包问题。
为什么不直接用换行符\n当分隔符?因为换行符在很多蓝牙调试助手里会触发“发送新行”的行为,容易把数据帧切碎;而且 LCD 显示的多行文本里也可能会包含换行,用来做帧分隔不够干净。
4.2 核心代码:读取、拼包、渲染三步走
下面是一份基于 I2C 屏和软串口的完整例程,注释都放在关键位置。波特率使用9600,这是 HC-06 和 JDY-31 的默认值,也兼顾了软串口的稳定性:
#include <Wire.h> #include <LiquidCrystal_I2C.h> #include <SoftwareSerial.h> // 初始化 1602 屏,地址按实际扫描结果改 LiquidCrystal_I2C lcd(0x27, 16, 2); // 软串口:RX 用 Pin 3(蓝牙 TX 接这里),TX 用 Pin 2(蓝牙 RX 接这里) SoftwareSerial bluetooth(3, 2); #define FRAME_HEAD 0xAA #define FRAME_TAIL 0x55 #define MAX_MSG_LEN 80 // 接收状态机变量 boolean inFrame = false; char msgBuffer[MAX_MSG_LEN]; int msgLen = 0; void setup() { Serial.begin(9600); // 硬件串口:调试用 bluetooth.begin(9600); // 蓝牙串口 lcd.init(); // 初始化液晶 lcd.backlight(); // 打开背光 lcd.clear(); lcd.setCursor(0, 0); lcd.print("Bluetooth Ready"); } void loop() { while (bluetooth.available() > 0) { byte b = bluetooth.read(); // 状态机:寻找帧头 if (!inFrame) { if (b == FRAME_HEAD) { inFrame = true; msgLen = 0; } continue; } // 已收到帧头,开始收正文 if (b == FRAME_TAIL && inFrame) { // 收到帧尾,一帧结束,立即显示 msgBuffer[msgLen] = '\0'; displayMessage(msgBuffer); inFrame = false; continue; } // 保护:防止超长数据造成数组溢出 if (msgLen < MAX_MSG_LEN - 1) { msgBuffer[msgLen++] = (char)b; } else { // 帧太长,丢弃这一帧,重置状态 inFrame = false; } } } void displayMessage(char *msg) { lcd.clear(); int len = strlen(msg); int line1Len = len > 16 ? 16 : len; char line1[17]; memcpy(line1, msg, line1Len); line1[line1Len] = '\0'; lcd.setCursor(0, 0); lcd.print(line1); if (len > 16) { // 超出一行,显示第二段 int line2Len = len - 16 > 16 ? 16 : len - 16; char line2[17]; memcpy(line2, msg + 16, line2Len); line2[line2Len] = '\0'; lcd.setCursor(0, 1); lcd.print(line2); } else { // 不足一行的情况,第二行留空 lcd.setCursor(0, 1); lcd.print(""); } }这里面的状态机思想值得多说一句。它的作用是把“持续到来的字节流”切分成“一条条完整帧”。每来一个字节,都问自己两个问题:我现在在不在帧里?这个字节是不是结束符?这种设计比“攒够一个字节就立即显示”要稳得多,强烈建议你在所有串口类项目中都套用。
4.3 超过 16 个字符怎么办:滚动显示或分屏
1602 屏一行只有 16 列,两行最多显示 32 个字符。如果手机端发来一条 50 字的短信,上面这段代码会丢弃超过 32 的部分。更友好的做法是加一个轮播滚动逻辑:
- 第一次收到消息,先显示第 1 行前 16 个字符、第 2 行后 16 个字符。
- 按下按键或每隔 3 秒,整体上下/左右移位一次,显示下一段。
要实现滚动,基线思路是:在displayMessage里把完整消息存到一个全局字符串messageStore[128],再由一个UpdateDisplay()函数负责管理当前偏移量,每次从messageStore + offset取两个 16 字节的片段到 LCD。配合millis()做无阻塞延时,效果就像电子广告牌一样。
这段代码我放在进阶部分,先用上面的静态版本把通路跑通再说。
4.4 手机端到底发什么?串口调试助手的正确使用姿势
用手机发送数据时,多数人默认会用“经典蓝牙串口调试工具”这类 App。连接配对后新建连接,在输入框里写纯文本,然后点击发送。要注意:
- 不要勾选“自动添加换行”(或者只把
\n当普通字符发送),否则它会在你的帧尾 0x55 之外再塞一个 0x0A,程序会把这个 0x0A 当成正文字节存下来,显示时可能会在 32 字符处制造一个换行残影。 - 发送内容按我们约定的格式,例如:
AAHELLO WORLD55但注意:在 App 里输入的是 ASCII 字符 'A' 'A',不是十六进制字节 0xAA。如果你直接在输入框里打字AAHELLO WORLD55,Arduino 收到的第一个字节是0x41(字符 'A'),而不是0xAA。程序会一直等帧头等不到,什么都不显示。 - 正确做法有两种:
- 方法一:在 App 的十六进制发送模式里,输入
AA 48 45 4C 4C 4F 55(对应AAHELLO的十六进制码)。 - 方法二:改程序,把帧头定义为两个普通可见字符,比如
"##",这样手机端直接发##HELLO55就能被识别。这种字符型帧头对新手更友好,也更容易调试。
- 方法一:在 App 的十六进制发送模式里,输入
我实际项目里推荐用字符型帧头,因为不少蓝牙调试助手的十六进制模式界面很别扭,而且容易误操作。帧头和帧尾也可以改成<<和>>,只要收发两端一致就行。下面把代码里的帧头帧尾定义改一下即可:
#define FRAME_HEAD '[' #define FRAME_TAIL ']'手机端就直接发[HELLO WORLD],干净直观,收到后程序会把方括号之间的文字显示在屏上。这种思路也同样适用于更复杂的类 JSON 协议,只是在 Arduino 上解析 JSON 开销较大、不推荐。
5. 实测中最容易翻车的四个问题与完整排查链路
这一节是我最想让你认真看的部分。我把自己和周围朋友做这类项目时踩过的坑整理成清单,每个问题都给出从现象到根因再到方案的完整链路。
5.1 手机搜不到蓝牙模块
- 现象:手机蓝牙扫描列表里一直刷不出 HC-06 或 JDY-31。
- 排查链路:先确认模块有没有进入可被发现模式。HC-06 上电即进入可配对状态,指示灯一般快闪;HC-05 则需要先按模块上的小按键,或者通过 AT 指令把模块设为“允许配对”模式。如果指示灯是慢闪(约 2 秒一次),它很可能没在广播。
- 常见根因:VCC 接错了引脚。有些模块标的是 5V 输入、板上带 LDO,但有些廉价板子只有 3.3V 稳压管,5V 直接灌进去会发热甚至烧毁。先摸一下模块温度,很烫基本就是供电问题。
- 解决方案:换一个 3.3V 或 5V 供电试试(注意区分型号)。实测中 JDY-31 的默认广播名是
JDY-31,HC-06 通常是HC-06,HC-05 是HC-05,这些基本都是直接可见的。
5.2 连上了但 LCD 显示乱码 / 间隔性乱码
- 现象:手机发送简单字符串后,屏上出现“╪╫╪”“烫烫烫”或半个汉字乱码。
- 排查链路:先不经过 Arduino,直接把蓝牙模块 TX 接到 USB 转 TTL 模块的 RX,用电脑串口调试助手看原始字节。如果电脑端也乱码,问题在蓝牙模块的波特率与当前串口调试助手波特率不一致。如果电脑端正常,问题就在 Arduino 这端。
- 常见根因一:波特率不匹配。模块出厂默认 9600,但有人买到了被改成 38400 或 115200 的模块。ATM 指令把它改回来即可:连接模块(先把 RX 悬空或接低电平进入 AT 模式),发
AT+BAUD4(HC-05 表示 9600)或AT+BAUD=9600(JDY-31)。 - 常见根因二:软串口时序丢 bit。SoftwareSerial 在 9600 波特率下通常没事,但如果你的代码里在
displayMessage()里用了delay(),这期间蓝牙 RX 引脚上的数据就会积压,再被读取时容易错位。改法是把显示逻辑改为非阻塞,或者把蓝牙挪到硬件串口(Pin 0/1),把调试信息挪到软串口。 - 解决方案:规范帧协议 + 把显示改成定时任务,是根治乱码的最好组合。波特率统一 9600。
5.3 LCD 无显示,只有背光亮
- 现象:背光亮了,但屏幕上没有任何字符。
- 排查链路:先用 I2C 扫描代码确认地址。常见问题是默认
0x27不等于实际地址0x3F,代码里初始化的地址不对,屏幕就不会响应。 - 常见根因二:对比度电位器没拧对。虽然 I2C 版的对比度由背后电位器控制,出厂设置一般能看到字,但如果你买到的模块电位器被拧到了极端位置,也会出现白屏或全黑方块。
- 解决方案:用万能 I2C 扫描程序打印设备地址,再把地址填进
LiquidCrystal_I2C lcd(addr, 16, 2)。如果扫描到的是 0x3F 而你写的是 0x27,那当然什么都显示不了。
5.4 Arduino 烧录程序时反复失败
- 现象:点上传之后 IDE 一直报“avrdude: stk500_recv(): programmer is not responding”。
- 排查链路:如果蓝牙模块的 TX 连在 Pin 0(硬件 RX)上,先拔掉这根线再烧录。如果还是失败,断掉所有外部供电,只保留 USB 线,看看能否上传成功。
- 常见根因:外部 5V 电源和 USB 同时给 Arduino 供电,形成了压差竞争,导致复位不稳定。
- 解决方案:养成习惯——烧录之前把所有接在 RX/TX 上的外设断开,烧完再插回去。或者干脆用软串口方案,硬件串口留给烧录与调试。
6. 进阶玩法:从“显示一条消息”到“带时间戳的滚动留言板”
基础通路跑通后,这个项目的潜力其实比想象中大不少。下面展开三个我认为最实用、也最容易扩展的方向。
6.1 加入 DS3231 实时时钟,显示消息接收时间
如果你只是想显示一条手机发来的文本,不需要时钟。但如果要做留言板或通知屏,接收时间是很重要的信息。用 DS3231 模块可以做到:收到每条消息后,把YYYY-MM-DD HH:MM写到第一行,把消息正文滚动显示在第二行。DS3231 走 I2C 总线,和 LCD 一起用完全不冲突。接线只需再并一组VCC/GND/SDA/SCL,代码里用RTClib库读时间即可。
实现时要注意:DS3231 的 I2C 地址是0x68,和 PCF8574 的0x27不冲突,所以可以直接挂同一条总线。收到帧后先不急着清屏,而是读取当前时间、拼成一行,再把消息正文放入滚动区。
6.2 自制手机 App:用 BLE 或经典蓝牙发送自定义协议
如果你不满足于调试助手,想自己写个小 App 来控制显示,路径上有两个分岔:
- 经典蓝牙 SPP:Android 的
BluetoothSocket写起来不算复杂,用UUID 00001101-0000-1000-8000-00805F9B34FB就能建立串口通道。这种方式和 HC-05、HC-06、JDY-31 完全兼容。iOS 对经典蓝牙 SPP 支持很差,基本只能走 MFi 认证。 - BLE 4.0:用 HM-10 模块 + iOS 原生
CoreBluetooth或 Android 的BluetoothGatt。只需写自定义 Service 和 Characteristic,手机端把字符串写入 Write Characteristic,模块解包后从串口吐出来。优点是低功耗、iOS 原生支持,缺点是调试链路长一些。
做 App 的时候,最容易被忽略的是数据分帧:手机端发送时,也最好按[消息内容]的格式封装一帧再写入 socket,避免半包、粘包。不要把“分帧”的责任全抛给 Arduino。
6.3 多屏联动与远程消息中转
更进一步,可以把这套显示终端接到一个本地网关(树莓派或 PC)上,网关通过 Wi-Fi 接收云端消息,再通过蓝牙下发到 Arduino。这样你就拥有了一个真正意义上的“IoT 留言板”:手机在任何地方发一条消息到云端,屋里的 LCD 就自动显示。
网关端只需要几行 Python:
import bluetooth import time sock = bluetooth.BluetoothSocket(bluetooth.RFCOMM) sock.connect(("XX:XX:XX:XX:XX:XX", 1)) # 换成 HC-06 的 MAC while True: msg = input("> ") framed = "[" + msg + "]" sock.send(framed.encode()) time.sleep(0.5) sock.close()这是一个很有代表性的“链路打通”思路:无线感知层(蓝牙) + 计算层(Arduino) + 展示层(LCD)各司其职。以后你想做一个“门铃联动屏”或“空气质量通知屏”,都是在这个骨架上替换传感器和显示内容而已。
7. 我个人踩过几次坑之后的体会
这套组合做下来,我最深的感受是:串口项目的难点从来不在“接线”和“API 调用”,而在“数据边界”。你把帧协议定义清楚、把状态机写好、把共地接牢,大概率一两小时内就能跑通。反过来,如果一上来就去查“为什么乱码”,很容易陷入盲人摸象——波特率、电平、供电、干扰都可能产生类似的现象。
这里再留一个小技巧:调试期间,把 Arduino 的硬件串口接上 USB 线,在 IDE 的串口监视器里开 9600,然后在displayMessage()里同时把消息打印到硬件串口。这样你在电脑上能看到 Arduino 到底收到了什么、解析出了什么,与 LCD 上的内容一对照,问题出在传输层还是显示层,立刻一目了然。这比你在 LCD 上瞎猜要高效得多。
如果你已经按上面的方法跑通了,下一步建议去改一版滚动显示,把四条消息存进数组里,用按键换页。等到你把这些都玩熟,再回头看这个“Arduino + 蓝牙 + LCD”组合,你会发现它其实就是一个微缩版的物联网显示终端,任何数据源都可以往这个接收端里塞。到那时候,你需要的可能就不是一块 1602 屏,而是一块 OLED、一个触摸屏,甚至一个网页仪表盘了。