从串口到手机App:BLE数传全链路实战指南与避坑记录
2026/9/14 15:22:19 网站建设 项目流程

大家在嵌入式开发里摸爬滚打,大概率都遇到过这种场景:板子上的传感器数据已经能从串口正常打印了,但数据只能在电脑前看,人一离开工位,什么都干不了。做产品原型、做现场调试、做设备监测,天天被“串口线不够长”这事卡脖子。后来我换了条思路,用BLE数传把串口数据接到手机App上,一端是单片机串口,一端是手机屏幕,中间走低功耗蓝牙,链路打通之后,板子放哪都能看数据,现场调参也不用再抱着一台笔记本到处跑了。

这篇博文就完整拆一遍“串口到手机App”的BLE数传全链路,从硬件选型、串口侧数据采集,到BLE模块配置、手机端接收和展示,每步都讲清楚“为什么这么做”,穿插一些我自己踩过的坑和调试技巧。不管你是刚接触BLE的新手,还是已经在用ESP32、STM32做项目的开发者,按这条链路走一遍,都能做出一条自己的无线数传通道。

1. 完整链路设计与方案选型

1.1 先看清这条链路里到底有哪几环

所谓“BLE数传”,剥掉各种术语,本质就是一条数据搬运流水线:传感器数据先由MCU采集,通过UART串口交给BLE从机模块,BLE从机把数据打包成广播包或GATT特征值,手机App作为BLE主机扫描、连接、读取并最终显示在屏幕上。

这条链路里,数据形态经历了三次转换:第一段是MCU串口输出的原始字节流,第二段是BLE协议栈封装后的无线数据包,第三段是手机App解析后的结构化数值。每一段都有自己的约束和坑,比如串口侧要处理帧格式、波特率和粘包问题,BLE侧要处理MTU大小、连接间隔和通知机制,App侧则要先过权限、扫描、配对、服务发现这几关。

我见过不少做BLE的新手,第一反应是“蓝牙不就是无线串口吗”,直接把串口发数据的代码套进BLE里,结果要么数据长度超了被截断,要么手机收不到完整帧,要么好不容易连上却几秒就断开。根本原因就是没把这条链路拆开看,每一段的传输特性完全不同。

1.2 方案选型:外挂透传模块 vs 集成SoC

搭建BLE数传链路,硬件上有两种主流路线:一种是用MCU(STM32、Arduino等)外挂一个BLE透传模块,比如JDY-23、HM-10、CC2541模块,MCU只当数据源,BLE模块内部自带协议栈和透传固件,串口进什么就无线出什么;另一种是直接用带BLE的SoC,比如ESP32、nRF52832,这种方案MCU和BLE在同一颗芯片里,开发自由度更高,但代码复杂度也上来了。

在实际项目里怎么选,主要看两个维度:一是你的数据源是不是已经固定在某个MCU上了,二是你对功耗和体积的敏感度有多高。

如果现有产品已经用STM32在采数据,只差一个无线出口,用外挂BLE透传模块是最省事的路,我自己的做法是把模块当“无线串口线”用,MCU代码几乎不用大改,原来往串口写数据的函数直接照用。如果是从零开始做一款超低功耗的穿戴设备,那就得用nRF系列SoC自己控制协议栈,BLE从机连接间隔、广播间隔都能按需调,把平均功耗压到微安级别。

下表是我个人在选型时比较常用的对照,同样参数下,外挂模块胜在开发速度快,SoC方案胜在灵活和性能上限高:

对比项外挂BLE透传模块(JDY-23/HM-10)集成SoC(ESP32/nRF52832)
开发周期当天就能透传至少一到两周
对MCU代码改动基本不动,串口函数照用要把数据管理逻辑重构到协议栈里
功耗控制模块固定功耗,可调空间小可精细控制连接间隔、睡眠策略
调试难度低,AT指令即配即用高,需要熟悉SDK和协议栈
适用场景现有产品加无线能力、快速原型穿戴设备、低功耗产品、大批量生产

1.3 BLE和串口在逻辑上怎么对齐

把BLE当成“无线串口”用,有一个底层逻辑要理顺:串口是一条双向管道,发出去和收进来是并行的,而BLE数据是按GATT(Generic Attribute Profile)协议组织的,数据放在服务(Service)里的特征值(Characteristic)中,手机要先发现服务,再读写指定特征值,才能拿到数据。

所以我在做链路设计时,第一步就是在BLE模块上定义一个透明传输服务:一个Write特征值用来接收手机下发的指令,一个Notify特征值用来向手机主动推送数据。对应到MCU侧,串口发过来的数据写进Notify特征值,手机App写进Write特征值的内容再从串口发出去,这样双向通道就建起来了,逻辑上高度接近串口的工作方式。

另外一个关键参数是MTU(Maximum Transmission Unit),BLE 4.2之前默认MTU只有23字节,扣除协议头,实际单包最多传20字节;BLE 4.2之后可以协商到247字节。如果一次要传的串口数据超过20字节,必须在固件或模块层做分包,我在后面的章节里会详细讲怎么处理分包和重组。

2. 硬件准备与基础配置

2.1 一套最省心的硬件组合

我自己调试这套链路时,最常用也最推荐新手复刻的硬件组合是:一块STM32F103C8T6最小系统板加一个JDY-23 BLE从机模块,再加一片CP2102或CH340的USB转串口小板。整套下来成本不到三十块,但能覆盖串口采集、BLE透传、手机接收这一整条路径的所有调试需求。

接线方面,STM32的USART1_TX(PA9)接BLE模块的RXD,USART1_RX(PA10)接BLE模块的TXD,注意一定要共地,GND必须连在一起,否则电平参考点不一致,串口数据会乱成一片。JDY-23模块支持3.3V和5V供电,但串口电平是3.3V,STM32的PA9/PA10也是3.3V,所以可以直接对接,如果用5V的Arduino板子,就需要确认模块是否兼容5V电平,不兼容的话加个电平转换芯片更稳妥。

选USB转串口小板时,CH340和CP2102都行,区别是驱动方式不一样。CH340的Windows驱动偶尔会被杀毒软件拦截,CP2102相对省心,但价格略高。日常调试至少准备两块USB转串口:一块给STM32下载和打印日志,另一块专门接BLE模块的AT配置口,用来改模块参数,这样能避免频繁插拔线缆,少很多麻烦。

2.2 串口驱动的安装与验证

串口驱动这一关,看起来简单,实际卡住的人不在少数。CH340的驱动在Windows 10以上系统通常能自动识别,但如果你用的是精简版系统或者公司电脑有驱动限制策略,就得去芯片厂商官网下载对应版本的驱动,别用第三方驱动精灵之类的工具,容易装上全家桶。

驱动装好后,打开设备管理器,展开“端口(COM和LPT)”一栏,能看到一个COM口编号,把USB转串口小板插到不同USB口,COM口编号会变,代码里或者调试工具里选的串口号要跟着改,这个细节经常导致“我代码没变,怎么突然连不上了”的错觉。

验证驱动是否正常,最直接的方法是用串口调试助手做回环测试:把USB转串口板的TX和RX用杜邦线短接,打开串口调试助手,选对COM口和波特率,发送一串字符,如果能原样收到,说明驱动和串口通路都没问题。这一步看似多余,但能帮你排除“硬件坏了还是驱动没装好”的巨大不确定性,我每次换新板子都先过一遍。

2.3 BLE模块的AT指令配置与双人协作式调试

外挂BLE透传模块出厂时通常默认是透传模式,但波特率、设备名、广播间隔这些参数要先通过AT指令配置到项目需要的状态。以JDY-23为例,先把模块的EN引脚拉高或在上电前按住模块上的按键,让模块进入AT指令模式,然后在串口助手里发送以下几条核心指令:

AT+NAME=BLE_UART // 设置蓝牙从机名称,方便手机端识别 AT+UART=115200,0,0 // 设置串口波特率115200,无校验,1位停止位 AT+BAUD=115200 // 部分模块用这条指令设置波特率 AT+ADVI=100 // 设置广播间隔为100ms,兼顾连接速度和功耗 AT+MTU=200 // 尝试协商更大的MTU,提高单包数据量

每条指令发送后,模块会返回OK或者ERROR,一定要等返回结果再发下一条。我习惯把AT指令配置过程写成一份清单,每改一个参数就记录一次,模块配置这种事特别容易“改完就忘”,等过几天要复现问题时,完全想不起来模块里刷了什么参数,有个记录能省大量排查时间。

配置完成后,把模块断电重新上电,进入透传模式。这时候可以用手机上的nRF Connect或者LightBlue这类BLE调试工具扫描,确认能看到你设置的设备名,然后尝试连接,连接成功后手动向Notify特征值写入测试数据,如果能在手机端读出来,说明模块的BLE通道已经打通,链路还剩最后一公里——怎么让MCU的数据自动进入这条通道。

3. MCU固件编写:从串口到BLE的稳定搬运

3.1 用环形缓冲区解决串口粘包和丢包

MCU串口接收数据时最经典的问题是粘包和丢包。传感器或上位机发来的一帧数据可能被拆成好几次中断接收,也可能一次中断塞进来好几帧数据,如果每次中断直接处理,很容易出现半帧数据被当成完整帧解析的情况。

解决粘包的通用方案是引入环形缓冲区:串口中断只负责把收到的字节塞进缓冲区,主循环或定时器任务再按帧格式从缓冲区里取数据解析。这样数据接收和数据处理被解耦,中断里只做最轻量的操作,不会因为处理逻辑过长导致后续字节被硬件丢弃。

STM32上实现环形缓冲区,核心是一个数组加两个读写索引,写索引由串口中断更新,读索引由解析任务更新。当写索引追上读索引时说明缓冲区满了,这时候要按“丢新保旧”还是“丢旧保新”的策略处理,我在数据采集场景里一般选择丢旧保新,因为实时性优先,过期的数据意义不大。

#define BUF_SIZE 256 uint8_t rx_buf[BUF_SIZE]; volatile uint16_t head = 0; volatile uint16_t tail = 0; void USART1_IRQHandler(void) { if (USART_GetITStatus(USART1, USART_IT_RXNE)) { uint8_t data = USART_ReceiveData(USART1); uint16_t next = (head + 1) % BUF_SIZE; if (next != tail) { rx_buf[head] = data; head = next; } // next == tail 表示缓冲区满,直接丢数据 } }

这段代码里最关键的是(head + 1) % BUF_SIZE这个取模操作,它让数组在逻辑上变成了一个首尾相连的环。%运算在C语言里对嵌入式MCU有一定开销,所以我习惯把缓冲区大小设为2的幂,比如256,这时取模可以用& (BUF_SIZE - 1)代替,性能更好。

3.2 设计一套简单的数据帧协议

串口数据进了环形缓冲区后,下一步是把字节流切分成一帧一帧的完整数据。我常用的帧格式是:帧头(0xAA 0x55) + 数据长度(1字节) + 数据类型(1字节) + 数据区(N字节) + CRC校验(1字节) + 帧尾(0x0A)。

帧头用来找帧起始位置,避免把数据区里的字节误判成帧头;数据长度让你知道该收多少字节才算一帧;CRC校验用来过滤传输过程中被干扰的错帧。这套协议设计不复杂,但能应对绝大多数数传场景,比直接裸发数据可靠得多。

解析时,主循环每轮检查缓冲区里有没有完整的帧,先找帧头,再根据长度字段判断剩余数据是否够一帧,够的话就把整帧搬出来做校验和解析。这样做的好处是无论串口把数据切成几段发过来,最终都能正确拼出完整帧。

int parse_frame(uint8_t *src, uint16_t len, frame_t *frame) { for (uint16_t i = 0; i < len; i++) { if (src[i] == 0xAA && src[i+1] == 0x55) { // 找到帧头 uint8_t dlen = src[i+2]; if (i + 3 + dlen + 2 >= len) return -1; // 数据还没到齐 uint8_t crc = calc_crc(src + i + 3, dlen); if (crc != src[i + 3 + dlen]) return -2; // 校验失败 memcpy(frame->data, src + i + 3, dlen); frame->len = dlen; return i + 3 + dlen + 2; // 返回帧尾位置,外层从这里继续扫描 } } return -3; // 没找到完整帧 }

这个解析函数有返回值,外层调用时根据返回值决定把缓冲区里的哪些数据丢弃、哪些保留,避免把半包数据直接丢掉。实际调试中,我会把解析结果和原始数据同时通过另一路串口打印出来,方便对比协议解析是否正确。

3.3 怎么把串口数据喂给BLE模块

BLE模块在透传模式下,本质上就是一个无线串口管道。MCU串口收到并解析好的数据,只需要继续通过另一个串口发往BLE模块,模块就会自动把数据打包成BLE通知发出去。

我在代码里做了一层抽象,把“发送给手机”封装成一个函数,例如ble_send_packet(uint8_t *data, uint16_t len),内部实现就是往BLE模块所在的串口写入数据。如果数据超过MTU限制,就按MTU-3(预留GATT头)的长度拆成多包依次发送,每包之间加一点小延时,避免BLE模块内部缓冲区溢出。

还有一个容易被忽视的点:BLE从机模块向手机发送数据的能力和手机端的连接间隔有关。连接间隔是手机和模块之间协商的一个固定时间周期,默认可能是30毫秒或50毫秒,在正常情况下每个连接间隔最多能传6到8个数据包,如果MQTT或传感器数据上报频率太高,数据就会积压在模块内部的发送缓冲区里,造成延迟越来越大。解决思路是提高MTU、缩短连接间隔,或者在MCU侧做数据限流,保证平均数据率低于BLE通道的实际吞吐能力。

void ble_send_packet(uint8_t *data, uint16_t len) { const uint16_t mtu = 200; // 应与模块AT指令配置的MTU一致 const uint16_t chunk_len = mtu - 3; uint16_t offset = 0; while (offset < len) { uint16_t n = (len - offset > chunk_len) ? chunk_len : (len - offset); uart_write_bytes(BLE_UART, data + offset, n); offset += n; delay_ms(5); // 给BLE模块一点缓冲时间,防止内部FIFO溢出 } }

这段代码看起来简单,但delay_ms(5)这个细节是我在实际项目里反复调出来的。刚开始我不加延时,高速发送时模块偶尔丢包,加了小延时后稳定性明显提升。如果你的数据量特别大,可以考虑换用更高吞吐的BLE5模块,或者把MTU协商到最大化。延迟与吞吐之间的平衡,始终是BLE数传绕不开的核心问题。

4. 手机App端开发与调试

4.1 调试阶段先用现成工具验证链路

写手机App之前,强烈建议先用nRF Connect或LightBlue这类现成的BLE调试工具把整条链路验证一遍,别一上来就写代码。手机端BLE开发最大的痛点是权限多、流程复杂,如果链路本身没调通,代码写得再好也没用。

用nRF Connect验证时,按这几个关键步骤走:扫描到你的BLE设备,点击Connect建立连接,连接成功后看Service列表,找到你定义的透明传输服务(比如UUID为FFE0),点进去能看到特征值列表。Notify特征值上点Enable Notifications,然后让MCU定期通过串口向BLE模块发测试数据,如果手机端能看到数据实时刷新,说明BLE链路已经通了。

这一步验证的是“硬件和模块配置是否正确”,把问题范围缩小到手机端之后,再写App代码才是合理的开发顺序。我见过太多人在App里调试蓝牙连接失败,最后发现是模块根本没配好,白折腾一整天。

4.2 Android端开发的一个最小可跑流程

Android平台做BLE开发,从Android 6.0开始就需要动态申请定位权限,Android 12及以上又细分了“附近的设备”蓝牙权限,权限处理不对,App连扫描都扫不到设备。我写Android端BLE功能的通常顺序是:

第一步,申请权限。AndroidManifest.xml里声明BLUETOOTH_SCANBLUETOOTH_CONNECTACCESS_FINE_LOCATION权限,运行时用requestPermissions动态申请。Android 12以上,蓝牙权限属于“附近的设备”权限组,需要在系统设置里单独看是否授权。

第二步,初始化蓝牙适配器和扫描。用BluetoothLeScanner.startScan(callback)扫描设备,扫描结果回调里找到目标设备名后调用connectGatt建立连接。

第三步,发现服务并开启通知。连接成功后系统会回调onServicesDiscovered,在这里通过getService(uuid)拿到服务,getCharacteristic(uuid)拿到Notify特征值,然后调用setCharacteristicNotification(characteristic, true),同时把描述符CCCD设置为ENABLE_NOTIFICATION_VALUE,这样App才能收到模块主动推过来的数据。

BluetoothGattCallback gattCallback = new BluetoothGattCallback() { @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { if (newState == BluetoothProfile.STATE_CONNECTED) { gatt.discoverServices(); } } @Override public void onServicesDiscovered(BluetoothGatt gatt, int status) { BluetoothGattService service = gatt.getService(BLE_UUID_SERVICE); BluetoothGattCharacteristic notifyChar = service.getCharacteristic(BLE_UUID_NOTIFY); gatt.setCharacteristicNotification(notifyChar, true); BluetoothGattDescriptor descriptor = notifyChar.getDescriptor(CCCD_UUID); descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); gatt.writeDescriptor(descriptor); } @Override public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { byte[] data = characteristic.getValue(); // 在这里解析并更新UI } };

这段代码里,很多新手会漏掉写CCCD描述符这一步,结果就是连接和服务都正常,但设备发数据时App毫无反应。原因就在于Android系统默认不会自动使能通知,必须显式往描述符里写入1才有数据上来,这是个特别容易踩的坑。

4.3 iOS端和跨平台方案的取舍

iOS端核心是用CoreBluetooth框架,流程和Android相似,但权限处理简单很多,只需要在Info.plist里加NSBluetoothAlwaysUsageDescription描述。iOS不允许App随意扫描所有蓝牙设备,必须声明使用场景,审核时也会看这条描述是否合理。

如果你不想分别为Android和iOS写两套原生代码,用uni-app的BLE能力是一个不错的选择。uni-app封装了uni.openBluetoothAdapteruni.startBluetoothDevicesDiscoveryuni.createBLEConnectionuni.notifyBLECharacteristicValueChange这一系列跨平台接口,一套代码两端跑。

不过跨平台方案也有代价:蓝牙相关的API封装层级越多,出问题时越难定位是系统API的问题还是框架的问题。我个人的建议是:如果是产品原型或者内部工具,用uni-app能大幅提速;如果是正式商业产品,尤其涉及后台保活、频繁大数据量传输,还是老老实实写原生,稳定性和可调试性都更好。

5. 常见问题排查与避坑记录

5.1 串口调试时最让人上火的几类故障

先说说串口侧我遇到最多的问题。第一种是电脑识别不到USB转串口设备,打开设备管理器发现一个黄色感叹号。这种情况90%是驱动问题,重装对应芯片的官方驱动基本能解决,CH340就装CH340的,CP2102就装CP2102的,别混用。

第二种是串口助手能打开但收发无反应。先检查接线,重点看TX和RX有没有交叉连接(A设备的TX接B设备的RX),再看是否共地,还要确认波特率、数据位、停止位、校验位两端设置完全一致。我遇到过好多次就是因为开发板默认波特率是9600,而我代码里初始化成115200,两边对不上,数据全是乱码。

第三种是串口打印乱码,这通常是波特率不匹配,或者代码里系统时钟配错了导致波特率实际值和理论值偏差。STM32用外部8MHz晶振配置115200没问题,但如果你用的是内部HSI时钟,误差可能会大到无法通信,这种情况下建议把波特率降到9600试试。

我把自己这几年调试串口的排查顺序总结成一套口诀式清单,遇到问题照着走一遍,能省大量时间:

  • 先看设备管理器,确认COM口存在且驱动正常
  • 再用串口助手的回环测试(TX短接RX)确认硬件通路
  • 然后量一下电平,确认板子供电和TXD/RXD引脚电压正常
  • 最后核对参数,波特率、数据位、停止位、校验位必须两端一致
  • 查模块资料确认是否有EN引脚需要拉高才能进入配置模式

5.2 手机连上了但收不到数据,问题出在哪

手机能连接到BLE模块,说明广播、连接链路都没问题,但收不到数据,这个问题有80%的概率出在CCCD描述符上。Android端忘了写描述符,iOS端忘了用setNotifyValue,都会导致这个现象。所以排查顺序一定是先确认你已经在代码里使能了Notify,再用nRF Connect这类工具手动试试能不能收到数据。

如果调试工具能收数据,但自己的App收不到,优先检查服务UUID和特征值UUID是否和模块文档里的一致,很多ble透传模块默认UUID是FFE0/FFE1,但不同批次固件可能改成别的。用nRF Connect直接看模块广播出来的UUID,比对代码里写死的UUID,这是最快的方式。

另一个隐蔽的坑是Android后台限制。手机熄屏或者App切到后台后,系统可能会挂起BLE回调,导致数据不再刷新。这时候需要在App里申请一个前台服务,或者采用高频率唤醒机制来保持接收。最简单的方式是在调试阶段把屏幕保持常亮,先验证业务逻辑,再考虑后台保活的技术方案。

5.3 数据丢包、延迟过大,这类性能问题的调试思路

BLE数传的丢包和延迟问题,要从两端同时排查。模块侧,先看广播间隔和连接间隔是否设得太长,广播间隔太长会导致手机扫描到设备很慢,连接间隔太长会导致数据传输吞吐量低。我调试时习惯把连接间隔设在30毫秒到50毫秒之间,这是连接速度和功耗比较平衡的区间。

手机侧,Android系统的BLE栈在不同厂商的ROM上表现差异很大,同一套代码在小米手机上正常,换到某款定制ROM上就可能丢包。这种情况没什么通用的完美解法,我的经验是尽量在onCharacteristicChanged里只做轻量接收和数据入队操作,UI更新放到主线程通过Handler或协程处理,避免回调里做耗时逻辑导致BLE数据读取不及时。

还有一点是MTU协商。如果不协商MTU,默认20字节的单包上限会严重限制传输效率。建议在连接成功后主动发起MTU协商请求,Android用requestMtu(200),iOS用maximumWriteValueLength(for:)查询实际支持的长度。MTU提升之后,同样一次通知能传更多数据,有效降低丢包率。

5.4 串口烧写失败问题

串口烧写失败这个话题,几乎每一个玩STM32和ESP32的人都遇到过。用串口给STM32下载程序时,最常见的失败提示是“芯片超时无应答”或者“连接失败”,这类错误一般有四个原因:BOOT0引脚没有拉到高电平、串口号选错、下载器驱动异常、或者芯片已经处于读保护状态。

STM32的串口下载流程是:先拉高BOOT0,复位芯片进入系统存储器Bootloader模式,然后通过USART1的串口接收程序镜像。很多人烧写失败是因为没复位,或者BOOT0跳线帽没接对。我用的是带BOOT0按钮和复位按钮的最小系统板,操作顺序是:按住BOOT0,按一下复位,松开BOOT0,然后开始下载。

ESP32的串口烧写失败相对复杂一些,通常是因为下载时GPIO0必须保持低电平进入下载模式。ESP32开发板一般内置了自动下载电路,但如果用的是自己搭的模组,就得手动把GPIO0接地再上电。另外供电不稳也会导致烧写失败,USB线用了劣质线或者USB口供电不足,都会出现“连接失败”或“芯片超时”的报错,换一根短而粗的数据线往往就正常了。

5.5 我的调试辅助工具清单

最后分享一套我固定在用的调试工具组合,每次做BLE数传项目都靠它们,省掉大量无效排查时间:

硬件方面:逻辑分析仪是排查串口波形问题的利器,任何“代码看起来对但就是不通”的串口疑难杂症,接上逻辑分析仪看一眼波形就能定位。USB转串口板至少备两块,一块给MCU调试日志,一块给BLE模块AT配置。电压表用来查供电和电平问题。

软件方面:nRF Connect和LightBlue是手机端BLE调试标配,串口调试助手我常用sscom和MobaXterm,前者登录串口调试简单直接,后者用它的串口会话直接看日志。如果是用来抓手机和BLE模块之间的通信包,有预算可以上PCAP格式的BLE嗅探器,没有就用nRF Connect的记录功能也能做个大概分析。

写在最后的一点体会

这整套“串口到手机App”的BLE数传链路,开发过程中最大的体会其实不是某个具体技术有多难,而是每一层的坑都很隐蔽:串口侧是电平适配和帧格式,模块侧是MTU和连接参数,手机侧是权限和描述符,任何一环没对齐,整条链路就是不通的。按照“先硬件、再模块、再工具验证、最后写App代码”的顺序一步步推进,是最稳的一条路。

再分享一个小技巧:整套链路里我花最多时间排查的,往往是那些“看起来最简单”的环节,比如串口号变了、接线松了、模块默认波特率和你代码不一致,这类低级问题反而比协议栈还难发现。所以做BLE数传,建议从一开始就准备一张调试记录表,每次配置和测试都往里填数据,后面排查问题时能省下几小时的时间。链路打通之后再回头优化功耗、吞吐和稳定性,那就是另一个话题了,但底层的这套经验,到哪个项目都通用。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询