资讯动态

基于BLE的儿童电动牙刷数据上传方案:从芯片选型到App对接

发布时间:2026/10/13 3:19:21 来源:尧图企业网站定制
1. 从一支电动牙刷说起这个方案到底在解决什么问题小朋友刷牙这件事大概是每个家庭早晨和睡前都要上演的拉锯战。家长站在卫生间门口催孩子含着牙刷发呆两分钟能拖成十分钟刷没刷干净全靠猜。市面上主打“智能护牙”的电动牙刷不少但真正把“刷了多久、刷得对不对”这件事讲清楚的并不多。我最近完整跟了一个小项目核心目标很明确让电动牙刷把每次刷牙的时长数据通过蓝牙BLE上传到家长手机App里家长打开手机就能看到孩子今天刷了几次、每次刷了多久、有没有偷懒。这个方案听起来简单真做起来涉及的东西一点不少。它横跨了嵌入式固件开发、低功耗蓝牙协议栈、数据缓存与补传机制、手机端App的数据解析与展示还要兼顾儿童牙刷特有的使用场景——比如孩子可能刷到一半把牙刷放下、可能一天刷好几次、可能牙刷没电了数据还没传出去。所以这不是一个“加个蓝牙模块就完事”的活儿而是一套从芯片选型到数据闭环的完整方案。我写这篇东西是想把整个方案的设计思路、关键细节、实操踩过的坑尽可能完整地摊开来讲。适合谁看如果你正在做智能个护类硬件尤其是带BLE通信的消费电子或者你是做儿童健康管理类App的开发者需要对接硬件数据再或者你单纯对“一颗几块钱的蓝牙芯片怎么把刷牙数据传出去”这件事好奇这篇都能给你一些可直接参考的东西。全文会围绕芯片方案、BLE通信、数据缓存、App对接、功耗控制这几个核心点展开尽量说人话把每个选择背后的“为什么”讲透。2. 整体方案设计与芯片选型思路2.1 为什么是BLE而不是其他无线方案先聊最上层的选择为什么用蓝牙BLE而不是WiFi、2.4G私有协议或者NFC。这个决定其实在项目一开始就定了理由很实在。电动牙刷是电池供电的小型个护设备体积小、电池容量有限通常一颗锂电池也就几百毫安时要撑一两个月。WiFi的功耗对这类设备来说完全不可接受待机电流都是毫安级几天就没电了。2.4G私有协议虽然功耗低但需要额外的接收端比如一个插在手机上的dongle或者一个网关家长用起来多一道门槛不符合“打开App就能看”的体验目标。NFC更不行它要求手机贴上去才能通信数据没法自动同步。BLE的优势在这里非常突出手机原生支持、功耗极低、连接建立快、成本可控。一颗支持BLE的SoC芯片批量采购价可以压到几块钱人民币而且待机电流能做到微安级。家长只要打开App靠近牙刷就能自动连接同步数据这个体验是最顺的。所以方案定调就是牙刷端做BLE外设Peripheral手机App做中心设备Central刷牙结束后牙刷主动广播App连接后读取数据。2.2 芯片选型的几个硬指标选芯片这件事我踩过坑也对比过好几家。核心指标其实就几个BLE协议栈是否成熟、Flash和RAM够不够、功耗表现、开发工具链是否友好、供货是否稳定。先说协议栈。BLE协议栈的稳定性直接决定了连接成功率和数据可靠性。有些便宜芯片的协议栈是半成品连接偶尔失败、断连后重连慢、配对信息丢失这些问题在实验室里可能看不出来一到用户手里就是投诉。所以选型时我优先看那些协议栈经过大量出货验证的方案哪怕贵几毛钱也值。Flash和RAM这块很多人会低估。刷牙数据看起来简单就是时间戳加时长但你要考虑本地缓存。孩子可能一周都不打开App数据得存在牙刷里等下次连接再补传。假设一天刷两次一次存一条记录一条记录按时间戳4字节时长2字节状态位1字节算大概7字节一周也就100字节左右。但实际实现里还要存固件配置、设备信息、OTA相关数据所以Flash至少留64KB以上比较稳妥RAM建议8KB起步16KB更从容。功耗方面重点看两个场景广播时的平均电流和连接时的峰值电流。广播间隔可以设长一点比如1秒一次这样平均电流能压到几十微安。连接时峰值电流通常在几毫安到十几毫安只要连接时间短几秒内完成同步对整体续航影响很小。开发工具链也很关键。有些芯片用私有IDE调试起来很痛苦有些支持标准的Keil、IAR或者GCC还有完善的SDK和例程能省大量时间。我个人的偏好是选那种SDK文档齐全、有现成BLE例程、社区活跃的方案遇到问题能快速找到答案。2.3 整体数据流设计方案的整体数据流是这样的刷牙开始时主控芯片记录起始时间戳刷牙结束时计算时长把这条记录写入本地Flash缓存然后触发BLE广播等待App连接App连接后按约定协议读取缓存中的所有未同步记录读取成功后牙刷端标记这些记录已同步可以覆盖或清除App端把数据存到本地数据库展示给家长。这个流程里有个关键设计点数据是“牙刷主动推”还是“App主动拉”。我们最终选的是App主动拉因为BLE外设牙刷在连接后作为服务端App作为客户端去读特征值这个模型更符合BLE的标准交互方式也更省电——牙刷不需要一直等着推送连上后被动响应就行。还有一个细节刷牙结束后多久开始广播。如果刷完立刻广播可能家长手机不在旁边广播一会儿没连上就停了数据就传不出去。我们的做法是刷牙结束后先广播一段时间比如30秒如果没连上就停止等下一次刷牙或者下次唤醒时再广播。同时牙刷上可以加一个物理按键或者通过特定动作比如放回充电座触发一次强制同步广播给用户一个“手动同步”的入口。3. 核心细节解析与实操要点3.1 刷牙时长的判定逻辑“刷牙时长”这四个字听起来简单但怎么判定“开始刷”和“结束刷”直接决定了数据的准确性。我们试过几种方案最后落地的是一套组合判定。最基础的是压力/姿态传感器判定。牙刷里有一颗三轴加速度计当检测到牙刷从静止变为有规律的往复运动比如X轴和Y轴交替变化就认为刷牙开始了。结束的判定是连续一段时间比如5秒没有检测到有效运动就认为刷牙结束。这个方案的好处是不依赖用户按任何按键全自动。但这里有个坑孩子可能刷两下就停下来玩过一会儿又刷两下。如果按上面的逻辑会生成很多条很短的记录家长看到的就是“刷了8次每次10秒”体验很差。所以我们在固件里加了一个合并逻辑如果两次刷牙之间的间隔小于某个阈值比如60秒就把它们合并成一条记录时长累加。这样孩子中途停顿一下不会被拆成多条。另一个坑是误触发。牙刷放在杯子里被碰到、或者装在包里晃动都可能被误判为刷牙。我们的解决办法是结合姿态角只有当牙刷大致处于水平或特定角度范围内且运动模式符合刷牙特征时才判定为有效刷牙。这个阈值需要根据实际牙刷的形态做标定没有万能值。提示刷牙判定逻辑一定要在真实用户场景里测尤其是儿童的使用习惯和成人差别很大。我们早期在成人身上测得很准换成孩子就各种误判后来专门找了一组儿童用户做了两周的数据采集才调好。3.2 BLE服务与特征值的设计BLE通信的核心是GATT服务Service和特征值Characteristic。牙刷作为外设需要定义自己的服务App作为中心设备去发现和读写这些特征值。我们的设计如下服务/特征值UUID自定义属性用途刷牙数据服务0xFF01-主服务数据同步特征值0xFF02Notify/ReadApp读取刷牙记录同步确认特征值0xFF03WriteApp写入同步确认设备信息特征值0xFF04Read读取固件版本、电量等时间同步特征值0xFF05WriteApp下发当前时间这里重点说数据同步特征值。刷牙记录可能有多条一条记录7个字节如果一次Notify只能传20字节BLE默认MTU那一次最多传两条多。我们的做法是分包传输App先读一个“记录总数”特征值知道有多少条待同步然后逐条读取或者让牙刷端把多条记录打包成一个长包通过多次Notify发送。考虑到MTU协商后可以到247字节甚至更大实际实现里我们协商到MTU185一次能传20多条记录基本够用。时间同步这个特征值容易被忽略但很重要。牙刷本身没有RTC实时时钟或者RTC精度很差如果不在连接时同步一次手机的时间记录里的时间戳就是错的。我们的做法是App每次连接后先把当前手机时间写入时间同步特征值牙刷端收到后更新自己的时间基准后续记录都用这个基准来打时间戳。注意BLE的UUID不要用标准UUID要用自定义的128位UUID或者至少16位的自定义短UUID避免和标准服务冲突。另外特征值的权限要设对该只读的只读该可写的可写不然App端会报错。3.3 本地缓存与断点续传机制本地缓存是这个方案里最容易被低估的部分。很多方案假设“每次刷完牙手机就在旁边立刻同步”但现实是孩子刷牙时家长可能在忙别的手机不在身边或者牙刷在卫生间手机在客厅BLE连不上。所以牙刷必须能存住数据等有机会再传。我们的缓存设计是环形缓冲区。Flash里划出一块区域比如2KB按顺序写入记录。写满之后从头覆盖最老的记录。2KB能存大约290条记录按一天两次算能存将近5个月完全够用。每条记录除了时间戳和时长还有一个同步标志位初始为0App成功读取并确认后置为1。App读取时只读标志位为0的记录读完一批后写入确认牙刷端再把标志位置1。这里有个细节如果App读到一半断连了怎么办。比如读了5条第6条时断连那前5条已经读了但还没确认。我们的做法是App端做去重App本地数据库用“时间戳时长”作为唯一键重复的数据直接忽略。这样即使断连重读也不会产生重复记录。牙刷端则是在收到确认后才置标志位没确认的下次还会被读到保证不丢数据。提示环形缓冲区的写入要注意Flash的擦除特性。Flash写之前要先擦除而擦除是按扇区比如1KB或4KB进行的。如果每条记录都擦一次Flash寿命很快耗尽。我们的做法是攒够一个扇区的数据再统一擦写或者用“日志式”写入只在扇区写满后才擦除整个扇区。这个细节直接关系到牙刷的寿命不能马虎。3.4 功耗控制的关键手段电动牙刷的续航是用户非常敏感的指标。如果因为加了BLE导致续航从两个月变成两周这个方案就是失败的。所以功耗控制必须从设计之初就考虑而不是事后优化。广播策略是第一个优化点。BLE广播有三种模式可连接广播、不可连接广播、定向广播。我们用的是可连接广播但广播间隔设得比较长比如1秒一次。广播间隔越短被发现越快但功耗越高。1秒是一个平衡点家长打开App后通常几秒内就能连上同时平均电流能控制在几十微安。连接参数是第二个优化点。BLE连接建立后会协商一个连接间隔Connection Interval比如7.5ms到4s。间隔越短数据传输越快但功耗越高。我们的场景是“连上后快速传完数据就断开”所以连接间隔设短一点比如15ms或30ms让数据尽快传完然后主动断开。这样虽然连接时功耗高但连接时间短总体能耗反而低。睡眠策略是第三个优化点。牙刷在不刷牙、不广播的时候主控芯片应该进入深度睡眠模式只保留RTC和唤醒中断。深度睡眠电流可以做到几微安甚至更低。唤醒源可以是加速度计中断检测到运动、按键中断或者定时器。我们的做法是刷牙结束后广播30秒没连上就进入深度睡眠等加速度计检测到下一次运动再唤醒。注意深度睡眠唤醒后BLE协议栈需要重新初始化这个过程需要时间通常几十毫秒到几百毫秒。如果用户刷完牙立刻打开App可能牙刷还在初始化连不上。所以我们的策略是刷牙结束后先保持广播一段时间给App一个连接窗口而不是立刻睡眠。4. 实操过程与核心环节实现4.1 硬件搭建与固件框架硬件这边核心是一颗带BLE的SoC外加三轴加速度计、Flash、电池管理电路和电机驱动。SoC和加速度计通过I2C通信SoC和Flash通过SPI通信。电机驱动是独立的刷牙判定和电机控制互不干扰。固件框架我分成四层硬件抽象层HAL、驱动层、业务逻辑层、BLE协议层。HAL层封装GPIO、I2C、SPI、定时器等底层操作驱动层实现加速度计读取、Flash读写、电机控制业务逻辑层负责刷牙判定、数据缓存、功耗管理BLE协议层负责GATT服务定义、广播、连接管理、数据收发。这个分层的好处是可移植性强。如果换一颗芯片只需要改HAL层和BLE协议层的适配业务逻辑基本不用动。我们后来确实换过一次芯片因为供货问题得益于这个分层移植工作只花了几天。4.2 刷牙判定算法的实现细节刷牙判定算法的核心是一个状态机状态包括空闲、疑似刷牙、确认刷牙、结束判定。空闲状态下加速度计以低频率比如10Hz采样检测是否有超过阈值的运动。如果有进入疑似刷牙状态采样频率提高到50Hz开始分析运动模式。分析的内容包括运动幅度、运动频率、姿态角变化。如果连续2秒都符合刷牙特征进入确认刷牙状态记录起始时间戳。确认刷牙状态下持续监测运动。如果连续5秒没有有效运动进入结束判定。结束判定里再等2秒确认不是短暂停顿然后计算时长生成记录写入缓存触发BLE广播。这里的关键参数——运动幅度阈值、频率范围、姿态角范围、确认时间、结束时间——都需要根据实际牙刷的机械结构和用户习惯来标定。我们的做法是先用手工标定的方式定一组初始值然后找一批用户做数据采集用采集到的数据回放调整参数直到准确率和误判率都达标。提示刷牙判定算法不要追求100%准确那是不可能的。关键是漏判要少误判要可控。漏判就是用户刷了但没记录这个体验很差误判就是没刷却记录了这个家长可能发现不了但也不能太多。我们的目标是漏判率低于1%误判率低于5%。4.3 BLE数据同步的完整流程App和牙刷的同步流程我按实际代码逻辑拆解一下App扫描并连接牙刷。App调用BLE扫描接口根据服务UUID过滤找到牙刷后发起连接。连接成功后App先做服务发现找到刷牙数据服务获取各个特征值的句柄。App读取设备信息特征值获取固件版本、电量、待同步记录数。App写入时间同步特征值把当前手机时间下发给牙刷。App读取数据同步特征值牙刷端把待同步的记录打包通过Notify分多次发送。App收到数据后解析并存入本地数据库同时做去重。App写入同步确认特征值告诉牙刷哪些记录已同步。牙刷收到确认后更新记录的同步标志位。App主动断开连接牙刷端广播停止进入低功耗状态。这个流程里第5步的分包传输是难点。BLE的Notify每次最多传MTU-3个字节ATT头占3字节。如果MTU协商到185那每次能传182字节按每条记录7字节算一次能传26条。如果记录超过26条就需要多次Notify。App端要维护一个接收缓冲区把多次Notify的数据拼起来直到收完所有记录。注意BLE的Notify是异步的App端要处理好“收到一半断连”的情况。我们的做法是给每次同步会话分配一个IDApp端记录已收到的记录数断连后重连时从断点继续而不是从头开始。4.4 App端的数据展示与家长交互App端拿到数据后怎么展示给家长直接决定了这个功能有没有价值。我们的设计原则是一眼看懂不用思考。首页是一个日历视图每天一个格子格子里显示当天刷牙的总时长和次数。点进去是当天的详细记录每条记录显示时间段和时长。如果某天没刷或者刷得不够格子用不同颜色标出来。家长打开App扫一眼就知道孩子这周刷得怎么样。这里有个细节刷牙时长的“达标线”怎么定。牙医建议是每次2分钟一天两次。但孩子实际很难做到如果严格按2分钟标红家长每天看到的都是红色反而焦虑。我们的做法是分档展示2分钟以上是绿色1到2分钟是黄色1分钟以下是红色。同时给家长一个“目标设置”的入口可以自己调整达标线。还有一个功能是提醒。如果孩子超过一定时间没刷牙比如超过12小时App可以推送一条提醒。这个功能要谨慎推送太频繁家长会烦我们的默认设置是一天最多提醒一次而且家长可以关闭。提示App端的数据展示要考虑到多孩子场景。如果家里有两个孩子各自有一支牙刷App要能区分并分别展示。我们的做法是每支牙刷有唯一的设备IDApp绑定后给设备起名字比如“哥哥的牙刷”“妹妹的牙刷”数据按设备分开存储和展示。5. 常见问题与排查技巧实录5.1 连接不稳定、频繁断连这是BLE开发里最常见的问题没有之一。表现是App能扫描到牙刷但连接后几秒就断开或者连接成功率很低。排查思路我总结了一个顺序先看信号强度再看连接参数最后看协议栈配置。信号强度RSSI如果低于-80dBm说明距离太远或者有遮挡这是物理限制只能让用户靠近。如果RSSI正常但还断连就要看连接参数。有些手机默认的连接间隔很短比如7.5ms而牙刷端如果处理不过来就会断连。解决办法是在牙刷端主动发起连接参数更新请求把连接间隔设到一个双方都能接受的值比如30ms或50ms。还有一个坑是配对信息丢失。有些手机在断开后会清除配对信息下次连接需要重新配对。我们的做法是不使用配对绑定而是用“无绑定”的连接方式每次连接都是新连接不依赖配对信息。这样虽然安全性稍低但稳定性好很多而且刷牙数据本身也不敏感。问题现象可能原因排查方法解决办法扫描不到设备广播未开启或广播间隔太长用抓包工具看广播包检查广播配置缩短广播间隔连接后立刻断开连接参数不匹配查看手机端日志的连接间隔牙刷端发起连接参数更新连接成功率低信号弱或干扰大查看RSSI值靠近设备避开WiFi路由器断连后重连慢广播停止或协议栈问题查看断连后是否还在广播断连后重新开启广播5.2 数据同步不完整或重复数据同步的问题通常出在确认机制和去重逻辑上。我们遇到过两种情况一种是App读到了数据但没确认下次又读到导致重复另一种是App确认了但牙刷没收到导致数据丢失。解决重复的办法前面说了App端用“时间戳时长”做唯一键去重。解决丢失的办法是确认要可靠。我们的做法是App写入确认后牙刷端要回一个“确认收到”的NotifyApp收到这个Notify才算确认成功。如果App没收到下次连接时会重新读取这批数据。还有一个坑是时间戳精度。如果牙刷端的时间基准和手机时间有偏差同一条记录在App端可能被当成两条。我们的做法是时间戳精确到秒并且在时间同步时记录一个偏移量后续记录都用“基准时间偏移量”来计算保证一致性。5.3 功耗超标、续航缩水功耗问题往往在实验室里看不出来一到用户手里就暴露。我们早期版本续航只有两周后来一步步优化到两个月。关键优化点有三个第一广播间隔从100ms改成1s。这一项就把平均电流降了一个数量级。第二连接后尽快断开。我们原来设计是连接后保持一段时间后来改成传完数据立刻断开连接时间从几十秒降到几秒。第三深度睡眠时关闭所有外设。加速度计、Flash、电机驱动不用的时候全部断电或进入低功耗模式。还有一个容易被忽略的点LED指示灯。很多牙刷有LED灯如果LED一直亮着功耗比BLE还高。我们的做法是LED只在刷牙时亮刷完自动熄灭而且亮度调低。提示功耗测试要用专业的电流分析仪比如能测微安级电流的设备。普通万用表测不出深度睡眠的电流会误以为功耗很低。5.4 刷牙判定误触发误触发是用户投诉的重灾区。表现是牙刷放在杯子里App却显示“刷了3分钟”。我们分析下来主要是加速度计的误触发和判定阈值太宽松。解决办法有两个一是加姿态角过滤只有牙刷处于特定角度范围才判定为刷牙二是加运动模式匹配刷牙的运动是往复式的频率在2到5Hz之间而杯子晃动、包内碰撞的频率和模式都不一样。我们把这两个条件都加上后误触发率从每天几次降到几乎为零。还有一个场景是运输模式。牙刷在包装盒里运输时可能被晃动触发。我们的做法是出厂前进入“运输模式”加速度计中断关闭只有按键才能唤醒。用户第一次使用时按键唤醒退出运输模式。问题现象可能原因排查方法解决办法没刷牙却有记录加速度计误触发查看触发时的运动数据加姿态角过滤和频率匹配刷了牙没记录判定阈值太严查看刷牙时的运动数据放宽阈值延长确认时间记录时长偏短结束判定太快查看结束时的运动数据延长结束确认时间记录被拆成多条合并逻辑未生效查看记录间的时间间隔调整合并阈值6. 方案扩展与个人实操体会这套方案跑通之后其实还有很多可以扩展的方向。比如刷牙区域识别通过六轴传感器加速度计陀螺仪分析牙刷的姿态变化判断孩子有没有刷到后槽牙、有没有只刷一边。这个功能对家长来说更有价值但算法复杂度也高很多需要采集大量数据做训练。再比如游戏化激励App端根据刷牙数据给孩子发星星、做排行榜提升孩子的刷牙积极性。这些扩展都不影响核心的BLE上传方案是在数据基础上的增值功能。我个人在这个项目里最大的体会是BLE方案的成功一半在协议栈一半在场景理解。协议栈的问题可以通过选成熟芯片、用成熟SDK来解决但场景理解只能靠实际测试。比如“刷牙结束多久开始广播”这个参数我们在实验室里设的是10秒觉得够了结果用户测试时发现家长经常在刷完牙后一两分钟才打开App10秒根本连不上。后来改成30秒并且加了“下次唤醒时补广播”的逻辑体验才好起来。另一个体会是数据缓存的设计要留足余量。我们一开始只留了512字节的缓存觉得够存一周了结果遇到一个用户出差两周回来发现最早的数据被覆盖了。后来改成2KB并且加了“缓存将满”的提醒让App在缓存快满时主动提示用户同步。这种细节不实际跑一遍用户场景是想不到的。最后分享一个小技巧在牙刷上做一个“强制同步”的隐藏操作比如连续按三次按键或者放回充电座时自动触发一次广播。这个功能在用户遇到同步问题时非常有用相当于给了一个“手动刷新”的入口能解决很多“数据怎么没同步”的客服问题。这个操作不用写在说明书里但售后知道怎么指导用户就行。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑