这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » DIY与开源设计 » 电子DIY » 【EEPW-Let'sdesign】SHT43DemoBoard的BLE解码原理

共1条 1/1 1 跳转至页

【EEPW-Let'sdesign】SHT43DemoBoard的BLE解码原理

菜鸟
2026-10-21 14:25:28     打赏

SHT43 DemoBoard 开袋即食,目前为止遇到最友善测评开发板,感觉真的不写一行代码就能完成任务,那不写代码玩点啥呢。 本文结合结合 Sensirion 开源板卡固件(sht43-demoboard-ble-firmware)把数据怎么解读这个关键问题搞清楚了,就不止得到一个摆设了。 

我们来结合源码理解为什么能解、字节从哪来、公式在源码里长什么样整条数据线走一遍。

https://github.com/Sensirion/sht43-demoboard-ble-firmware

f0fccc0776a2b39f0277bae3b38dd61c.jpg

数据来源渠道

一块 SHT43 DemoBoard 上其实跑着四条互相独立的温湿度通路:

  1. 板载 LCD 直接显示

  2. BLE 广播(空口,被动监听)—— 把原始 ticks 塞进厂自定义字段持续发

  3. BLE GATT(需连接)—— 把换算后的 float32 温度值暴露成可读特征

  4. MyAmbience App 数据日志(需连接)—— 后台攒帧导出

 可以通过这四种方法获取数据,看你字节能有多折腾。 

129464f227c67a5ce6c01753d4920ccb.jpg

image.png

一、整条数据线:从传感器到空口字节

固件里数据是单向流水线,每一步都有源码实锤:

SHT4x 芯片 (I2C)
   │  读 4 字节原始值: [T_hi T_lo CRC RH_hi RH_lo CRC]
   ▼
ExtractMeasurementValues()   Sht4x.c:234
   │  temperatureTicks = (data[0]<<8)|data[1]   ← 高字节在前 = 大端组合成 uint16
   │  humidityTicks    = (data[3]<<8)|data[4]
   ▼
MessageBroker 发消息 (SHT4X_MESSAGE_ID_SENSOR_DATA)
   ▼
BleDefaultStateCb()         BleContext.c:468
   │  gCompleteAdvData.temperatureTicks = sensorMsg->...temperatureTicks
   │  gCompleteAdvData.humidityTicks    = sensorMsg->...humidityTicks
   │  BleGap_AdvertiseRequest(...)       ← 每次读到新值就重发广播
   ▼
广播 payload 里的 8 字节 (T 2B + RH 2B + 固定头 4B)
   ▼
空口抓包 → 套 Sht4x 公式 → 真实温湿度

关键设计:BleContext.c:472 那行注释写得很直白——"this does not change the advertisement mode"。意思是每读到一次新传感器值,就把 ticks 原样塞进广播结构体、重新发一遍广播。设备像一个"活体信标":不等你连,它自己不停把最新读数往空口上甩。

二、广播帧逐字节拆解(packed 结构体分析)

空口抓到的一帧(完整 33 字节):

09 09  53 48 54 34 33 20 44 42  0b ff  d5 06 00 06 b6 48 a1 65 ac 8d 02 0a 00

这 18 字节的厂自定义段(0b ff 之后)正好对应固件 BleTypes.h 里的一个 __attribute__((__packed__)) 结构体:

// BleTypes.h:129  字段顺序 = 空口字节序, packed 保证无对齐填充
typedef struct __attribute__((__packed__)) {
  uint8_t  adTypeSize;            // 09
  uint8_t  adTypeFlag;            // 09  (Complete Local Name)
  uint8_t  adTypeValue;           // 06  (sample-type 占位)
  uint8_t  adTypeNameSize;        // 09
  uint8_t  adTypeNameFlag;        // 09
  uint8_t  name[8];               // "SHT43 DB"
  uint8_t  adTypeManufacturerSize;// 0b
  uint8_t  adTypeManufacturerFlag;// ff  (Manufacturer Specific)
  uint16_t companyIdentifier;     // d5 06  = 0x06D5 小端 = Sensirion
  uint8_t  sAdvT;                 // 00     ← 注意: 这是 sAdvT, 不是 sampleType
  uint8_t  sampleType;            // 06     ← 这才是 sample-type
  uint8_t  deviceIdMsb;          // b6
  uint8_t  deviceIdLsb;          // 48
  uint16_t temperatureTicks;     // a1 65  = 26017 (小端)
  uint16_t humidityTicks;        // ac 8d  = 36268 (小端)
} BleTypes_CompleteAdvertisementData_t;

两个极易混淆的字节

论坛帖把 00 06 整段写成"ad-type=0, sample-type=6",省写了——但结构体里它们是两个独立字段:

字节字段含义




00sAdvT广播子类型标志(advertisement sub-type)
06sampleType数据格式标记(SHT4x 温湿度样本)

两者数值一个 0 一个 6,不是一回事。BleContext.c:103-108 的初始化也分开写:.adTypeValue = 0x06(这是设备名段后面跟的值,不是 sampleType)和 .sampleType = 0x06(真正的格式标记)。读空口时别把 sAdvT 当 sampleType。

为什么 ticks 是小端

temperatureTicks 在结构体里是 uint16_t,而 STM32WB55(Cortex-M4,小端架构)往 packed 结构体写值时按小端落盘——所以空口上 a1 65 要读成 0x65a1 = 26017。这就是为什么帖子用 26017 而不是 0xa165。字节序由目标 MCU 的 ABI 决定,不是 BLE 规范规定的——这是逆向空口数据时最坑的一个点。

三、原始 ticks 怎么变成真实值

temperatureTicks/humidityTicks 是 SHT4x 芯片 I2C 输出的 14 位 ADC 原始值(0–16383),不是温度/湿度本身。换算公式是 Sensirion 官方 Datasheet 公开的标准线性公式,固件 Sht4x.c:155-165 里一行不差:

// Sht4x.c:155  单精度 float 运算 (注意 .F 后缀)
float Sht4x_TicksToTemperatureCelsius(uint16_t ticks) {
  return (float)(ticks * (175.F / 65535.F)) - 45.F;
}
float Sht4x_TicksToHumidity(uint16_t ticks) {
  return (float)(ticks * (125.F / 65535.F)) - 6.F;
}

代入帖子那帧的 raw 值:

T  = -45 + 175 × 26017 / 65535 = 24.47 ℃
RH = -6  + 125 × 36268 / 65535 = 63.18 %RH

有个小细节需要注意一下:

公式里 175.F / 65535.F 用的是 32-bit float 运算,不是整数除法。这意味着:

  • 解算方用 float 算,结果和固件内部换算(喂给 GATT 的那份)完全一致

  • 用 double 算会多出几个末位误差(无伤大雅,但和板端 GATT Read 对到第 2 位小数时可能差 0.01)

  • 这就是为什么"四路对齐"里 GATT 和空口差不到 1℃ 却不会完全相等——它们经过不同数值通路(空口是 uint16 ticks 直接套 float 公式;GATT 是固件先算好 float 再按 IEEE754 小端 4 字节发出)

四、GATT 通路使用4 字节 float32

帖子另一条路——nRF Connect 连板读 Temperature 服务 0x2234 的特征 0x2235,返回 6E 88 CD 41。这不是 ticks,是固件已经换算好的 float32 温度值:

// TemperatureService.c:77  把 float 直接按 4 字节写进特征值
void TemperatureService_SetTemperature(float temperature) {
  BleGatt_UpdateCharacteristic(..., (uint8_t*)&temperature, sizeof temperature);
}
// AddTemperatureCharacteristic: .maxValueLength = 4  ← 4 字节 = 1 个 float32

把 6E 88 CD 41 按 IEEE 754 float32 小端 拆:

41 CD 88 6E  →  0x41CD886E  →  25.69 ℃

(注意 6E 88 CD 41 是小端字节序,重排成 41 CD 88 6E 才是 IEEE754 机器序。)

广播用 uint16 ticks(2 字节),GATT 用 float32(4 字节)——两条通路的"数据宽度"不同,这是 Sensirion 的取舍:广播要省 31 字节的 payload 预算,塞不下 4 字节浮点,只能压成 14-bit ticks;GATT 连接后带宽宽松,直接给换算好的高精度 float。

五、核心原理

  1. 广播是明文的。Manufacturer Specific Data(AD type 0xFF)是蓝牙规范明确留给厂商自由发挥的字段,BLE 协议本身不对它做任何加密/签名。Sensirion 把 ticks 原样塞进去,等于"裸奔"——任何 BLE 嗅探器都能偷听。

  2. 公式是公开的。ticks→物理量的换算写在 Sensirion 官方 Datasheet(HT_DS_Datasheet_SHT4x.pdf),线性公式,无密钥。

  3. 厂自定义段的结构是开源的。BleTypes.h 的 packed 结构体把每个字节的偏移、宽度、字节序全写死了,逆向后能精确对齐。

只要同时知道 ① Sensirion 的 SIG 公司 ID(0x06D5)② sample-type 标记(0x06)③ Datasheet 公式,剩下的就是"抓包 + 套公式",零固件依赖。

六、动手:自己写 30 行解码器

空口抓一帧后,按结构体偏移取字段(C 伪代码):

#include <stdint.h>
#include <stdio.h>

// 从一帧 BLE 广播里提取 0xFF 厂自定义段 (偏移见上表)
// raw: 0b ff d5 06 00 06 b6 48 a1 65 ac 8d 02
//      ── ── ────── ── ── ────── ────── ────── ──
//      L  T  company  sAdvT  sampleT  deviceID  Traw  RHraw

float ticks_to_C(uint16_t t)  { return (float)t * (175.f/65535.f) - 45.f; }
float ticks_to_RH(uint16_t h)  { return (float)h * (125.f/65535.f) - 6.f; }

int main() {
  // 按小端组 uint16: low byte first
  uint16_t t_raw = 0x65a1;   // a1 65 → 26017
  uint16_t h_raw = 0x8dac;   // ac 8d → 36268
  printf("T  = %.2f C\n", ticks_to_C(t_raw));   // 24.47
  printf("RH = %.2f %%RH\n", ticks_to_RH(h_raw)); // 63.18
  return 0;
}

抓包工具:手机端 BleAnalyzer / nRF Sniffer,PC 端 BLE 嗅探器(任何能 dump 广播 payload 的)。把 0xFF 段抠出来,按上面偏移取值,套两个公式,就解完了。

七、可打包带走的经验

SHT43 DB 最值钱的不是那颗 ISO 认证的 SHT43,而是它把传感器读数做成"主动广播信标"的设计:

  • 零成本扩展:家里摆 N 块,任何一台带 BLE 的中控(手机、树莓派、ESP32)空口监听就能聚合温湿度,不用给每块板配网络

  • 被动可观测:不需要连接、不需要账号、不需要授权——纯空口旁路监听,适合做"读而不连"的传感网络

  • 对照校准:同一片区域多块板互相广播,做空间温湿度场分布(哪面窗漏风、哪个角落回潮)

这也是 Sensirion 把这套固件开源的意图——给嵌入式开发者一个"BLE 传感节点"的完整参考:从 I2C 读传感器、到 MessageBroker 消息分发、到 GATT 服务 + 广播双通路暴露数据,全链路可读。


参考

  • 固件源:github.com/Sensirion/sht43-demoboard-ble-firmware

  • 关键文件:source/app_service/networking/ble/BleTypes.h(packed 结构体)、source/app/BleContext.c(广播填充 + 重发)、source/app_service/sensor/Sht4x.c(ticks→物理量公式、I2C 原始值解析)

  • GATT 服务:source/app_service/networking/ble/gatt_service/TemperatureService.c(float32 特征值)

  • 传感器规格:Sensirion HT_DS_Datasheet_SHT4x.pdf(线性校准公式、τ63% 响应时间)






关键词: EEPW-Let     SHT43     DemoBoard         

共1条 1/1 1 跳转至页

回复

匿名不能发帖!请先 [ 登陆 注册 ]