09_NB-IoT模块数据发送流程与实现
大家好呀!我是你们的老师,今天咱们继续聊 NB-IoT 模块(骑士100,也就是 Quectel BC95)的事儿。上节课初始化完了,今天咱们重点讲咋用它把 GPS 数据发出去,从头到尾过一遍流程,搞清楚每一步咋干。别慌,咱们一步一步来,保证你能弄懂!走起!
1. 目标分析:NB-IoT 模块要干啥?
咱们先理理思路,初始化完 NB-IoT 模块后,接下来干啥?其实很简单,核心目标就是把数据发出去。
-
目标明确:GPS 数据已经收到了(比如经纬度啥的),咱们得用 NB-IoT 模块把这些数据发到云端服务器。
-
对比经验:之前用过的 LoRa 模块(比如 SX1276),发数据得建连接,NB-IoT 也差不多,先得建个 Socket,再发数据。
-
核心问题:咋建连接?咋发数据?发完咋确认成功?得一步步搞清楚。
流程图:目标分析
NB-IoT模块初始化完成
获取GPS数据
通过NB-IoT发送到云端
创建Socket连接
发送数据
确认发送成功
补充例子
假设 GPS 数据是 latitude=30.4688,longitude=114.4689
,咱们得用 NB-IoT 发到云端的一个服务器,比如 IP 是 112.125.89.8
,端口 44939
。
2. 第一步:判断是否联网
发数据前,先得确认 NB-IoT 模块联网没。插上 SIM 卡和天线,按理说一上电就该联网,但得确认下。
-
联网状态:就像手机插上 SIM 卡,能连上中国移动啥的,但不一定有数据交互。咱们得确认模块是不是处于“附着”状态。
-
AT 指令:用
AT+CGATT?
查询附着状态。返回+CGATT:1
表示已附着(联网),+CGATT:0
表示未附着(没联网)。 -
处理逻辑:发
AT+CGATT?
,看响应。如果是 0,说明没联网,报错给用户,检查 SIM 卡、天线、芯片啥的。如果是 1,继续下一步。
流程图:判断是否联网
发送AT+CGATT?
等待响应
+CGATT:1
+CGATT:0
已联网,继续
未联网,报错
检查SIM卡/天线/芯片
补充例子
发送指令代码:
int_qs100_send_cmd((uint8_t *)"AT+CGATT?\r\n"); int_qs100_handle_response(); if (strstr((char *)qs100_response_buffer, "+CGATT:1") != NULL) { // 已联网,继续 } else { printf("Error: Not attached to network\r\n"); return; }
3. 第二步:创建 Socket
联网了,接下来得创建个 Socket,才能发数据。啥是 Socket?就是个通信通道。
-
AT 指令:用
AT+NSOCR
创建 Socket。格式:AT+NSOCR=<type>,<protocol>,<listen_port>,<socket_id>,<receive_control>
。-
type
:流式传输,填“DGRAM”。 -
protocol
:咱们用 TCP(有连接),填“6”。 -
listen_port
:本地端口,填“0”让系统随机分配(反正咱们是发数据,不咋用本地端口)。 -
socket_id
:Socket ID,芯片支持 0 或 1,咱们填“0”。 -
receive_control
:0 表示忽略下行消息(咱只发,不收)。
-
-
响应结果:成功返回
+NSOCR:<socket_id>
,比如+NSOCR:0
,表示创建成功,Socket ID 是 0。 -
循环尝试:如果创建失败,每秒试一次,最多试 20 次,20 秒还不成,报错。
流程图:创建 Socket
计数<20
计数>=20
发送AT+NSOCR
等待响应
+NSOCR:0
失败
创建成功,继续
每秒重试,计数+1
报错:创建失败
补充例子
创建 Socket 代码:
int retry = 0; while (retry < 20) { int_qs100_send_cmd((uint8_t *)"AT+NSOCR=DGRAM,6,0,0,0\r\n"); int_qs100_handle_response(); if (strstr((char *)qs100_response_buffer, "+NSOCR:0") != NULL) { break; // 创建成功 } HAL_Delay(1000); // 每秒重试 retry++; } if (retry >= 20) { printf("Error: Failed to create socket\r\n"); return; }
4. 第三步:创建连接
Socket 有了,得连上服务器。咋连?用服务器的 IP 和端口。
-
AT 指令:用
AT+NSOCO
创建连接。格式:AT+NSOCO=<socket_id>,<remote_addr>,<remote_port>
。-
socket_id
:刚才创建的 Socket ID,填“0”。 -
remote_addr
:服务器 IP,比如112.125.89.8
。 -
remote_port
:服务器端口,比如44939
。
-
-
响应结果:成功返回“OK”,表示连接建立。
-
循环尝试:如果失败,每秒试一次,最多 20 次,20 秒失败就报错。
流程图:创建连接
计数<20
计数>=20
发送AT+NSOCO
等待响应
OK
失败
连接成功,继续
每秒重试,计数+1
报错:连接失败
补充例子
假设服务器地址是 112.125.89.8:44939
:
int retry = 0; char cmd[64]; sprintf(cmd, "AT+NSOCO=0,%s,%u\r\n", "112.125.89.8", 44939); while (retry < 20) { int_qs100_send_cmd((uint8_t *)cmd); int_qs100_handle_response(); if (strstr((char *)qs100_response_buffer, "OK") != NULL) { break; // 连接成功 } HAL_Delay(1000); retry++; } if (retry >= 20) { printf("Error: Failed to connect\r\n"); return; }
5. 第四步:发送数据
连接好了,准备发数据。数据得按十六进制字符串格式发,比如“ABC”得转成“616263”。
-
AT 指令:用
AT+NSOSD
发送数据。格式:AT+NSOSD=<socket_id>,<length>,<data>,<flag>,<cycles>
。-
socket_id
:填“0”。 -
length
:数据长度(十六进制字符串长度)。 -
data
:十六进制字符串,比如“616263”。 -
flag
:默认“0x200”,表示发完发下一条。 -
cycles
:消息编号,1 到 255,递增用。
-
-
响应结果:返回
OK
表示模块收到指令,但不代表发到云端成功。 -
数据转换:比如发“ABC”,ASCII 是 97、98、99,十六进制是“616263”。
流程图:发送数据
准备数据
转十六进制字符串
构造AT+NSOSD
发送指令
等待响应
OK
失败
模块收到,继续
重试几次
补充例子
发送“ABC”代码:
uint8_t data[] = "ABC"; char hex_data[128]; for (int i = 0; i < strlen((char *)data); i++) { sprintf(hex_data + i*2, "%02X", data[i]); } char cmd[256]; sprintf(cmd, "AT+NSOSD=0,%u,%s,0x200,1\r\n", strlen(hex_data)/2, hex_data); int_qs100_send_cmd((uint8_t *)cmd); int_qs100_handle_response();
6. 第五步:查询发送状态
模块收到指令不代表发到云端成功,得查状态。
-
AT 指令:用
AT+NSOSTS
查询发送状态。格式:AT+NSOSTS=<socket_id>,<cycles>
。-
socket_id
:填“0”。 -
cycles
:刚才发数据的编号,比如“1”。
-
-
响应结果:返回
+NSOSTS:<socket_id>,<cycles>,<status>
。状态:0=失败,1=成功,2=发送中,-1=无此消息。 -
循环查询:每秒查一次,直到状态是 1(成功)或 0(失败),最多查 60 秒。
流程图:查询发送状态
60秒
发送AT+NSOSTS
等待响应
+NSOSTS:0,1,1
+NSOSTS:0,1,2
成功,结束
每秒重试
失败,报错
补充例子
查询代码:
int retry = 0; char cmd[64]; sprintf(cmd, "AT+NSOSTS=0,1\r\n"); while (retry < 60) { int_qs100_send_cmd((uint8_t *)cmd); int_qs100_handle_response(); if (strstr((char *)qs100_response_buffer, "+NSOSTS:0,1,1") != NULL) { printf("Data sent successfully\r\n"); break; } if (strstr((char *)qs100_response_buffer, "+NSOSTS:0,1,0") != NULL) { printf("Error: Data send failed\r\n"); break; } HAL_Delay(1000); retry++; }
7. 封装函数:整合发送逻辑
最后把这几步封装成一个函数,方便调用。
-
函数定义:
void int_qs100_send_data(const uint8_t *server_ip, uint16_t server_port, const uint8_t *data, uint16_t data_len, uint8_t cycles)
。 -
步骤整合:
-
判断是否联网(60 秒超时)。
-
创建 Socket(20 次重试)。
-
创建连接(20 次重试)。
-
发送数据。
-
查询发送状态(60 秒查询)。
-
-
返回值:成功返回 0,失败返回 -1。
流程图:封装函数逻辑
成功
失败
成功
失败
成功
失败
成功
失败
int_qs100_send_data
判断联网
创建Socket
报错
创建连接
发送数据
查询发送状态
返回0
返回-1
补充例子
伪代码:
int int_qs100_send_data(const uint8_t *server_ip, uint16_t server_port, const uint8_t *data, uint16_t data_len, uint8_t cycles) { // 1. 判断联网 // 2. 创建Socket // 3. 创建连接 // 4. 发送数据 // 5. 查询状态 // 返回结果 }
总结
好了,今天咱们把 NB-IoT 模块发数据的流程过了一遍!从判断联网到创建 Socket、连接、发数据、再到确认成功,核心就是这几步。流程清楚了,代码就好写了。
关键点是:
-
先用
AT+CGATT?
确认联网,60 秒超时。 -
用
AT+NSOCR
创建 Socket,AT+NSOCO
创建连接,20 次重试。 -
用
AT+NSOSD
发数据,数据转十六进制,AT+NSOSTS
查状态。
课后可以试试把这几步写成代码,跑跑看,感受下完整流程。下节课咱们再细聊封装细节,有问题随时来问我哈,休息下,继续搞~
希望这篇博客对你有帮助!如果有啥需要调整的地方,随时告诉我~