资讯动态

STM32驱动SHT85传感器并通过BLE传输数据的物联网实践

发布时间:2026/8/20 1:43:51 来源:尧图企业网站定制
1. 项目概述从传感器到云端的数据桥梁最近在做一个环境监测的小项目核心需求是把高精度的温湿度数据通过蓝牙无线传输到手机或网关。手头正好有STM32的开发板和Sensirion的SHT85温湿度传感器这个组合在工业级和消费级应用中都很常见。SHT85是数字I2C接口的传感器精度和稳定性没得说而STM32作为MCU界的“瑞士军刀”处理传感器数据和驱动BLE协议栈绰绰有余。这个项目的本质就是搭建一个从物理世界感知Sensor到数字世界传输BLE的完整数据链路。听起来简单但里面涉及到嵌入式固件开发、低功耗蓝牙协议、传感器驱动、数据打包以及移动端或网关端的交互每一个环节都有不少细节可以琢磨。无论你是刚开始接触物联网的嵌入式新手还是想为现有产品增加无线传感功能的老手这个从STM32采集SHT85数据并通过BLE上报的完整流程都是一个非常经典且实用的练手项目。2. 核心硬件与方案选型背后的逻辑为什么是STM32 SHT85 BLE这个组合不是随便选的背后有一整套工程化的考量。首先看传感器SHT85是Sensirion SHT3x系列中的高端型号提供了±1.5%RH的湿度精度和±0.1°C的温度精度并且带有增强的信号处理功能。在环境监测场景下数据的准确性是首要的SHT85的工业级可靠性避免了因传感器漂移导致的数据失真。它的I2C接口也非常简洁只需要两根线SCL SDA就能通信极大简化了硬件连接。MCU选择STM32尤其是带BLE射频的型号比如STM32WB系列或者用普通型号搭配外部BLE芯片如nRF52832。这里有一个关键决策点是选择集成BLE的SoC还是MCU外挂BLE芯片的方案对于这个项目如果追求极致的集成度和更简单的PCB设计STM32WB55是理想选择它内置了Cortex-M4应用内核和Cortex-M0的蓝牙射频内核。但如果手头已有常见的STM32F1/F4系列开发板那么搭配一个基于nRF52832的BLE模块如JDY-24是更经济快捷的方案。我这次用的是STM32F103C8T6蓝色药丸板和一個通用的BLE透传模块这样可以把重点放在传感器驱动和主控逻辑上蓝牙部分通过AT指令或串口协议控制降低了初期复杂度。BLE蓝牙低功耗协议的选择更是重中之重。它相比经典蓝牙功耗极低一颗纽扣电池能让设备运行数月甚至数年非常适合间歇性上报数据的传感器。在BLE的架构中我们的STM32设备需要扮演“外围设备Peripheral”的角色它广播自己的存在并包含一个“服务Service”这个服务下有一个或多个“特征值Characteristic”。对于温湿度数据我们通常会定义一个自定义服务里面包含两个特征值一个用于手机APP读取Read另一个用于手机APP订阅通知Notify这样当数据更新时手机能自动收到而不需要频繁轮询既省电又实时。3. 开发环境搭建与工程框架解析工欲善其事必先利其器。嵌入式开发的第一步永远是搭建一个顺手的开发环境。对于STM32主流的选择有STM32CubeIDE、Keil MDK和IAR。我个人更倾向于使用STM32CubeIDE因为它是ST官方推出的免费集成环境集成了STM32CubeMX图形化配置工具对于外设初始化和中间件包括蓝牙栈如果使用WB系列的配置非常直观。首先我们需要用STM32CubeMX来初始化工程。即使你用的是F1系列外挂BLE模块CubeMX也能帮你快速配置好系统时钟、调试接口SWD以及最关键的和传感器通信的I2C接口。创建新工程选择你的具体芯片型号例如STM32F103C8T6。在Pinout Configuration标签页下找到I2C1将其模式设置为I2C模式通常选择标准模式100kHz或快速模式400kHzSHT85最高支持1MHz但为了稳定起见先选择Fast Mode。注意查看自动分配的引脚通常是PB6-SCL PB7-SDA确保和你硬件上的连接一致。接下来配置时钟树Clock Configuration。对于F103通常使用外部8MHz晶振HSE通过PLL倍频到72MHz作为系统主频。这一步CubeMX可以自动帮你计算并配置你只需要在RCC设置里选择Crystal/Ceramic Resonator然后回到时钟树页面点击“解决”问题图标即可。然后要配置一个定时器比如TIM2用于产生一个固定的时间间隔例如每2秒去触发一次传感器数据读取和BLE发送流程。将TIM2设置为内部时钟源预分频器和周期值根据你的系统时钟计算。假设系统时钟72MHz我们希望产生一个2秒的更新事件可以设置预分频器PSC为7199这样计数器时钟为72MHz / (71991) 10kHz。再设置自动重装载值ARR为19999那么定时器周期就是 (199991) / 10kHz 2秒。最后使能定时器更新中断。对于调试务必使能SYS里的Debug为Serial Wire这样才能通过ST-LINK进行下载和调试。生成工程时选择工具链为STM32CubeIDE代码结构建议选择“为每个外设生成独立的.c/.h文件”这样代码更清晰。点击生成代码CubeIDE会自动打开项目。如果你的蓝牙模块是通过串口UART控制的还需要配置一个UART外设比如USART1设置合适的波特率常见的有9600 115200并开启中断接收用于接收蓝牙模块的响应和数据。注意使用CubeMX生成代码后用户代码要写在/* USER CODE BEGIN */和/* USER CODE END */注释对之间这样当你后期需要调整配置重新生成代码时自己写的代码不会被覆盖。这是一个非常重要的好习惯。4. SHT85传感器驱动开发详解传感器驱动是数据采集的源头必须稳定可靠。SHT85采用标准的I2C通信地址是0x447位地址写地址0x88读地址0x89。它的命令集很简单我们主要用两个触发测量命令和读取数据命令。首先在CubeMX生成的I2C初始化代码基础上我们需要编写几个基础函数I2C_Write和I2C_Read。STM32的HAL库提供了HAL_I2C_Master_Transmit和HAL_I2C_Master_Receive函数但为了处理SHT85的特定时序和错误重试最好做一层封装。SHT85的典型单次测量流程是发送测量命令例如高重复性测量命令0x2400。等待测量完成典型值15ms。读取6个字节的数据温度2字节 CRC校验1字节湿度2字节 CRC校验1字节。这里的关键点在于CRC校验。SHT85对传输的每两个字节数据都附带一个8位的CRC校验码用于确保数据在传输过程中的完整性。校验算法是CRC-8生成多项式为x^8 x^5 x^4 1初始值为0xFF。我们必须在驱动层实现这个CRC校验函数并在读取数据后立即验证如果校验失败应该丢弃这次数据并重试。下面是一个简化的驱动函数示例#define SHT85_I2C_ADDR_WRITE 0x88 #define SHT85_I2C_ADDR_READ 0x89 #define CMD_MEAS_HIGHREP 0x2400 uint8_t SHT85_CRC8(const uint8_t *data, int len) { uint8_t crc 0xFF; for(int j len; j; --j) { crc ^ *data; for(int i 8; i; --i) { crc (crc 0x80) ? (crc 1) ^ 0x31 : (crc 1); } } return crc; } HAL_StatusTypeDef SHT85_ReadTempHumidity(float *temperature, float *humidity) { uint8_t cmd[2] {CMD_MEAS_HIGHREP 8, CMD_MEAS_HIGHREP 0xFF}; uint8_t rx_data[6]; // 1. 发送测量命令 if(HAL_I2C_Master_Transmit(hi2c1, SHT85_I2C_ADDR_WRITE, cmd, 2, HAL_MAX_DELAY) ! HAL_OK) { return HAL_ERROR; } // 2. 等待测量完成使用HAL_Delay简单实现实际项目建议用定时器非阻塞等待 HAL_Delay(20); // 3. 读取6字节数据 if(HAL_I2C_Master_Receive(hi2c1, SHT85_I2C_ADDR_READ, rx_data, 6, HAL_MAX_DELAY) ! HAL_OK) { return HAL_ERROR; } // 4. CRC校验 if(SHT85_CRC8(rx_data, 2) ! rx_data[2] || SHT85_CRC8(rx_data[3], 2) ! rx_data[5]) { return HAL_ERROR; // CRC校验失败 } // 5. 数据转换 uint16_t raw_temp (rx_data[0] 8) | rx_data[1]; uint16_t raw_humi (rx_data[3] 8) | rx_data[4]; *temperature -45 175 * ((float)raw_temp / 65535.0f); *humidity 100 * ((float)raw_humi / 65535.0f); return HAL_OK; }实操心得I2C通信容易受到线缆干扰。如果发现偶尔读取失败或CRC错误除了检查硬件连接上拉电阻是否已接通常4.7kΩ和电源稳定性外可以尝试降低I2C时钟速度从400kHz降到100kHz并加入简单的重试机制比如失败后延时几毫秒再重试最多3次。另外SHT85的VDD引脚旁需要加一个100nF的陶瓷电容去耦紧挨着传感器引脚放置这对保证通信稳定至关重要。5. BLE数据通信协议设计与实现数据从传感器读出来了接下来要考虑如何通过BLE发送出去。这里我们面临两个选择如果使用STM32WB系列你需要学习并配置ST的蓝牙协议栈基于OpenThread并定义GATT通用属性配置文件表。如果使用外挂BLE透传模块事情就简单很多通常模块已经固化了串口到BLE的透传功能我们只需要通过UART向模块发送设置指令和数据即可。我以更通用的外挂模块方案为例。假设我们使用一个支持AT指令的BLE模块它默认工作模式是手机连接后手机发送给模块的数据会通过串口输出给STM32STM32通过串口发送给模块的数据会被模块转发给手机。首先我们需要在STM32的UART初始化完成后发送一系列AT指令来配置模块。这通常在系统启动时完成一次。例如AT测试通信。ATNAMEEnvSensor设置蓝牙设备名称。ATUUID0xFFE0设置主服务的UUID这是一个常见的用于透传的服务UUID。ATCHAR0xFFE1设置特征值的UUID用于读写数据的通道。ATADVI5设置广播间隔数值越大广播越慢越省电。ATADTY3设置广播模式为持续广播。配置完成后模块就会开始广播手机上的蓝牙调试APP如LightBlue nRF Connect就能搜索到名为“EnvSensor”的设备并连接。数据上报的逻辑是在之前配置的2秒定时器中断服务函数里读取SHT85数据然后将温湿度数值按照一个简单的协议打包通过UART发送给BLE模块。协议设计要兼顾效率和可解析性。一个简单的例子[帧头][温度整数][温度小数][湿度整数][湿度小数][帧尾]例如温度25.6°C湿度60.3%RH可以打包为0xAA 0x19 0x06 0x3C 0x03 0x550xAA帧头25-0x19 0.6106-0x06 60-0x3C 0.3103-0x03 0x55帧尾。在STM32端代码实现如下// 在定时器中断回调函数或主循环中 if(measurement_flag) { // 2秒标志位 measurement_flag 0; float temp, humi; if(SHT85_ReadTempHumidity(temp, humi) HAL_OK) { uint8_t tx_buffer[6]; tx_buffer[0] 0xAA; // 帧头 tx_buffer[1] (uint8_t)temp; // 温度整数部分 tx_buffer[2] (uint8_t)((temp - tx_buffer[1]) * 10); // 温度小数部分*10 tx_buffer[3] (uint8_t)humi; // 湿度整数部分 tx_buffer[4] (uint8_t)((humi - tx_buffer[3]) * 10); // 湿度小数部分*10 tx_buffer[5] 0x55; // 帧尾 HAL_UART_Transmit(huart1, tx_buffer, 6, 100); // 通过UART1发送给BLE模块 } }手机端的APP需要能够解析这个自定义格式。或者你也可以让BLE模块工作在“直接传输原始数据”的模式然后在手机APP里按照同样的格式解析。注意事项串口发送数据要确保模块已准备好。有些模块在刚上电或模式切换时需要几十毫秒的稳定时间。最好在发送配置指令后等待并解析模块返回的“OK”响应。此外避免在中断服务函数中进行长时间的UART发送如果使用阻塞式HAL_UART_Transmit且超时时间设的过长这可能导致中断阻塞。更优的做法是设置一个发送标志在中断里启动DMA传输或在主循环中查询发送。6. 低功耗设计与电源管理优化对于电池供电的传感器节点功耗是生命线。我们的系统主要耗电部分有三个STM32 MCU、SHT85传感器、BLE模块。1. STM32的低功耗模式STM32F103支持睡眠Sleep、停止Stop和待机Standby模式。对于我们的应用2秒才工作一次大部分时间MCU都可以进入低功耗模式。最常用的是Stop模式。在Stop模式下所有时钟停止SRAM和寄存器内容保持功耗可以降到几十微安级别。唤醒源可以配置为外部中断比如按键或定时器RTC。我们需要重构程序流程将2秒的定时改用RTC的唤醒定时器Wake-up Timer或者低功耗定时器LPTIM来实现。主程序初始化完成后进入Stop模式。RTC每2秒产生一个唤醒事件STM32被唤醒执行数据采集和发送任务完成后再次进入Stop模式。CubeMX中可以配置RTC的唤醒中断。2. SHT85的功耗控制SHT85本身功耗不高单次测量典型值仅0.65mJ。但我们仍然可以在不测量时通过控制其电源引脚如果硬件设计允许或让其进入空闲模式来进一步省电。不过更常见的做法是直接让STM32的I2C引脚在进入Stop模式前设置为模拟输入高阻态避免产生漏电流。3. BLE模块的功耗控制这是功耗大头。透传模块通常有几种工作模式持续广播、连接态、睡眠模式。我们需要根据数据上报间隔来优化。广播间隔ATADVI指令设置的值越大广播间隔越长功耗越低。但手机搜索和连接会变慢。对于2秒上报一次的应用可以设置一个中等偏长的广播间隔如500ms-1s。连接间隔手机连接后可以通过手机端中央设备请求更长的连接间隔Connection Interval比如100ms甚至更长。连接间隔越长功耗越低但数据实时性稍差。这需要在手机APP端进行GATT参数更新请求。模块睡眠一些高级的BLE模块支持AT指令控制进入深度睡眠仅由STM32的GPIO唤醒。我们可以设计为STM32被RTC唤醒后先拉高一个GPIO脚唤醒BLE模块等待模块就绪几十毫秒然后发送数据数据发送完成后发送AT指令让模块进入睡眠最后STM32自己再进入Stop模式。实现低功耗是一个系统工程需要软硬件协同。硬件上断开所有不必要的负载使用低静态电流的LDO测量时务必用电流表或功耗分析仪实际测量各个阶段的电流才能找到真正的优化空间。7. 数据接收端应用与云端集成初步数据通过BLE发送出去后需要有“接收端”。最常见的是手机APP。你可以使用Android的BluetoothGATTAPI或iOS的CoreBluetooth框架来开发一个简单的APP扫描设备、连接、发现服务/特征值然后订阅通知或定期读取特征值解析我们自定义的数据包将温湿度显示在界面上。对于更倾向于无屏化或远程监控的场景另一个强大的接收端是蓝牙网关。市面上有很多基于树莓派Raspberry Pi或ESP32的蓝牙网关方案。它们运行着如bluezLinux等协议栈可以同时连接多个BLE设备读取数据然后通过Wi-Fi或以太网上传到云服务器如阿里云IoT、腾讯云IoT、ThingsBoard私有部署等。这里简述一个基于树莓派和Python的网关数据抓取思路在树莓派上安装bluez和pybluez或bleak一个跨平台的Python BLE库库。编写Python脚本使用bleak扫描并连接名为“EnvSensor”的设备。发现指定UUID的服务和特征值对于我们透传模块模拟的场景通常是0xFFE0服务和0xFFE1特征值。启动通知start_notify并设置回调函数。当STM32设备通过模块发送数据时网关的回调函数会收到原始字节数据。在回调函数中按照约定的帧格式0xAA开头0x55结尾解析出温度和湿度值。将解析后的数据通过MQTT协议发布到云平台或者写入本地数据库。这样一来你的STM32传感器节点就成为了一个真正的物联网终端数据可以跨越物理空间在云端进行存储、分析和展示。8. 项目调试与常见问题排查实录在实际动手过程中你肯定会遇到各种各样的问题。下面是我在多次项目中总结的一些典型问题及其排查思路希望能帮你少走弯路。问题1I2C通信失败HAL_I2C_Master_Transmit返回超时或错误。检查硬件首先用万用表测量SDA和SCL线对上拉电阻的电压空闲时应该是高电平接近VCC。确认传感器供电是否正常3.3V。检查焊接和连接I2C线路对地或对电源短路是常见问题。检查软件确认CubeMX中I2C的引脚配置是否正确是否和其他功能如调试口冲突。确认I2C时钟速度是否过快尝试降到100kHz。检查HAL_I2C_MspInit函数中GPIO和时钟的初始化是否被正确生成。使用逻辑分析仪这是最强大的工具。连接SCL和SDA查看实际波形。看起始信号、地址位、ACK信号是否正常。有时是因为传感器响应太慢需要在I2C初始化时延长时钟延展Clock Stretching的超时时间HAL库相关配置。问题2能读到数据但数值明显不对比如温度是85°C湿度是0。CRC校验失败首先检查你的CRC校验函数是否正确。可以用一组已知的数据和CRC结果进行测试。Sensirion官网通常提供CRC计算示例代码。数据字节序确认你组合两个字节数据时的顺序。SHT85是高位字节MSB先发送。raw_temp (rx_data[0] 8) | rx_data[1];这个顺序是正确的。公式应用错误再次核对数据转换公式。SHT85的公式是线性的温度 -45 175 * (raw_temp / 65535)。确保使用浮点数运算65535要写成65535.0f或65535.0否则在C语言里整数相除会截断。问题3手机搜不到BLE设备或者连接后马上断开。模块未正确配置或启动用USB转TTL工具直接连接BLE模块的串口用串口助手发送AT指令看是否返回OK。确保模块已正确供电3.3V且使能引脚如果有被拉高。广播参数问题广播间隔太短可能导致部分手机扫描不到。尝试用ATADVI10设置更长的广播间隔如1秒。检查设备名称是否含有特殊字符。手机系统或APP问题尝试换一个手机或换一个BLE调试APP如从LightBlue换成nRF Connect。有时候手机蓝牙缓存旧设备信息会导致问题可以尝试重启手机蓝牙或重启手机。问题4数据发送不稳定偶尔丢包。电源干扰在STM32和BLE模块的电源引脚附近增加一个10uF的钽电容和一个100nF的陶瓷电容并联进行退耦。特别是在模块发射无线电的瞬间电流会有较大脉动稳定的电源至关重要。串口波特率不匹配确保STM32的UART波特率和BLE模块设置的波特率完全一致。常见的115200波特率实际时钟误差可能累积导致误码。可以尝试降低到9600波特率测试稳定性。流控未启用如果数据量较大可以考虑启用硬件流控RTS/CTS但这需要模块和STM32都支持并连接。更简单的办法是在软件上增加流控机制例如模块通过一个GPIO告诉STM32“缓冲区满暂停发送”。问题5系统整体功耗偏高。测量方法不要相信理论计算一定要用万用表电流档或专业功耗分析仪测量。将表串联在电池和系统电源之间观察不同状态广播、连接、睡眠下的电流。排查漏电将STM32所有未使用的GPIO设置为模拟输入模式。检查电路板上是否有其他始终供电的器件如指示灯。检查软件流程确保MCU在任务间隙真的进入了低功耗模式Stop模式。可以在进入低功耗模式前点亮一个LED唤醒后熄灭观察LED是否长时间熄灭。使用调试器连接有时会阻止MCU进入深度睡眠测量功耗时需要断开调试器。这个从STM32采集SHT85数据并通过BLE传输的项目麻雀虽小五脏俱全。它串联了传感器接口、实时数据采集、低功耗设计、无线通信和上层应用交互等多个物联网核心环节。每一步的深入理解和实践都能为你今后构建更复杂的嵌入式系统打下坚实的基础。当你第一次看到手机APP上稳定地显示出从几米外传感器发来的实时温湿度曲线时那种成就感就是驱动我们工程师不断探索的最佳燃料。

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

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

免费获取报价