
简介基于窄带物联网(NB-IoT)与STM32的光照度采集上云项目资源包面向物联网应用开发初学者、嵌入式爱好者以及正在备战物联网实训或毕业设计的学员也适合作为高校物联网课程的配套案例。项目以STM32为控制核心读取光照度传感器数据经NB-IoT模块传输至腾讯云再通过微信小程序展示实时数值完整覆盖硬件采集、网络通信、云端接入和移动端呈现的链路。资源包共5个文件集成约4.04MB包含两套STM32工程源码分别适配标准库与Pro版开发环境、项目简介与搭建步骤HTML文档、善学坊官方学习指南HTML文档以及License许可说明源码、配置和文档分层放置既有可直接运行的固件也有逐步搭建指引。当前已有643人浏览学习。通过该资源可系统掌握STM32的ADC采样与光照传感器驱动、NB-IoT模块的AT指令控制、腾讯云设备接入与数据流转、微信小程序API对接等关键环节同时了解嵌入式、通信、云端和移动端协同开发的完整思路便于后续扩展温湿度、GPS等更多传感器应用。1. 用 NB-IoT 把光照度传到腾讯云比 WiFi 方案多出来的三件事光照度采集这种项目最常见的第一版是用 STM32 加 ESP8266 走 WiFi上云快、调试简单但真放到室外绿化带、农业大棚或者厂房采光井里WiFi 覆盖和功耗两个问题立刻浮现。NB-IoT 走运营商授权频谱单点覆盖能穿两层楼一个基站扛几万个终端上报一条 100 字节的 JSON 报文平均电流能做到微安到毫安级——这正是光照度这类低速率、小报文、长时间待机的数据最舒服的传输方式。下面按“方案选型、腾讯云平台配置、STM32 采集与上报、链路排错、低功耗落地”这条线把基于 NB-IoTSTM32 实现采集光照度到腾讯云的一套可复现做法讲清楚适合准备毕业设计、物联网竞赛或要快速出 NB-IoT 落地 demo 的嵌入式工程师。2. NB-IoT STM32 光照度采集系统的方案选型与数据链路2.1 为什么光照度这类低速率小报文适合走 NB-IoT2.1.1 NB-IoT 的特性边界室内光照度采集的报文通常是“设备编号、时间戳、lux 值、信号强度”一条不超过几十字节。NB-IoT 的设计目标就是这类业务上行速率约 20-60kbps下行更窄但覆盖增益比 LTE 高 20dB能钻到地下室和管道井里。3GPP 给 NB-IoT 定义了 PSM 和 eDRX 两种节电机制设备可以长时间“睡觉”睡醒后只上报一个数据包又睡回去。相比之下WiFi 模组待机电流在毫安级还要维护 TCP keepalive在无人维护的现场很不划算。需要注意边界NB-IoT 不适合实时性要求高的双向控制时延通常在 1-6 秒命令下发是低频次的。如果后面要做自动补光灯的毫秒级控制正确做法是让 STM32 本地判断云端只做记录和远程策略调整。这个定位想清楚后边腾讯云上的数据模板和 Topic 设计才不会跑偏。2.1.2 与 Cat.1 和 LoRa 的取舍如果数据量不大但要求实时响应4G Cat.1 更合适如果园区里自己布网关、不上运营商网络LoRa 更合适但要自己维护网关和频点合规问题。NB-IoT 的优势是直接用运营商存量基站不用自建网关一张卡一年流量很少也够用。选 NB-IoT 的场景一般是位置分散、数量多、没有本地 WiFi、又不希望自己维护网络设备的项目。这个逻辑也决定了腾讯云那边选产品时要按“设备-密钥”直接配而不是按“网关-子设备”的拓扑配。2.2 硬件选型BH1750 传感器与 NB-IoT 模组的搭配光照度采集传感器常用两种光敏电阻加 ADC 电路或者数字光照度传感器。光敏电阻成本低但需要自己校准分压电阻和 ADC 参考电压不同批次的器件一致性差工程上标定很烦。BH1750 是 I2C 接口的数字传感器量程 1-65535 lx内部有光电二极管和 ADC直接输出 lux 值精度对农业补光和室内照明监测够用。I2C 只需要两根线STM32 的硬件 I2C 或 GPIO 模拟 I2C 都能驱动。要注意 BH1750 的 ADDR 引脚电平决定设备地址是 0x23 还是 0x5C多路采集时可以用不同地址挂同一条总线。NB-IoT 模组常见的有移远 BC26/BC35-G、中移 M5310-A以及国产的利尔达、有人物联网模组。选型主要看三件事是否有支持 MQTT 的 AT 固件、PSM 功耗参数能不能通过 AT 指令配置、串口波特率是否方便直接接 STM32 的 USART。大部分模组默认波特率 9600 或 115200在 STM32 初始化串口时先对上调试阶段别一上来就开流控。拿到模组后先接 USB 转串口在电脑上验证一遍网络注册指令会话大概是这样的AT OK ATCSQ CSQ: 22,99 OK ATCEREG? CEREG: 0,1 OK这里CSQ: 22,99表示信号强度 220-31越大越好第二个参数 99 表示误码率未知先不管。CEREG: 0,1里第二个 1 表示已注册上网络。这两条能过再谈连云。2.3 数据上腾讯云的三种路径MQTT 直连为什么是首选这里有个关键决定数据怎么从 NB-IoT 模组到达腾讯云。常见做法有三条路。路径组件优点缺点MQTT 直连模组内置 MQTT AT 固件 IoT Explorer链路最短平台可管理模组固件要支持 MQTTUDP 云网关模组 UDP 上报 自建云转发报文最省流量要自己维护服务器并适配运营商平台转发通过运营商物联网平台转接到腾讯云适合存量平台设备多一跳配置复杂我一般建议优先走 MQTT 直连因为它正好用上腾讯云 IoT Explorer 的设备管理、数据模板和规则引擎。设备端只要把 JSON 报文发到固定的 Topic平台自动解析并显示在控制台不用自己搭后端。对毕业设计和快速验证来说这是最稳妥的路径。后面章节的代码默认也是模组使用支持云平台接入的 MQTT AT 固件如果手上的模组固件不带 MQTT需要先用串口或 DFU 方式升级模组固件。3. 腾讯云 IoT Explorer 的产品创建与 MQTT 连接参数3.1 创建产品和设备时要确定的 4 个关键配置在控制台选“物联网开发平台 IoT Explorer”新建产品时有四个配置会影响后面代码的写法。配置项取值示例作用节点类型设备不是网关决定是否走子设备拓扑数据协议MQTT与模组 AT 指令对接认证方式密钥认证决定 MQTT 密码生成规则产品名称stm32-lux-sensor建议拼音或英文避免 URL 编码问题节点类型选“设备”不要选“网关”数据协议选“MQTT”认证方式选“密钥认证”。认证方式决定了设备侧最终连接时 MQTT 密码的生成规则这是最容易让新手卡住的地方下面专门讲。创建完产品后在设备列表里点“添加设备”会得到一个三元组产品 ID、设备名称、设备密钥。产品 ID 是一串字母数字设备名称是自己填的英文标识设备密钥是 24 位左右的密文这三样会被烧进 STM32 的代码里。提示设备密钥相当于设备在云端的登录口令调试日志里不要再打印出来调试完记得把相关打印关掉。3.2 设备三元组与 MQTT 连接参数的对应关系腾讯云 IoT Explorer 的 MQTT 连接参数不是随便填的。clientId 和 username 都是用产品 ID 加设备名拼接password 是对时间戳用设备密钥做 HMAC-SHA1 生成的签名。常见做法是先取当前 UTC 时间戳秒把设备密钥作为 HMAC 的 key时间戳作为消息内容得到签名后拼成“时间戳;签名”。用 Python 可以这样生成签名import hmac import hashlib import time device_secret 你的设备密钥 ts str(int(time.time())) sign hmac.new(device_secret.encode(), ts.encode(), hashlib.sha1).hexdigest() print(timestamp:, ts) print(password:, f{ts};{sign})这个脚本每次运行生成的都是新签名原因是 timestamp 直接参与 HMAC 计算云端用相同规则验签设备端时间偏差超过一定范围会连接失败。所以设备侧也要维护一个能拿到当前 Unix 时间戳的时钟源常见做法是上电时通过模组的取网络时间指令不同模组指令名不同如 ATQLTS同步一次。如果设备上没有 RTC 校准直接把固定签名烧进去只能在签名有效期内连接时间一长必然掉线重连失败。另外不同版本控制台上的调试工具会直接生成一份 MQTT 连接参数供参考。第一次调不通时把工具生成的参数和代码算出来的逐字符对比比反复猜快得多。3.3 Topic 设计属性上报与事件上报的差别IoT Explorer 的数据模板生成后会有两类 Topic属性上报$thing/up/property/{productId}/{deviceName}事件上报$thing/up/event/{productId}/{deviceName}。光照度建议用属性上报因为属性会持续记录可以直接在控制台看到实时曲线事件适合告警比如光照度持续低于阈值触发“光照不足”事件。上报的报文格式是 JSONmethod 取 reportparams 里写字段名和值。字段名必须和数据模板里的标识符一致。{method:report,clientToken:123456,params:{lux:852.3,rssi:-71}}clientToken 是用于透传去重的字符串可以用 STM32 的毫秒 tick 或自增计数代替。字段类型在数据模板里定义成浮点或整型上报的值会做类型校验类型不匹配会在平台侧解析失败这个在第 5 章排错部分还会看到。3.4 在腾讯云开发者后台确认设备在线在控制台的设备详情页可以看到“在线/离线”。设备上线意味着 MQTT 连接成功此时还没有任何业务数据只代表云平台这个产品下对应的设备完成了认证。如果设备显示在线但你发的属性没有更新去“设备调试”和“消息日志”页面查看上报记录消息日志里能看到原始报文、解析结果和失败原因。这里建议养成习惯每次改完代码先看消息日志再回来看设备是否在线不然容易被表面在线的假象带偏。4. STM32 HAL 库读取 BH1750 并通过 NB-IoT 模组上报腾讯云4.1 BH1750 的 I2C 读取与光照度换算用 CubeMX 生成工程时把 I2C1 设为 I2C 模式速率选 100kHz 或 400kHz 都行BH1750 最大支持 400kHz。下面这段用 HAL 库读取 BH1750 的代码比较常用// bh1750_driver.c #define BH1750_ADDR_W 0x46 // ADDR 接低电平时7位地址0x23左移1位得到写地址 #define BH1750_ADDR_R 0x47 #define BH1750_PWR_ON 0x01 // 上电指令 #define BH1750_CONT_H 0x10 // 连续高分辨率模式分辨率1lx uint16_t bh1750_read_lux(I2C_HandleTypeDef *hi2c) { uint8_t cmd BH1750_PWR_ON; HAL_I2C_Master_Transmit(hi2c, BH1750_ADDR_W, cmd, 1, 100); cmd BH1750_CONT_H; HAL_I2C_Master_Transmit(hi2c, BH1750_ADDR_W, cmd, 1, 100); HAL_Delay(180); // 高分辨率模式测量时间约120ms留足余量 uint8_t buf[2]; HAL_I2C_Master_Receive(hi2c, BH1750_ADDR_R, buf, 2, 100); return (uint16_t)((buf[0] 8) | buf[1]); }HAL_I2C_Master_Transmit 最后一个参数是超时时间 100ms超过会返回 HAL_TIMEOUT实际使用中最好检查返回值。测量结果是 16 位原始值连续高分辨率模式下要除以 1.2 得到 lux。很多网上代码把原始值直接当 lux 用会导致光照度整体偏大实际采集时建议拿照度计或手机 App 对比校准。4.2 NB-IoT 模组开机到连云的 AT 指令序列模组上电后先做网络注册再打开 MQTT 上下文最后连接腾讯云。以移远 BC35-G/BC26 这类模组的 AT 指令为例整个流程可以抽象成下面这张表阶段AT 指令预期返回说明自检ATOK串口通信正常开射频ATCFUN1OK打开射频功能附着网络ATCGATT1OK数据业务附着查询注册ATCEREG?CEREG: 0,1第二位为1表示已注册信号强度ATCSQCSQ: 25,99第一位越大越好99表示无信号打开连接ATQMTOPEN0,接入点域名,1883OK指向腾讯云 MQTT 接入点建立连接ATQMTCONN0,clientId,username,passwordOK三个参数按第 3 章规则生成连接成功后模组会返回QMTCONN: 0,0。失败时返回错误码常见的是 -3表示 MQTT 层连接被拒优先检查 password 签名和时间偏差。接入点域名在控制台“设备调试”页面能看到常见是iot-mqtts.pvt.tencent.com端口 1883。注意有些老固件要求填 IP 而不是域名遇到解析失败先看固件版本。4.3 在 STM32 里拼接 MQTT 报文并处理返回连接建立后上报一条属性报文只需要三步拼接 Topic拼接 JSON发送。代码如下// mqtt_report.c char topic[128]; char payload[160]; char at_cmd[256]; snprintf(topic, sizeof(topic), $thing/up/property/%s/%s, product_id, device_name); snprintf(payload, sizeof(payload), {\method\:\report\,\clientToken\:\s%lu\,\params\:{\lux\:%.1f,\rssi\:%d}}, (unsigned long)HAL_GetTick(), lux, rssi); snprintf(at_cmd, sizeof(at_cmd), ATQMTPUB0,0,1,0,\%s\,%d\r\n%s, topic, (int)strlen(payload), payload);QMTPUB 的第二个参数是 msgid填 0 让模组自动生成第三个参数 qos 填 1表示至少一次光照度上报丢一条可以接受但 qos 太低不利于排查问题第四个参数 retain 填 0云端不需要保留旧值。整条 AT 指令通过 USART 发给模组模组返回 OK 表示指令已接受返回QMTPUB: 0,0表示发布完成。注意不要在上一条 AT 响应返回前就发下一条实际代码里可以用状态机或串口中断标志位来串行化。4.4 用串口把上报链路完整打印出来调试阶段常见的做法是把 USART2 接 STM32 的 printf 重定向USART1 接 NB-IoT 模组两条串口分开避免 AT 响应和调试日志混在一起。串口调试时要打印的内容其实不多模组每次 URC 响应、QMTPUB 完成标志、本地的 lux 原始值和换算后的值。printf 重定向用半主机模式或者直接写 ITM 都可以MCU 资源紧张时优先用 ITM不占串口。光照度数据变化很快上一秒 800 lx 下一秒被手挡住变成 50 lx如果每次都上报会导致模组频繁发射功耗和流量都受影响。常见做法是加上“变化超过阈值才上报”的本地判断这条优化留到最后一章讲它比改 PSM 参数更容易被忽略。5. 从 SIM 卡到腾讯云日志排查光照度不上云的顺序5.1 第一步确认 NB-IoT 网络注册ATCEREG 和 ATCSQ上报失败先别急着改代码先确认网络层是否就绪。ATCEREG? 返回的第二位是注册状态0 未注册、1 已注册、2 正在注册、3 被拒绝、5 已注册但漫游。看到 3 或 100 之类的错误码优先怀疑 SIM 卡没有开通 NB-IoT 套餐或者模组频段和当地基站不匹配。CSQ 返回第一项是信号强度数值范围 0 到 3110 以下基本别指望稳定连接20 以上才算正常99 表示没读到信号。NB-IoT 测试最好的做法是准备一张已激活的测试卡先插到 USB 转串口模块上用电脑手动敲指令确认网络层和 MQTT 都能通再接 STM32。这样能快速切分是设备问题还是网络问题。提示给设备换过 SIM 卡后务必重新执行 ATCFUN1 和 ATCGATT1某些模组不会自动重新附着。5.2 第二步用 AT 指令自测 MQTT 与上报链路模组接电脑时还可以直接绕过 STM32 手动发一条测试报文。这个自测可以判定是模组侧 MQTT 问题还是 STM32 侧代码问题。常见的错误包括ATQMTCONN 返回错误码说明认证信息错了返回 -3 基本可以断定是 clientId/username/password 拼法问题。另一点容易踩的是 Topic 里的产品 ID 和控制台显示的产品名称不是一回事上报 Topic 里用的是产品 ID不要混用。发布时如果 QMTPUB 返回 ERROR先检查 Topic 字符串是否完整。很多嵌入式代码把 snprintf 的目标缓冲区设成 64 字节Topic 加 JSON 一长就截断了返回的 ERROR 信息很误导人。把缓冲区放大到 160 字节以上问题往往直接消失。5.3 第三步在腾讯云控制台消息日志里定位问题设备能连上但属性不更新十有八九是数据模板字段名或 JSON 格式不一致。控制台“消息日志”里会显示每条上行消息的解析结果解析失败时能看到字段级别的原因比如“lux 字段类型不匹配”或“找不到产品 ID”。这里有个使用习惯先在控制台“在线调试”发一条模拟数据确认平台解析正常再用设备发真实数据这样就能知道是平台模板配置问题还是设备报文问题。另外clientToken 在线调试里是必填项设备端用自增计数也能通过。若你看到消息日志里有数据但设备详情面板无曲线检查数据模板里有没有把 lux 的“展示”开关打开数据模板定义了字段但不展示平台上就不会画曲线这不是设备问题。5.4 常见坑芯片包、晶振电容与电平匹配刚接触 STM32 工程的人最容易卡在开发环境。Keil5 安装 STM32 芯片包时如果 Pack 版本和 CubeMX 生成的代码版本不一致编译会报各种奇怪的未定义错误。建议先装齐对应系列的 Device Pack再打开 CubeMX 生成的工程别用旧工程硬改芯片型号。如果你用的是国产替代芯片APM32 这类型号可以直接吃 STM32 的工程但记得在魔术棒里改一下 Device 选项否则烧录时报烧录算法错误。硬件上还有两个细节容易忽略。第一是晶振电容STM32 外部 8M 晶振的负载电容一般按晶振规格书选 18pF 到 22pF但走线电容和芯片引脚电容会改变实际谐振频率焊接后最好用示波器量一下系统时钟万用表量不出来错配会导致串口波特率偏差模组 AT 指令时通时不通。第二是 NB-IoT 模组的串口电平不少模组是 1.8V 电平STM32 的 USART 引脚默认 3.3V直接连接容易损坏模组或收不到数据最稳妥的做法是加电平转换芯片或者选用 3.3V 电平兼容的模组型号。如果调试时发现 HAL_Delay 偶尔卡死多半是 SysTick 中断优先级被改动或调用位置在中断上下文里把 I2C 和串口中断优先级理一遍再看。6. 把光照度采集系统的待机功耗压下来的两个落地技巧6.1 用 PSM 和 eDRX 控制 NB-IoT 射频的休眠时机NB-IoT 待机功耗的大头在射频不配置 PSM 时模组会保持监听几十毫安级别的电流一直放。PSMPower Saving Mode允许设备在空闲一段时间后进入深度休眠休眠期间网络侧认为设备不可达但已注册的信息保留醒来后不用重新附着可以直接发数据。常见配置是用 ATCPSMS 打开并指定 T3324活跃定时器和 T3412周期性注册更新定时器// T332460秒进入PSMT341230分钟唤醒一次做周期性更新 ATCPSMS1,,,00000101,00111000这两个值都用 8 位二进制表示定时器编码具体 bit 含义在模组 AT 手册里有表格不同模组微有差异。eDRX 则可以看成轻量休眠适合需要保持可达性的场景用 ATCEDRXS1 配置。光照度采集通常一小时上报一次PSM 比 eDRX 更合适因为上报间隙不需要接收下行。6.2 设备端加一层本地阈值过滤比调参更值钱STM32 上电后先读一次 BH1750光照度只有变化超过设定阈值才组装 MQTT 报文否则只更新本地缓存。阈值按场景设室内照明监控可以设 20 lx农业补光跟踪可以设当前值的 10%。判断代码很短但对流量和功耗影响很明显float new_lux bh1750_read_lux(hi2c1); if (fabsf(new_lux - last_lux) LUX_THRESHOLD) { report_to_tencent(new_lux); last_lux new_lux; }上报完后调用 STM32 的 HAL_PWR_EnterSTOPMode 进入停止模式并把 NB-IoT 模组设为 PSM 休眠定时器唤醒后再走一遍采集流程。这一套组合下来关键在“少发报文”和“早进 PSM”。可以先在本地用电流钳表量一下 PSM 状态的待机电流再逐步加阈值过滤往往能在一个上报周期内看到明显改善。本文还有配套的精品资源点击获取