100 篇文章精通 STM32F103(第 23 篇):NB-IoT 通信实战 —— 广域物联网数据上传

大家好!在前 22 篇中,我们用蓝牙 BLE 实现了短距离无线通信,但当设备需要跨区域、长距离传输数据(如户外气象站、远程水表)时,BLE 的 100 米通信范围完全无法满足需求。而NB-IoT(Narrow Band Internet of Things,窄带物联网) 作为广域物联网技术,依托运营商蜂窝网络(2G/4G/5G),可实现 “无距离限制” 的数据传输,且功耗仅略高于 BLE,是电池供电广域设备的首选。这一篇我们将从 NB-IoT 的核心原理讲起,详解 STM32 与 NB-IoT 模块(BC26)的通信配置,通过 “温湿度数据上传至阿里云 IoT 平台” 的实操,让你掌握 “本地采集→广域传输→云端监控” 的完整物联网开发流程。

一、什么是 NB-IoT?用 “手机信号” 理解核心逻辑

NB-IoT 是基于运营商蜂窝网络的低功耗广域通信技术,本质是 “专为物联网优化的窄带通信协议”,就像 “轻量化手机通信”:

设备端(STM32+NB-IoT 模块):像 “极简手机”,仅保留数据传输功能,不支持语音 / 视频,通过 SIM 卡接入运营商网络;网络端(运营商基站):像 “信号塔”,接收设备发送的数据,转发至互联网;云端(IoT 平台):像 “数据中心”,接收、存储、展示设备上传的数据(如阿里云、腾讯云),支持远程查看与控制;低功耗关键:采用 “PSM(省电模式)” 和 “eDRX(扩展不连续接收)” 技术 —— 设备上传数据后立即进入深度休眠,仅按预设周期(如 1 小时)唤醒联网,待机电流可低至 μA 级。

NB-IoT 与蓝牙 BLE 的核心差异

特性 NB-IoT(广域) 蓝牙 BLE(短距)
通信距离 无限制(依托运营商网络) 10~100 米(视距)
功耗 低(PSM 模式≈5μA,传输≈50mA) 极低(休眠≈10μA,传输≈10mA)
网络依赖 需运营商 SIM 卡与基站信号 无需网络,直接点对点
数据速率 极低(上行最高 62.5kbps) 低(最高 2Mbps)
适用场景 远程监控(如农田、管网) 近距离交互(如手机连传感器)

二、NB-IoT 通信的核心流程:从设备到云端

NB-IoT 设备上传数据的完整流程分为 “联网→认证→数据上传→休眠” 四步,核心依赖 “AT 指令” 控制模块,关键概念如下:

1. 核心组件与依赖

NB-IoT 模块:如移远 BC26,集成基带与射频,通过 UART 与 STM32 通信,需插入运营商 NB-IoT 专用 SIM 卡(支持 NB 网络,年费低至几元);IoT 云平台:如阿里云 IoT、华为云 IoT,提供设备接入、数据存储、可视化界面,需为设备创建 “产品” 与 “设备”,获取唯一 “设备三元组”(ProductKey、DeviceName、DeviceSecret)用于身份认证;通信协议:NB-IoT 设备与云端通常采用 “MQTT 协议”(轻量级、低带宽),模块内置 MQTT 客户端,无需 STM32 手动实现复杂协议栈。

2. 核心通信流程(以 BC26 + 阿里云为例)

模块上电初始化:STM32 通过 UART 发送 AT 指令,初始化 BC26(如设置频段、检查 SIM 卡);接入运营商网络:BC26 搜索并注册到 NB-IoT 网络,获取 IP 地址(需确保有基站信号);云端身份认证:使用设备三元组,通过 MQTT 协议连接阿里云 IoT 平台,认证通过后建立稳定连接;数据上传:STM32 将采集的温湿度数据封装为 JSON 格式,通过 AT 指令发送给 BC26,模块转发至云端;进入低功耗模式:上传完成后,BC26 进入 PSM 模式,STM32 进入停止模式,按预设周期(如 1 小时)唤醒重复流程。

三、实操:STM32+BC26 实现温湿度上传阿里云 IoT

我们基于第 22 篇的低功耗框架,替换 BLE 模块为 NB-IoT 模块(BC26),实现功能:

STM32 采集温湿度数据,通过 UART 控制 BC26 接入 NB 网络;BC26 连接阿里云 IoT 平台,完成身份认证;将温湿度数据以 JSON 格式上传至云端;上传完成后,BC26 进入 PSM 模式,STM32 进入停止模式,1 小时后唤醒重复流程。

1. 硬件准备与连接

硬件 型号 / 规格 连接方式(STM32→NB 模块) 作用
STM32F103C8T6 主控制器,采集温湿度
NB-IoT 模块 移远 BC26(UART 接口) TX→PA10(USART1_RX),RX→PA9(USART1_TX),VCC→3.3V,GND→GND,PWR_KEY→PB0 广域数据传输,需插 NB SIM 卡
温湿度传感器 SHT30(I2C) SCL→PB6,SDA→PB7 采集温湿度数据
锂电池 18650(3.7V/2000mAh) 经 LDO 稳压至 3.3V,供电给 STM32 与 BC26 续航更长,适合户外使用

2. 云端准备(阿里云 IoT 平台配置)

创建产品与设备

登录阿里云 IoT 平台,进入 “设备管理→产品”,创建 “自定义产品”,通信方式选择 “NB-IoT”,数据格式选择 “JSON”;进入产品详情,点击 “设备→添加设备”,创建一个设备,记录生成的 “设备三元组”(ProductKey、DeviceName、DeviceSecret),用于模块认证。

创建数据解析模板(可选):

在产品详情的 “数据解析” 中,创建解析脚本,将设备上传的 JSON 数据(如
{"temp":25.5,"humi":60.2}
)映射为平台可识别的 “温度”“湿度” 字段,便于后续可视化。

3. STM32 配置步骤(STM32CubeIDE)

步骤 1:配置 USART1(与 BC26 通信)

模式:异步模式,波特率 9600(BC26 默认波特率),8N1,引脚 PA9(TX)、PA10(RX);使能 USART 接收中断(用于接收 BC26 的 AT 指令响应,避免轮询占用 CPU)。

步骤 2:配置低功耗与定时唤醒

保留 RTC 定时唤醒配置,将唤醒周期改为 3600 秒(1 小时,减少联网次数降低功耗);保留 STM32 停止模式配置,确保休眠时功耗≈50μA。

步骤 3:生成代码

点击 “Generate Code”,自动生成 USART1、RTC、GPIO 的初始化代码。

4. 编写 NB-IoT 通信代码(核心:AT 指令控制 BC26)

(1)BC26 初始化与联网函数(nb_iot.c)



#include "nb_iot.h"
#include "usart.h"
#include "string.h"
#include "delay.h"
 
extern UART_HandleTypeDef huart1;
char uart_rx_buf[256]; // USART接收缓冲区
uint16_t uart_rx_len = 0;
 
// 发送AT指令并等待响应(timeout:超时时间ms)
uint8_t NB_Send_AT_CMD(char *cmd, char *resp, uint32_t timeout)
{
  memset(uart_rx_buf, 0, sizeof(uart_rx_buf));
  uart_rx_len = 0;
 
  // 发送AT指令
  HAL_UART_Transmit(&huart1, (uint8_t*)cmd, strlen(cmd), 100);
  HAL_UART_Transmit(&huart1, (uint8_t*)"
", 2, 100);
 
  // 等待响应
  uint32_t start_time = HAL_GetTick();
  while (HAL_GetTick() - start_time < timeout)
  {
    if (strstr(uart_rx_buf, resp) != NULL)
    {
      return 0; // 收到预期响应,成功
    }
  }
  return 1; // 超时,失败
}
 
// BC26初始化:检查SIM卡、注册网络
uint8_t BC26_Init(void)
{
  // 1. 模块上电(拉低PWR_KEY引脚1秒,触发开机)
  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);
  HAL_Delay(1000);
  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET);
  HAL_Delay(5000); // 等待模块启动
 
  // 2. 检查模块是否响应(发送AT,预期响应OK)
  if (NB_Send_AT_CMD("AT", "OK", 3000) != 0)
  {
    printf("BC26模块无响应!
");
    return 1;
  }
 
  // 3. 检查SIM卡状态(预期响应SIM OK)
  if (NB_Send_AT_CMD("AT+CPIN?", "SIM OK", 5000) != 0)
  {
    printf("SIM卡异常!
");
    return 1;
  }
 
  // 4. 注册NB网络(预期响应+CGREG: 0,1或0,5,表示注册成功)
  if (NB_Send_AT_CMD("AT+CGREG?", "+CGREG: 0,1", 30000) != 0 && 
      NB_Send_AT_CMD("AT+CGREG?", "+CGREG: 0,5", 10000) != 0)
  {
    printf("NB网络注册失败!
");
    return 1;
  }
  printf("NB网络注册成功!
");
  return 0;
}
 
// 连接阿里云IoT平台(使用设备三元组)
uint8_t BC26_Connect_AliIoT(char *product_key, char *device_name, char *device_secret)
{
  char cmd[128];
 
  // 1. 设置MQTT服务器地址(阿里云IoT地址:${product_key}.iot-as-mqtt.cn-shanghai.aliyuncs.com:1883)
  sprintf(cmd, "AT+QMTCFG="broker",0,"%s.iot-as-mqtt.cn-shanghai.aliyuncs.com",1883,0,0", product_key);
  if (NB_Send_AT_CMD(cmd, "OK", 5000) != 0) return 1;
 
  // 2. 设置设备认证信息(ClientID格式:${device_name}|securemode=3,signmethod=hmacsha1|)
  sprintf(cmd, "AT+QMTCFG="clientid",0,"%s|securemode=3,signmethod=hmacsha1|"", device_name);
  if (NB_Send_AT_CMD(cmd, "OK", 5000) != 0) return 1;
 
  // 3. 计算MQTT密码(需提前通过阿里云算法生成,此处简化用示例密码)
  char password[] = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"; // 实际项目需动态计算
 
  // 4. 连接MQTT服务器(用户名:${device_name}&${product_key},密码:上述password)
  sprintf(cmd, "AT+QMTCONN=0,"%s&%s","%s"", device_name, product_key, password);
  if (NB_Send_AT_CMD(cmd, "+QMTCONN: 0,0,0", 10000) != 0) // 响应0,0,0表示连接成功
  {
    printf("阿里云连接失败!
");
    return 1;
  }
  printf("阿里云连接成功!
");
  return 0;
}
 
// 上传温湿度数据到阿里云(topic:/sys/${product_key}/${device_name}/thing/event/property/post)
uint8_t BC26_Upload_Data(char *product_key, char *device_name, float temp, float humi)
{
  char cmd[256];
  char topic[128];
  char payload[64];
 
  // 1. 构建阿里云数据上传topic
  sprintf(topic, "/sys/%s/%s/thing/event/property/post", product_key, device_name);
 
  // 2. 构建JSON payload(符合阿里云物模型格式)
  sprintf(payload, "{"params":{"temp":%.1f,"humi":%.1f}}", temp, humi);
 
  // 3. 发送MQTT消息(AT+QMTPUB=客户端号,消息ID,QoS,是否保留,topic, payload长度,payload)
  sprintf(cmd, "AT+QMTPUB=0,1,0,0,"%s",%d,"%s"", topic, strlen(payload), payload);
  if (NB_Send_AT_CMD(cmd, "+QMTPUB: 0,1,0", 10000) != 0) // 响应0,1,0表示发送成功
  {
    printf("数据上传失败!
");
    return 1;
  }
  printf("数据上传成功:%s
", payload);
  return 0;
}
 
// BC26进入PSM模式(低功耗)
void BC26_Enter_PSM(void)
{
  // 发送AT指令设置PSM参数,上传完成后立即进入PSM
  NB_Send_AT_CMD("AT+CPSMS=1,,,0,3600", "OK", 5000); // 3600秒后唤醒
  NB_Send_AT_CMD("AT+QPOWD=1", "OK", 3000); // 触发PSM休眠
}
 
// USART1接收中断回调(存储BC26响应)
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
  if (huart->Instance == USART1)
  {
    uart_rx_buf[uart_rx_len++] = huart->Instance->DR;
    if (uart_rx_len >= sizeof(uart_rx_buf)-1) uart_rx_len = 0;
    // 重新启动接收中断
    HAL_UART_Receive_IT(&huart1, (uint8_t*)&uart_rx_buf[uart_rx_len], 1);
  }
}
(2)主程序逻辑(main.c)



#include "main.h"
#include "nb_iot.h"
#include "sht30.h"
#include "rtc.h"
#include "power.h"
 
// 阿里云设备三元组(替换为自己的实际信息)
char PRODUCT_KEY[] = "a1XXXXXXXXXX";
char DEVICE_NAME[] = "STM32_NB_Device";
char DEVICE_SECRET[] = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
 
float temp, humi;
 
int main(void)
{
  HAL_Init();
  SystemClock_Config();
 
  // 初始化外设
  MX_GPIO_Init();
  MX_USART1_Init();
  MX_I2C1_Init();
  MX_RTC_Init();
 
  // 启动USART1接收中断
  HAL_UART_Receive_IT(&huart1, (uint8_t*)uart_rx_buf, 1);
 
  // 初始化RTC(1小时唤醒一次)
  RTC_Alarm_Init(3600);
 
  while (1)
  {
    // 1. 初始化BC26并连接NB网络
    if (BC26_Init() != 0)
    {
      goto LOW_POWER; // 初始化失败,直接进入低功耗
    }
 
    // 2. 连接阿里云IoT平台
    if (BC26_Connect_AliIoT(PRODUCT_KEY, DEVICE_NAME, DEVICE_SECRET) != 0)
    {
      goto LOW_POWER;
    }
 
    // 3. 采集温湿度数据
    SHT30_Read_Data(&temp, &humi);
    printf("采集数据:Temp=%.1f℃, Humi=%.1f%%
", temp, humi);
 
    // 4. 上传数据到阿里云
    BC26_Upload_Data(PRODUCT_KEY, DEVICE_NAME, temp, humi);
 
  LOW_POWER:
    // 5. 进入低功耗:BC26 PSM + STM32停止模式
    BC26_Enter_PSM();
    Enter_Stop_Mode(); // 1小时后被RTC唤醒,重复循环
  }
}

5. 云端验证(阿里云 IoT 平台)

登录阿里云 IoT 平台,进入 “设备管理→设备”,找到已创建的设备,查看 “运行状态”;设备上传数据后,在 “物模型数据” 页面可实时看到 “温度”“湿度” 字段的最新值;进入 “数据日志” 页面,可查看历史上传记录,验证数据传输的连续性与准确性。

6. 功耗与续航验证

工作阶段(1 小时内的 60 秒):BC26 开机 + 联网 + 上传,功耗≈50mA(持续 60 秒);低功耗阶段(1 小时内的 3540 秒):BC26 PSM 模式(≈5μA)+ STM32 停止模式(≈50μA),总功耗≈55μA;平均功耗:≈(50mA×60s + 55μA×3540s)/3600s ≈ 0.84mA;续航估算:18650 锂电池(2000mAh)续航≈2000mAh / 0.84mA ≈ 2381 小时 ≈ 99 天(满足户外设备 3 个月续航需求)。

四、NB-IoT 通信常见问题与调试技巧

模块无响应(AT 指令不回 OK)?

供电不足:BC26 启动时峰值电流达 200mA,需确保电源(如锂电池 + LDO)能提供足够电流,避免电压跌落;PWR_KEY 引脚操作错误:开机需拉低 PWR_KEY 1 秒以上,若仅拉低几十 ms,模块无法正常启动;波特率不匹配:BC26 默认波特率 9600,若修改过需重新配置 STM32 的 USART 波特率(可通过硬件复位模块恢复默认)。

NB 网络注册失败(+CGREG: 0,2)?

SIM 卡问题:确认是 NB-IoT 专用 SIM 卡,且已激活、有流量(可插入手机查看是否有信号);信号问题:户外或靠近窗户测试,室内信号弱可能导致注册失败;可通过 AT 指令查看信号强度(AT+CSQ,返回值≥10 表示信号良好);频段不匹配:不同地区运营商 NB 频段不同(如中国电信常用 B5/B8),需通过 AT 指令设置正确频段(AT+QCFG=”band”,0,80000000,1,开启 B5/B8)。

阿里云连接失败(+QMTCONN: 0,1,4)?

三元组错误:确认 ProductKey、DeviceName、DeviceSecret 与阿里云平台完全一致(区分大小写,无空格);密码计算错误:阿里云 MQTT 密码需通过 “device_name+product_key+device_secret” 按 hmacsha1 算法生成,不可硬编码示例值(实际项目需在 STM32 中实现算法);服务器地址错误:确认 MQTT 服务器地址格式为 “${product_key}.iot-as-mqtt.cn-shanghai.aliyuncs.com”,端口 1883。

五、第 23 篇总结与下一篇预告

总结

这一篇我们掌握了 NB-IoT 广域通信的核心:

NB-IoT 依托运营商网络实现无距离限制传输,通过 PSM 模式保障低功耗,适合户外远程设备;开发关键是通过 AT 指令控制 NB 模块(BC26)完成 “联网→认证→上传”,无需手动实现复杂协议;实操中实现了 “本地采集→云端上传→远程监控” 的完整物联网链路,平均功耗≈0.84mA,续航达 3 个月,满足户外设备需求。

© 版权声明
THE END
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容