1. 项目概述:用IN100打造一个无线温湿度传感器
最近在捣鼓一些低功耗的物联网小玩意儿,发现InPlay的IN100这颗芯片挺有意思。它是一个超低功耗的蓝牙5.1 SoC,自带NanoBeacon功能,非常适合用来做那些需要长时间待机、偶尔上报一下数据的传感器节点。正好手头有个项目,需要监测几个储物柜内部的温湿度,布线不方便,电池供电还得撑个大半年,IN100就成了我的首选。这个项目,说白了就是利用IN100读取SHT40温湿度传感器的数据,然后通过蓝牙广播(Beacon)的方式把数据“喊”出来,让附近的手机或者网关设备能接收到。整个过程不建立复杂的蓝牙连接,功耗极低,实现起来也相对简单。
你可能要问,为什么选IN100和SHT40这个组合?IN100的核心优势在于其极低的待机电流和集成的NanoBeacon协议栈,你几乎不用写复杂的蓝牙协议代码,配置一下就能发广播包。SHT40则是Sensirion新一代的温湿度传感器,精度不错,功耗也低,关键是它使用I2C接口,和IN100通信起来非常方便。这个项目适合那些想入门低功耗蓝牙传感、或者需要快速搭建一个无线数据采集原型的开发者。即使你对蓝牙协议栈不熟,跟着这个思路走,也能很快把东西跑起来。
2. 核心硬件选型与电路设计思路
2.1 主角解析:IN100与SHT40为何是黄金搭档
选择IN100,最看中的就是它的“懒人”模式。传统的蓝牙芯片,你要想发个数据,得先初始化协议栈,处理连接事件,管理功耗状态,一堆事。而IN100的NanoBeacon模式,把它配置成一个信标(Beacon),它就会按照你设定的时间间隔,周期性地广播一个特定的数据包。这个数据包里可以携带自定义的信息,比如我们的温湿度读数。芯片大部分时间在深度睡眠,只有到点广播和读取传感器时才醒来一下,平均电流可以做到微安级,一颗纽扣电池用上一年不是梦。
SHT40是数字温湿度传感器里的“优等生”。它提供了±1.8%RH的湿度精度和±0.2°C的温度精度,对于大多数环境监测应用完全足够。它的功耗非常低,一次测量消耗约0.4mA电流,耗时不到10ms,测完就可以进入休眠模式。通信接口是标准的I2C,只有两根线(SCL和SDA),节省IO资源。最重要的是,它的工作电压范围是1.08V到3.6V,和IN100的供电电压完美匹配,可以直接用同一颗电池供电,不需要额外的电平转换电路。
2.2 供电与外围电路设计要点
整个系统的供电方案是设计的重中之重,直接决定了续航能力。我推荐使用一颗CR2032纽扣电池(3V,220mAh)作为主电源。IN100的工作电压范围是1.8V到3.6V,SHT40是1.08V到3.6V,电池电压从满电的3.2V放到2.0V,两者都能正常工作,可以充分利用电池容量。
注意:虽然理论上电压可以低至1.8V,但考虑到电池内阻和瞬间电流,建议设置一个稍高的截止电压,比如2.2V,以保护电池并保证系统稳定。
电源电路需要一颗滤波电容,通常用一个10µF的钽电容或陶瓷电容并联一个0.1µF的陶瓷电容,分别滤除低频和高频噪声,确保IN100射频部分和数字内核的稳定。对于SHT40,在其VDD和GND引脚之间也需要加一个0.1µF的去耦电容,位置尽可能靠近传感器引脚。
复位电路方面,IN100有专用的复位引脚(RSTn)。为了保证上电稳定和必要时的手动复位,建议连接一个标准的阻容复位电路:一个100kΩ的上拉电阻接到VCC,一个0.1µF电容接地。同时可以引出一个轻触开关到地,实现手动复位。
2.3 I2C通信线路连接与上拉电阻选择
IN100和SHT40通过I2C总线连接,这是本项目唯一的数字通信线路。连接非常简单:
- IN100的某个GPIO(配置为I2C_SCL)接SHT40的SCL引脚。
- IN100的另一个GPIO(配置为I2C_SDA)接SHT40的SDA引脚。
- 两者共享VCC和GND。
这里有个关键细节:I2C总线是开漏输出,必须加上拉电阻。上拉电阻的阻值需要仔细计算,它影响了总线速度、功耗和信号上升时间。公式主要考虑总线电容(C_bus)和上升时间(t_r)。总线电容包括走线电容和所有连接在总线上的器件引脚电容,对于这种简单电路,估计在10-50pF之间。标准模式(100kHz)下,上升时间要求小于1µs。
假设VCC=3V,使用经典的公式R_pullup < t_r / (0.8473 * C_bus)。如果C_bus取50pF,t_r要求1µs,计算出的R_pullup最大值约为23.6kΩ。考虑到留有余量和降低功耗,我通常选择10kΩ的电阻。这个阻值在3V电压下,高电平时每条线电流约0.3mA,功耗可以接受;同时也能保证在100kHz甚至400kHz(快速模式)下可靠的信号上升。
实操心得:如果你追求极致的低功耗,可以考虑使用更大的阻值,比如47kΩ甚至100kΩ,但这会限制总线速度并增加对噪声的敏感性。对于本项目中每秒才读取一次数据的场景,100kHz速度绰绰有余,使用10kΩ或22kΩ是一个稳健的选择。务必使用精度为1%或5%的金属膜电阻。
3. 软件架构与IN100 NanoBeacon配置
3.1 工程初始化与时钟配置
首先需要在InPlay提供的开发环境(通常是基于Keil或IAR)中创建一个新工程,选择正确的IN100型号。第一步是配置系统时钟。IN100内部有多个时钟源:低速内部RC振荡器(32.768kHz)、高速内部RC振荡器(16MHz)以及高频晶体振荡器(可选)。为了平衡精度和功耗,我们这样配置:
- 低频时钟(LFCLK):使用内部32.768kHz RC振荡器,为实时计数器(RTC)和睡眠定时器提供时钟,这是实现周期性唤醒的关键。
- 高频时钟(HFCLK):在需要运行射频和高速处理时(比如广播瞬间),切换到16MHz内部RC振荡器。广播完成后,立即切换回低频时钟并进入睡眠。
这种动态时钟切换是低功耗设计的核心。在代码中,你需要初始化时钟树,设置好这些时钟源以及它们之间的切换条件。InPlay的SDK里通常有相应的API,例如sysclk_init()和pmu_switch_to_low_power()。
3.2 NanoBeacon广播数据包格式设计
NanoBeacon广播的数据包格式是自定义的,我们需要把温湿度数据塞进去。一个标准的蓝牙广播包最大是31字节。其结构通常包括:
- 前导码和接入地址:由协议栈自动处理。
- 广播报文头(Header):包含广播类型、地址类型等。
- 广播地址(AdvA):6字节的广播者蓝牙地址,可以自定义或随机生成。
- 广播数据(AdvData):这是我们放自定义数据的地方。长度可变,最多28字节左右(扣除报文头和地址)。
- CRC校验:协议栈自动添加。
我们的重点是设计AdvData。它由若干个「AD Structure」组成,每个结构包含1字节长度、1字节AD Type和N字节的AD Data。我们可以利用“制造商特定数据”(AD Type = 0xFF)来携带我们的数据。例如:
- 长度:假设我们数据总长8字节,则AD Structure长度为
1(长度字节)+1(类型字节)+8(数据)=10,所以第一个字节是0x0A。 - AD Type:0xFF 表示制造商特定数据。
- AD Data:接下来的8字节,我们可以定义如下:
- 字节1-2:制造商ID(自定义,例如0x1234)。
- 字节3:数据包类型/版本(例如0x01表示温湿度数据)。
- 字节4-5:温度数据(16位有符号整数,单位0.01°C)。
- 字节6-7:湿度数据(16位无符号整数,单位0.01%RH)。
- 字节8:电池电压(单位0.01V)或校验和。
这样,接收端(如手机App)在扫描到广播包后,识别制造商ID为0x1234且类型为0x01,就能正确解析出温度、湿度和电池电压。
3.3 低功耗任务调度与睡眠管理
程序的主循环将是一个典型的“事件驱动+深度睡眠”架构。流程图的核心逻辑如下:
- 系统上电初始化(时钟、GPIO、I2C、NanoBeacon参数)。
- 进入主循环。
- 检查RTC定时器是否到期(例如,每10秒唤醒一次)。如果未到期,则调用
pmu_enter_deep_sleep()进入深度睡眠模式。此时CPU停止,仅RTC和唤醒电路工作,电流在1µA级别。 - RTC定时器到期,系统唤醒。
- 唤醒后,首先将系统时钟切换到高速模式(16MHz)。
- 通过I2C读取SHT40的温湿度数据。
- 将读取到的原始数据(如温度ADC值)按照传感器数据手册中的公式转换为实际物理值(°C和%RH)。
- 将转换后的温湿度值(以及可能读取的电池电压)按照预设格式打包到NanoBeacon的广播数据缓冲区。
- 启动一次或连续几次蓝牙广播(例如,快速广播3次以提高被接收的概率)。
- 广播完成,将系统时钟切换回低速模式。
- 重置RTC定时器,开始下一个睡眠周期。
- 跳回步骤3。
这个循环中,99.9%以上的时间芯片都在深度睡眠,只有不到100ms的时间处于活跃状态,从而实现了超低功耗。
4. I2C驱动实现与SHT40数据读取详解
4.1 IN100的I2C外设初始化
IN100的I2C控制器配置相对直接。你需要配置相关的GPIO引脚为I2C功能模式(通常是开漏输出模式)。然后初始化I2C控制器:
- 设置时钟频率:SHT40支持标准模式(100kHz)和快速模式(400kHz)。为了可靠性和低功耗,我们先使用100kHz。对应的时钟分频系数需要根据系统高频时钟频率计算。如果系统时钟是16MHz,要产生100kHz的SCL,计算公式涉及I2C时钟控制寄存器的分频设置,具体值需参考IN100用户手册。
- 配置自身地址:由于IN100是主设备,SHT40是从设备,所以IN100的主地址模式可以简单设置。
- 使能I2C控制器。
这里有个坑要注意:IN100的I2C引脚功能映射可能不是固定的,需要查看数据手册的“引脚复用”章节,将对应的GPIO配置为I2C_SCL和I2C_SDA功能。
4.2 SHT40的测量指令与读取流程
SHT40的通信协议非常简洁。它有一个7位的I2C从机地址,默认是0x44(二进制1000100)。测量温湿度有两种精度模式,我们选择高精度模式,对应的命令字是0xFD。
一次完整的测量读取流程如下:
- 发送测量命令:IN100作为主设备发起START条件,发送SHT40的地址(0x44<<1 | 0=写),收到ACK后,发送命令字节0xFD,再收到ACK后,发送STOP条件。这条指令会触发SHT40内部进行一次温湿度测量,测量时间大约10ms。
- 等待测量完成:发送命令后,需要延迟至少10ms,确保测量完成。这里可以用一个简单的
delay_ms(12)函数。 - 读取数据:再次由IN100发起START条件,发送SHT40的地址(0x44<<1 | 1=读),然后连续读取6个字节的数据。这6个字节分别是:
- 温度数据高字节(Temp_MSB)
- 温度数据低字节(Temp_LSB)
- 温度数据的CRC8校验和(Temp_CRC)
- 湿度数据高字节(Hum_MSB)
- 湿度数据低字节(Hum_LSB)
- 湿度数据的CRC8校验和(Hum_CRC)
- 主设备在读完第6个字节后,发送NACK和STOP条件,结束传输。
重要提示:SHT40返回的数据附带了CRC8校验,这是保证数据在I2C传输过程中不出错的有效手段。在实际产品代码中,强烈建议实现CRC校验函数,在解析数据前先校验,如果校验失败,应丢弃本次数据或重试。
4.3 数据解析与物理量转换
读取到的6个字节是原始数据,需要转换成实际的温度和湿度值。转换公式在SHT40的数据手册中有明确给出。
温度转换:
- 将温度高字节和低字节组合成一个16位无符号整数
raw_temp(Temp_MSB << 8 | Temp_LSB)。 - 计算实际温度:
temperature_degC = -45 + 175 * (raw_temp / 65535)这个公式将原始ADC值(范围0~65535)线性映射到-45°C到+130°C的范围。计算时注意使用浮点数或进行定点数运算以避免精度损失。
湿度转换:
- 将湿度高字节和低字节组合成一个16位无符号整数
raw_hum(Hum_MSB << 8 | Hum_LSB)。 - 计算实际相对湿度:
humidity_RH = -6 + 125 * (raw_hum / 65535)这个公式将原始ADC值线性映射到-6%RH到+119%RH的范围。通常结果在0-100%RH之间。
在资源受限的MCU上,直接进行浮点运算可能比较耗时和耗能。可以采用定点数运算来优化。例如,将公式变形为:temperature_degC = (-45 * 65535 + 175 * raw_temp) / 65535。可以先计算分子(一个32位整数),再除以65535(相当于右移16位),最后除以100得到带两位小数的温度值(单位0.01°C),方便存入之前设计的16位整数广播字段。
5. 系统集成、功耗测试与优化
5.1 代码集成与广播触发逻辑
将上述模块整合起来。在主循环的唤醒分支中,依次调用:
sht40_read_data(&temp_raw, &hum_raw):封装好的读取函数,内部包含I2C通信、延迟和CRC校验。convert_to_physical_values(temp_raw, hum_raw, &temp_phy, &hum_phy):转换函数,输出定点数格式的物理量。prepare_beacon_data(temp_phy, hum_phy, beacon_buffer):将物理量按照预设格式(如2字节温度、2字节湿度)填充到广播数据缓冲区。nanobeacon_update_adv_data(beacon_buffer, length):调用IN100 SDK的API,更新广播数据。nanobeacon_start_adv(3):启动广播,参数3表示连续广播3个周期后自动停止。
广播触发后,IN100的射频模块会工作,电流会瞬间上升到几个毫安甚至十几毫安。广播完成后,SDK会产生一个事件,我们在事件处理函数中,可以再次将系统置于准备睡眠状态。
5.2 实际功耗测量与电池寿命估算
理论计算很重要,但实测才是王道。你需要一个能测量微安级电流的万用表或功耗分析仪(如Joulescope)。将仪器串联在电池和电路板之间。
测试方法:
- 睡眠电流:让设备进入深度睡眠模式,测量此时的平均电流
I_sleep。理想情况下应小于2µA。 - 活动窗口电流与时间:触发一次完整的“唤醒-读取-广播”流程,用示波器或功耗分析仪捕获电流波形。测量出:
- 唤醒及读取传感器阶段的平均电流
I_active1和持续时间t_active1(约15ms)。 - 广播阶段的平均电流
I_active2和持续时间t_active2(假设广播3次,约10ms)。
- 唤醒及读取传感器阶段的平均电流
- 计算平均电流:假设广播间隔为
T_interval(如10秒)。- 每次循环总电荷量
Q_cycle = I_active1 * t_active1 + I_active2 * t_active2 + I_sleep * (T_interval - t_active1 - t_active2)。 - 平均电流
I_avg = Q_cycle / T_interval。
- 每次循环总电荷量
举例:I_sleep=1.5µA, I_active1=2mA (15ms), I_active2=10mA (10ms), T_interval=10s。 计算得Q_cycle ≈ 2mA*0.015s + 10mA*0.01s + 1.5µA*9.975s ≈ 0.03 + 0.1 + 0.01496 = 0.14496 mAs。I_avg = 0.14496 mAs / 10s ≈ 14.5 µA。
使用一颗CR2032电池(标称容量220mAh,考虑到低温、自放电等因素,取可用容量200mAh)。 理论续航时间T_life = 200mAh / 14.5µA ≈ 13793小时 ≈ 574天。超过一年半!
5.3 深度优化技巧与常见陷阱
优化技巧:
- 调整广播间隔:根据应用需求,在数据更新频率和功耗之间权衡。从10秒延长到30秒,平均电流几乎成比例下降。
- 减少广播次数:每次唤醒不一定需要广播3次。在信号环境好的地方,广播1次成功率就很高。可以动态调整,例如连续3次广播失败后,下次唤醒增加广播次数。
- 降低广播功率:IN100的射频发射功率是可调的。降低发射功率(如从0dBm降到-20dBm)能显著降低广播时的峰值电流,代价是通信距离缩短。对于柜内监测这种短距离场景,完全可行。
- 优化传感器读取频率:如果不是每次都需要新数据,可以多次唤醒只读取一次传感器,其他时间广播旧数据。
- 断开所有无用负载:确保不用的GPIO设置为输入模式并内部上拉或下拉,关闭所有未使用的外设时钟。
常见陷阱:
- I2C总线锁死:如果程序异常或电源不稳,可能导致I2C从设备(SHT40)挂起,拉低SDA线,造成总线锁死。解决方法是在I2C初始化函数或发生超时后,增加一个“恢复序列”:手动控制SCL引脚产生9个时钟脉冲,同时检测SDA是否被释放。
- 广播被干扰:在2.4GHz频段,Wi-Fi、蓝牙其他设备都可能造成干扰。如果发现数据接收不稳定,可以尝试轻微改变广播间隔(如从1000ms改为1050ms),避免与环境中周期性的干扰源同步。
- 电池电压监测:为了预警电池电量低,可以定期用IN100内部的ADC测量电池电压(通过分压电阻)。但ADC转换本身也耗电(约几百微安,持续几十微秒)。需要权衡监测频率,比如每100个循环(约16分钟)测一次。
- 软件看门狗:在长期运行的项目中,一定要启用硬件或软件看门狗,防止程序跑飞。但注意,在进入深度睡眠前,需要暂停或妥善处理看门狗,否则它会阻止睡眠或唤醒后复位。
6. 数据接收端方案与项目扩展思路
6.1 手机App接收与解析示例
数据发出来了,还得有接收端。对于手机,可以开发一个简单的App(使用Android的BluetoothLeScanner或iOS的CoreBluetooth)来扫描和解析我们的自定义Beacon。
以Android为例,在扫描回调onLeScan或ScanCallback中,你会拿到一个ScanRecord对象。遍历其中的ScanRecord.getManufacturerSpecificData()方法返回的SparseArray。查找制造商ID(我们之前定义的0x1234)对应的字节数组。拿到这个数组后,按照我们约定的格式解析:
- 第0字节:数据包类型(应是0x01)。
- 第1-2字节:温度值(有符号short,需转换为
温度值/100.0f得到摄氏度)。 - 第3-4字节:湿度值(无符号short,需转换为
湿度值/100.0f得到百分比)。 - 第5字节:电池电压(需转换为
电压值/100.0f得到伏特)。
解析后就可以在UI上显示实时数据,并可以记录历史数据形成曲线图。
6.2 网关集中采集与云端同步
对于需要集中管理多个传感器的场景(如仓库、实验室),可以使用树莓派、ESP32或其他带蓝牙的Linux网关设备。网关设备上运行一个后台服务(可以用Python的bluepy或pybluez库),持续扫描周围的Beacon,过滤出我们设备的制造商ID,解析数据,然后通过Wi-Fi或以太网上传到云平台(如阿里云IoT、ThingsBoard、自建MQTT服务器)。
这种架构的优势在于,传感器节点极度简单、省电,复杂的网络连接和数据聚合由一直供电的网关负责。网关还可以进行初步的数据校验和过滤。
6.3 功能扩展与变体设计
这个基础框架可以衍生出很多变体:
- 多传感器集成:IN100有多个GPIO和ADC,可以同时连接多个传感器。例如,加上一个光照传感器(BH1750,也是I2C)或一个PIR运动传感器(GPIO中断)。在广播数据包中增加相应的字段即可。注意功耗管理,非必要时不唤醒这些传感器。
- 触发式上报:除了定时上报,还可以由事件触发。比如,用门磁开关(连接到GPIO中断)监测柜门开关,开门时立即唤醒并密集上报一段时间的数据。
- 数据加密:如果数据涉及隐私,可以在IN100端对广播数据进行简单的加密(如AES-128),接收端需要密钥才能解密。虽然广播本身是公开的,但加密后内容不可读。
- OTA固件升级:IN100支持通过蓝牙进行固件升级(DFU)。可以设计一个模式,当设备收到特定的升级指令Beacon时,进入DFU模式,等待手机或网关发送新的固件包。这是产品化必备的功能。
- 更长的续航:如果使用更大容量的电池(如AA锂铁电池),并结合太阳能板进行微能量收集,可以实现理论上的“永久”续航,适用于野外环境监测。
这个项目从硬件选型、电路设计到软件驱动、功耗优化,涵盖了一个低功耗无线传感节点开发的核心环节。IN100的NanoBeacon特性让蓝牙广播应用的门槛降低了很多,而SHT40提供了可靠的环境数据。把这两者结合起来,你就能快速搭建起一个稳定、省电的无线监测点。在实际部署中,最关键的就是功耗的精细测量和优化,以及通信可靠性的测试。多准备几块板子,放在不同的位置跑上几天,看看数据接收是否连续,电池消耗是否符合预期,这些实地测试的经验,远比理论计算来得宝贵。