news 2026/9/10 13:13:37

STM32+RM500U+AHT20温湿度上云实战:工业级可靠通信与TCP数据上报

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
STM32+RM500U+AHT20温湿度上云实战:工业级可靠通信与TCP数据上报

简介:这是一套面向嵌入式物联网开发者的STM32实战项目资源,聚焦5G通信与环境传感融合应用,适用于具备C语言基础和HAL库开发经验的中级单片机学习者及工程师。项目以移远RM500U 5G模块为核心,完整实现AHT20温湿度数据采集、TCP协议封装及上云传输全流程,覆盖硬件接线定义、KEIL工程配置(含J-Link/ST-Link适配说明)、FLASH容量适配要点及跨传感器扩展指引。压缩包共962个文件,主体为565个C源码与240个头文件,辅以启动汇编、链接脚本、调试配置及编译中间产物,结构完整、注释详实,便于理解底层驱动与网络协议栈集成逻辑。资源大小13.14MB,目录中可见多版本数学库(如iar_cortexM0l_math.a)、G070CB系列工程文件及重置编译脚本,体现工程可移植性设计。目前已有738人学习下载,提供从代码阅读、硬件联调到私有云对接的全链路参考实现。

1. 为什么用 STM32 + RM500U 做 AHT20 温湿度上云,不能只靠“能连”就完事?

很多工程师拿到“STM32 控制 AHT20,通过 RM500U 发 TCP 数据到私有云”这个需求时,第一反应是:AHT20 I²C 读得出来、RM500U AT 指令发得通、TCP socket 能 connect 上——任务就算完成了。但真实项目里,90% 的失败不是卡在“连不上”,而是卡在数据断续、模块掉线后不自恢复、温湿度值跳变却查不出源头、私有云平台收不到完整 JSON 包、甚至 STM32 在持续发送时突然 halt。这不是单个器件的问题,而是三者耦合后的时序链断裂:AHT20 的 100ms 测量周期与 RM500U 的 PSM 省电模式冲突;TCP 连接建立耗时(尤其首次附着 5G 网络)与 STM32 主循环调度失配;私有云要求的 Keep-Alive 心跳间隔与模块 UART 接收缓冲区溢出风险叠加。本方案聚焦可落地的工业级实践——不依赖开发板例程,不假设网络环境理想,所有代码基于 STM32F407VG(主流高性能型号)、HAL 库 v1.26.0、RM500U 固件版本 RM500U_V1.0.0.001_BETA(2023Q3 最稳定版),全程规避常见“AT 指令发了但没等响应”“I²C 地址写错导致 AHT20 返回 0x0000”“TCP send() 后未检查返回值直接清空缓冲区”三类致命误操作。

2. AHT20 与 STM32 的可靠通信:从硬件连接到校验级驱动实现

AHT20 是一款高精度数字温湿度传感器,采用标准 I²C 接口,但其初始化流程和数据解析逻辑比普通传感器更严格。若仅按“写地址+读寄存器”方式调用 HAL_I2C_Master_Transmit/Receive,极易因时序偏差或状态未确认导致后续读数全为 0xFF 或固定值。必须分步控制初始化状态机,并对每次 I²C 传输做超时与 ACK 校验。

2.1 硬件连接与关键参数配置

AHT20 工作电压为 1.8V–3.3V,严禁直连 STM32 的 5V IO。推荐接法如下:

AHT20 引脚STM32F407VG 引脚说明
VDD3.3V(非 USB 5V)必须经 LDO 或 MCU 内部稳压输出
GNDGND共地,避免地环路
SDAPB7(I²C1_SDA)需外接 4.7kΩ 上拉至 3.3V
SCLPB6(I²C1_SCL)同样需 4.7kΩ 上拉
INT(可选)任意 GPIO(如 PA0)用于触发测量完成中断,本方案暂不用

提示:AHT20 的 I²C 地址固定为0x38(7 位),无地址选择引脚。若用逻辑分析仪抓到 SDA/SCL 波形但无 ACK,优先检查上拉电阻是否缺失或阻值过大(>10kΩ 易导致上升沿过缓)。

2.2 初始化与测量状态机实现

AHT20 初始化需严格遵循三步:复位 → 触发初始化 → 等待 BUSY 清零。任何一步跳过或超时未处理,都将使后续读数失效。以下为精简但完整的 HAL 驱动核心逻辑(C 语言):

// aht20_driver.c #include "aht20_driver.h" #include "stm32f4xx_hal.h" #define AHT20_ADDR 0x38U #define AHT20_CMD_RESET 0xBAU #define AHT20_CMD_INIT 0xBEU #define AHT20_CMD_MEAS 0xACU static uint8_t aht20_status = AHT20_STATUS_UNINIT; HAL_StatusTypeDef AHT20_Init(I2C_HandleTypeDef *hi2c) { uint8_t cmd = AHT20_CMD_RESET; HAL_StatusTypeDef ret; // Step 1: Send reset command ret = HAL_I2C_Master_Transmit(hi2c, AHT20_ADDR << 1, &cmd, 1, 100); if (ret != HAL_OK) return ret; HAL_Delay(20); // Wait for reset complete // Step 2: Send init command with CRC enable uint8_t init_cmd[3] = {AHT20_CMD_INIT, 0x08, 0x00}; // 0x08 = enable CRC ret = HAL_I2C_Master_Transmit(hi2c, AHT20_ADDR << 1, init_cmd, 3, 100); if (ret != HAL_OK) return ret; // Step 3: Poll status until BUSY bit clears (bit7 = 0) uint8_t status; for (int i = 0; i < 100; i++) { // Max 100ms timeout ret = HAL_I2C_Master_Receive(hi2c, AHT20_ADDR << 1, &status, 1, 10); if (ret == HAL_OK && (status & 0x80) == 0) { aht20_status = AHT20_STATUS_READY; return HAL_OK; } HAL_Delay(1); } return HAL_TIMEOUT; }
2.2.1 关键参数说明与调试要点
  • HAL_I2C_Master_Transmit的 timeout 参数设为100(单位 ms),而非默认1000,避免主循环被长期阻塞;
  • 初始化命令init_cmd[3]中第二字节0x08表示启用 CRC 校验,若此处写成0x00,AHT20 将拒绝后续所有测量请求
  • 状态轮询使用HAL_I2C_Master_Receive读取单字节状态寄存器,不可用HAL_I2C_IsDeviceReady()替代,因其无法识别 AHT20 特有的 BUSY 位;
  • AHT20_Init()返回HAL_TIMEOUT,需用示波器确认 SCL 是否被拉低(I²C 总线死锁),此时应执行HAL_I2C_DeInit()+HAL_I2C_Init()复位总线。

2.3 温湿度数据读取与 CRC 校验

AHT20 测量命令发出后,需等待 80ms 以上再读取数据。返回 6 字节:[status, data0, data1, data2, data3, CRC]。其中data0~data3构成 32-bit 原始值,需按公式解码:

  • 湿度 =(data0 << 12) | (data1 << 4) | (data2 >> 4)→ 除以0x100000得 0–100% RH
  • 温度 =((data2 & 0x0F) << 16) | (data3 << 8) | data4→ 除以0x100000得 -40–85°C

CRC 校验必须启用(初始化时已设置),否则数据可信度归零:

uint8_t AHT20_CalcCRC(uint8_t *data, uint8_t len) { uint8_t crc = 0xFF; for (uint8_t i = 0; i < len; i++) { crc ^= data[i]; for (uint8_t j = 0; j < 8; j++) { if (crc & 0x80) crc = (crc << 1) ^ 0x31; else crc <<= 1; } } return crc; } HAL_StatusTypeDef AHT20_ReadData(I2C_HandleTypeDef *hi2c, float *temp, float *humi) { uint8_t cmd = AHT20_CMD_MEAS; uint8_t rx_buf[6]; // Trigger measurement if (HAL_I2C_Master_Transmit(hi2c, AHT20_ADDR << 1, &cmd, 1, 100) != HAL_OK) return HAL_ERROR; HAL_Delay(80); // Must wait ≥80ms // Read 6 bytes if (HAL_I2C_Master_Receive(hi2c, AHT20_ADDR << 1, rx_buf, 6, 100) != HAL_OK) return HAL_ERROR; // Verify CRC if (rx_buf[5] != AHT20_CalcCRC(rx_buf, 5)) return HAL_ERROR; // CRC mismatch → discard data uint32_t raw_humi = ((uint32_t)rx_buf[1] << 12) | ((uint32_t)rx_buf[2] << 4) | (rx_buf[3] >> 4); uint32_t raw_temp = (((uint32_t)rx_buf[3] & 0x0F) << 16) | ((uint32_t)rx_buf[4] << 8) | rx_buf[5]; *humi = (float)raw_humi * 100.0f / 1048576.0f; // 2^20 = 1048576 *temp = (float)raw_temp * 200.0f / 1048576.0f - 40.0f; return HAL_OK; }

注意:rx_buf[5]是 CRC 字节,但AHT20_CalcCRC()输入长度为5(即rx_buf[0]rx_buf[4]),若传入6将导致校验永远失败。这是 AHT20 协议文档明确规定的校验范围。

3. RM500U 5G 模块接入与 TCP 连接管理:从 AT 指令序列到状态机设计

RM500U 是移远推出的 LTE/5G 双模模块,支持 NSA/SA 两种 5G 组网方式。其 AT 指令集庞大,但实际项目中只需掌握网络附着、APN 配置、TCP 连接、数据透传四类指令。盲目堆砌AT+CGDCONT,AT+CGACT,AT+QIACT等指令而不做状态反馈校验,会导致模块看似“已联网”实则无法发包。

3.1 硬件连接与串口初始化关键点

RM500U 使用 UART3(PA10/PA11)与 STM32 通信,波特率默认115200,但首次上电必须用AT+IPR=115200固定波特率,否则模块可能因供电波动恢复出厂波特率9600。硬件连接需注意:

RM500U 引脚STM32F407VG 引脚说明
VCC3.3V(带 2A 限流)模块峰值电流达 1.5A,需独立 LDO(如 TPS54302)
GNDGND与 STM32 共地,且建议单点接地
TXDPA10(UART3_RX)电平兼容 3.3V,无需电平转换
RXDPA11(UART3_TX)同上
DTR任意 GPIO(如 PC13)用于模块唤醒/休眠控制
RESET任意 GPIO(如 PC14)硬复位引脚,低电平有效

提示:RM500U 的VCC不能由 STM32 的 3.3V 电源直接供电!必须使用外部 DC-DC 模块,否则模块在 5G 上传时电压跌落将触发异常重启。

3.2 网络附着与 APN 配置的原子化指令序列

以下为最小可行 AT 指令序列(按顺序执行,每条后必须等待OKERROR响应):

AT+CFUN=0 # 关闭射频功能(安全起点) AT+CPIN? # 检查 SIM 卡状态,返回 +CPIN: READY 才继续 AT+CGDCONT=1,"IP","CMNET" # 设置 APN(中国移动通用 APN) AT+CGACT=1,1 # 激活 PDP 上下文 AT+CFUN=1 # 开启射频功能 AT+QIACT # 激活默认 bearer(返回 +QIACT: 1,"10.123.45.67",... 表示成功)
3.2.1 每条指令的超时与响应解析逻辑

在 STM32 中,不能简单用HAL_UART_Transmit发送后HAL_UART_Receive读一次就结束。必须实现带超时的响应解析器:

typedef enum { AT_RESP_OK, AT_RESP_ERROR, AT_RESP_TIMEOUT, AT_RESP_UNKNOWN } at_response_t; at_response_t AT_SendCommand(UART_HandleTypeDef *huart, const char *cmd, uint32_t timeout_ms) { char rx_buffer[64] = {0}; uint32_t start_tick = HAL_GetTick(); HAL_UART_Transmit(huart, (uint8_t*)cmd, strlen(cmd), 100); HAL_UART_Transmit(huart, (uint8_t*)"\r\n", 2, 100); // Wait for response up to timeout_ms while (HAL_GetTick() - start_tick < timeout_ms) { if (HAL_UART_Receive(huart, (uint8_t*)rx_buffer, sizeof(rx_buffer)-1, 10) == HAL_OK) { rx_buffer[sizeof(rx_buffer)-1] = '\0'; if (strstr(rx_buffer, "OK") != NULL) return AT_RESP_OK; if (strstr(rx_buffer, "ERROR") != NULL) return AT_RESP_ERROR; if (strstr(rx_buffer, "+QIACT:") != NULL) return AT_RESP_OK; // Special case for QIACT } } return AT_RESP_TIMEOUT; }
  • timeout_ms对不同指令差异化设置:AT+CPIN?设为500AT+QIACT设为30000(5G 附着最长需 25s);
  • +QIACT:响应含 IP 地址,需额外解析提取(本方案暂不需,但调试时可用sscanf(rx_buffer, "+QIACT: %*d,\"%15[^\",]\"", ip_str));
  • AT+QIACT返回+QIACT: 0,表示 bearer 未激活,需检查AT+CGACT=1,1是否成功。

3.3 TCP 连接与数据发送的状态机实现

RM500U 支持AT+QTCP(透传模式)和AT+QSEND(非透传模式)。工业场景强烈推荐非透传模式,因其可精确控制每包数据长度、获取发送结果、避免粘包。连接流程为:

  1. AT+QTCP=1→ 创建 TCP client 实例
  2. AT+QIOPEN=1,"TCP","192.168.1.100",8080→ 连接私有云服务器
  3. AT+QISEND=1,32→ 准备发送 32 字节数据
  4. 发送实际数据(含\r\n结尾)
  5. 检查+QISEND: 1,32表示发送成功
typedef struct { uint8_t state; // 0=idle, 1=connecting, 2=connected, 3=sending uint32_t last_conn_time; } rm500u_ctx_t; rm500u_ctx_t rm500u; HAL_StatusTypeDef RM500U_ConnectTCP(const char *ip, uint16_t port) { char cmd[64]; sprintf(cmd, "AT+QIOPEN=1,\"TCP\",\"%s\",%d\r\n", ip, port); if (AT_SendCommand(&huart3, cmd, 30000) != AT_RESP_OK) { rm500u.state = 0; return HAL_ERROR; } // Parse +QIOPEN: 1,0 for success (0 = success code) char resp[128]; if (HAL_UART_Receive(&huart3, (uint8_t*)resp, sizeof(resp)-1, 1000) == HAL_OK) { if (strstr(resp, "+QIOPEN: 1,0") != NULL) { rm500u.state = 2; rm500u.last_conn_time = HAL_GetTick(); return HAL_OK; } } return HAL_ERROR; } HAL_StatusTypeDef RM500U_SendData(const uint8_t *data, uint16_t len) { char cmd[32]; sprintf(cmd, "AT+QISEND=1,%d\r\n", len); if (AT_SendCommand(&huart3, cmd, 1000) != AT_RESP_OK) return HAL_ERROR; // Send actual data, must end with Ctrl+Z (0x1A) HAL_UART_Transmit(&huart3, data, len, 1000); HAL_UART_Transmit(&huart3, (uint8_t*)"\x1A", 1, 1000); // Ctrl+Z to terminate // Wait for +QISEND: 1,len or ERROR char resp[64]; if (HAL_UART_Receive(&huart3, (uint8_t*)resp, sizeof(resp)-1, 2000) == HAL_OK) { char expected[32]; sprintf(expected, "+QISEND: 1,%d", len); if (strstr(resp, expected) != NULL) return HAL_OK; } return HAL_ERROR; }
3.3.1 TCP 连接保活与异常重连策略

私有云平台通常要求 30–60s 心跳包。若单纯定时发心跳,网络瞬断时AT+QISEND会阻塞超时。必须加入连接状态检测:

检测项方法阈值动作
连接存活发送AT+QISTAT查询 socket 状态返回+QISTAT: 1,1表示已连接正常发心跳
连接丢失AT+QISTAT返回+QISTAT: 1,0或超时执行AT+QICLOSE=1+ 重连
模块无响应连续 3 次AT指令超时AT+QRST=1硬复位模块

此逻辑需嵌入主循环,而非依赖HAL_Delay()定时。

4. 私有云平台数据格式与 STM32 端 JSON 构造:轻量级、可验证、免 malloc

私有云平台接收的数据格式通常为 HTTP POST JSON 或裸 TCP JSON。无论哪种,STM32 上构造 JSON 必须避免动态内存分配(malloc/free),因其在裸机环境下易引发碎片和崩溃。本方案采用预分配静态缓冲区 + 格式化字符串方式,确保 100% 可控。

4.1 私有云平台典型接收协议解析

以主流私有云 MQTT over TCP 或 HTTP RESTful 接口为例,常见要求如下:

  • Topic/Path/v1/device/{device_id}/sensor
  • Payload 格式{"device_id":"RM500U-001","timestamp":1717023456,"temperature":23.45,"humidity":45.67,"battery_mv":3280}
  • 字段约束device_id≤ 32 字符,timestamp为 Unix 时间戳(秒级),temperature/humidity保留两位小数,battery_mv为整数

注意:“TCP 传数据到私有云平台” 不等于直接发原始二进制。99% 的私有云平台(包括 ThingsBoard、EMQX 自建、阿里云 IoT 平台)均要求结构化文本载荷。若平台真收二进制,需额外协商协议头(如 4 字节长度 + 2 字节 CRC),但标题未提及,故按 JSON 处理。

4.2 静态 JSON 缓冲区构造与格式化

定义最大长度为 256 字节的静态缓冲区(覆盖所有字段 + 安全余量):

#define JSON_BUF_SIZE 256 static char json_buffer[JSON_BUF_SIZE]; // Format timestamp as seconds since epoch uint32_t get_unix_timestamp(void) { // 若无 RTC,可用 HAL_GetTick() / 1000 + 系统启动偏移 return HAL_GetTick() / 1000 + 1717020000U; // 示例:2024-05-30 00:00:00 UTC } HAL_StatusTypeDef BuildSensorJSON(const char *device_id, float temp, float humi, uint16_t bat_mv) { int len = snprintf(json_buffer, JSON_BUF_SIZE, "{\"device_id\":\"%s\",\"timestamp\":%lu,\"temperature\":%.2f,\"humidity\":%.2f,\"battery_mv\":%u}", device_id, (unsigned long)get_unix_timestamp(), (double)temp, (double)humi, (unsigned int)bat_mv ); if (len <= 0 || len >= JSON_BUF_SIZE) { json_buffer[0] = '\0'; return HAL_ERROR; } // Ensure null termination json_buffer[JSON_BUF_SIZE-1] = '\0'; return HAL_OK; }
4.2.1 关键安全机制与边界检查
  • snprintf()返回值len必须检查:若len >= JSON_BUF_SIZE,说明缓冲区不足,绝不能截断发送,应丢弃本次数据并记录错误;
  • %.2f格式化在 ARM Cortex-M4 上由newlib-nano提供,需在Project → Options → C/C++ → Define中添加__NEWLIB__,否则编译报错;
  • device_id若来自模块 IMEI(AT+CIMI获取),需先截取前 15 位并补-001后缀,避免超长。

4.3 完整数据上报主流程整合

将 AHT20 读取、JSON 构造、RM500U 发送串联为原子操作:

void SensorUploadTask(void const * argument) { float temperature, humidity; uint16_t battery_mv = 3300; // 示例:ADC 采样值换算 for(;;) { // Step 1: Read sensor (with retry) if (AHT20_ReadData(&hi2c1, &temperature, &humidity) != HAL_OK) { HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13); // Error LED blink osDelay(1000); continue; } // Step 2: Build JSON if (BuildSensorJSON("RM500U-001", temperature, humidity, battery_mv) != HAL_OK) { osDelay(1000); continue; } // Step 3: Ensure TCP connected if (rm500u.state != 2) { if (RM500U_ConnectTCP("192.168.1.100", 8080) != HAL_OK) { osDelay(5000); // Backoff before retry continue; } } // Step 4: Send data if (RM500U_SendData((uint8_t*)json_buffer, strlen(json_buffer)) != HAL_OK) { // Connection lost - close and retry AT_SendCommand(&huart3, "AT+QICLOSE=1\r\n", 1000); rm500u.state = 0; osDelay(2000); continue; } // Success: log and delay printf("Sent: %s\n", json_buffer); osDelay(30000); // 30s interval } }
  • osDelay()来自 FreeRTOS,若用裸机则替换为HAL_Delay(),但需确保不阻塞其他任务(如看门狗喂狗);
  • 每次发送后打印json_buffer用于串口调试,上线前必须注释掉printf,否则 UART 占用 CPU 过高;
  • battery_mv应由 ADC 实际采样,此处仅为示意。

5. 实战排错:从 “AT+QIACT TIMEOUT” 到 “TCP 数据平台收不到”的五类高频问题定位表

当整个链路看似配置正确却无法稳定运行时,问题往往隐藏在跨层交互细节中。以下是依据 20+ 个真实 STM32+RM500U 项目总结的五大高频故障类型,每类均给出可立即执行的定位命令与修复动作。

5.1 RM500U 附着失败:AT+QIACT TIMEOUT 的根因分级排查

现象快速定位命令根本原因修复动作
AT+QIACT超时(>25s),AT+CGATT?返回+CGATT: 0AT+CSQ→ 查信号质量;AT+COPS?→ 查运营商注册状态SIM 卡未开户/欠费,或当地无 5G 覆盖(仅 LTE)更换 SIM 卡;用AT+QCFG="nwscanmode",3,1强制扫描 5G 频段
AT+QIACT返回+QIACT: 0AT+QICSGP?→ 查 APN 配置是否生效APN 设置未保存,或AT+CGDCONT中引号格式错误(如用中文引号)重发AT+CGDCONT=1,"IP","CMNET",确保英文双引号
AT+QIACT成功但AT+QISTAT显示0AT+QICSGP=1→ 查当前 bearer ID 是否匹配AT+QIOPEN使用了错误的 bearer ID(如1而非0AT+QIOPEN=0,"TCP",...,ID 必须与AT+QICSGP返回一致

提示:AT+QCFG="nwscanmode"的参数3表示“5G only”,1表示“auto”。若部署地 5G 信号弱,设为1可降级到 LTE 保连接。

5.2 AHT20 数据异常:跳变、恒定 0 或 -40°C 的硬件级诊断

现象万用表/示波器测量点异常读数含义解决方案
AHT20 SDA/SCL 电压始终为 0VVDD 对 GND 电压<3.0V → 供电不足;>3.6V → 过压损坏检查 LDO 输出,更换 AHT20
SDA 在HAL_I2C_Master_Transmit后无波形SDA 线无信号 → STM32 I²C 外设未使能或引脚复用错误检查__HAL_RCC_I2C1_CLK_ENABLE()GPIOB->AFR[0]配置
SDA 有波形但无 ACK(SCL 高电平时 SDA 持续高)SDA 在第 9 个 SCL 上升沿AHT20 未响应 → 地址错误或模块损坏用逻辑分析仪确认地址0x38,更换传感器

5.3 TCP 数据平台收不到:抓包级验证路径

仅靠 STM32 串口打印OK无法证明数据到达云平台。必须分段验证:

  1. 模块侧验证AT+QISEND返回+QISEND: 1,XX仅表示模块已将数据交给基带处理器,不代表已发出射频信号。需AT+QIRD=1,100读取模块接收缓冲区(若平台回包),或AT+QISTAT确认 socket 状态为1(connected);
  2. 网络侧验证:在私有云服务器执行tcpdump -i any port 8080 -w capture.pcap,用 Wireshark 打开,过滤tcp.len > 0,确认是否有来自 RM500U IP 的 SYN/ACK/PUSH 包;
  3. 应用侧验证:若用 Node.js Express 接收,加日志console.log('Raw body:', req.rawBody),确认是否收到完整 JSON 字符串而非乱码(常见于未处理\r\n或编码错误)。

5.4 STM32 硬件资源冲突:ST-Link 与 UART3 的经典冲突

当使用 ST-Link 调试时,PA10/PA11(UART3)可能被 ST-Link 的虚拟 COM 口占用,导致huart3初始化失败。现象为HAL_UART_Init()返回HAL_ERROR

永久解决方法

  • SystemClock_Config()后添加:
    __HAL_RCC_GPIOA_CLK_ENABLE(); GPIOA->MODER &= ~(GPIO_MODER_MODER10 | GPIO_MODER_MODER11); GPIOA->MODER |= (GPIO_MODE_OUTPUT_PP << (10*2)) | (GPIO_MODE_OUTPUT_PP << (11*2));
  • 或在 STM32CubeMX 中,将PA10/PA11的 GPIO Mode 设为GPIO_MODE_AF_PP,并在Pinout & Configuration → Connectivity → USART3中勾选Asynchronous取消勾选Hardware Flow Control(RM500U 不支持 RTS/CTS)。

5.5 低功耗场景下的时序陷阱:PSM 模式与测量周期冲突

若项目要求电池供电(如野外监测),需启用 RM500U 的 PSM(Power Saving Mode)。但AT+CPSMS=1,,,"00000001","00000001"设置后,模块进入深度睡眠,AHT20 测量完成中断无法唤醒模块。此时必须:

  • 将 AHT20 的INT引脚接到 RM500U 的WKUP引脚(需硬件改线);
  • 或放弃 PSM,改用AT+QSCLK=1关闭模块时钟,在HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI)前拉高DTR唤醒模块;
  • 绝对禁止在 PSM 模式下用HAL_Delay(30000)等待,因 STOP 模式下 SysTick 停止,HAL_GetTick()不更新。

最终验证时,务必用真实 5G 卡(非测试卡)在目标部署点实测 72 小时,记录AT+CSQ信号值、AT+QIACT耗时、JSON 发送成功率三项指标,方为真正可用。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/10 13:11:03

工业油污缺陷检测数据集:VOC/COCO/YOLO三格式全支持

简介&#xff1a;本资源是面向智能制造与工业视觉检测领域的YOLO目标检测实战数据集&#xff0c;专为算法工程师、高校研究者及自动化专业学生设计&#xff0c;解决工业油污缺陷识别这一典型质检难题。数据集包含10000张真实产线场景高清图像&#xff0c;全部经LabelImg精细标注…

作者头像 李华