qs100.c
#include "qs100.h"
// 内部调用函数:发送AT命令和处理返回响应
static void QS100_SendCmd(uint8_t *cmd);
static void QS100_HandleResp(void);
// 串口接收中断回调
void QS100_UartCallback(uint16_t size);
// 定义接收数据的缓冲区和长度
static uint8_t rxBuff[NB_BUFF_SIZE]; // 一次中断接收的数据,最大128
static uint16_t rxSize;
static uint8_t respBuff[NB_RESP_SIZE]; // 一次完整响应接收的数据,最大256
static uint16_t respSize;
// 初始化
void QS100_Init(void)
{
// 1. 串口3初始化
MX_USART3_UART_Init();
// 2. 配置使用中断方式接收变长数据,打开中断使能
HAL_UARTEx_ReceiveToIdle_IT(&huart3, rxBuff, NB_BUFF_SIZE);
// 3. 发送指令进行配置
// 3.1 重启
debug_printfln("开始重启NB-IoT模块...");
QS100_SendCmd("AT+RB\r\n");
debug_printfln("重启NB-IoT模块完成!");
// 3.2 设置回显AT指令
QS100_SendCmd("ATE1\r\n");
}
void QS100_UartCallback(uint16_t size)
{
rxSize = size;
// 重新调用 ReceiveToIdle_IT,打开中断使能
HAL_UARTEx_ReceiveToIdle_IT(&huart3, rxBuff, NB_BUFF_SIZE);
}
static void QS100_SendCmd(uint8_t *cmd)
{
HAL_UART_Transmit(&huart3, cmd, strlen((char *)cmd), 2000);
// 中断方式接收响应数据,处理响应
QS100_HandleResp();
}
static void QS100_HandleResp(void)
{
// 先清空要保存数据的数组
memset(rxBuff, 0, NB_BUFF_SIZE);
rxSize = 0;
memset(respBuff, 0, NB_RESP_SIZE);
respSize = 0;
// 阻塞轮询,等待接收响应
// 为了处理没有OK和ERROR的情况,增加一个计数值,减到0就退出循环
uint8_t cnt = 2;
do
{
// 引入超时
uint32_t timeout = 0xffffff;
while (rxSize == 0 && timeout--)
{
}
// HAL_Delay(1);
// 接收到数据,就将 rxBuff 中的内容拼接到 respBuff 中
memcpy(&respBuff[respSize], rxBuff, rxSize);
respSize += rxSize;
// 清零size
rxSize = 0;
} while (strstr((char *)respBuff, "OK") == NULL && strstr((char *)respBuff, "ERROR") == NULL && --cnt);
// 打印输出收到的响应
debug_printfln("AT命令响应:\n%.*s", respSize, respBuff);
printf("=======================================\n");
}
// 向 TCP 服务器发送数据
static CommonStatus QS100_GetIP(void);
static CommonStatus QS100_OpenSocket(uint16_t port, uint8_t *socketID);
static CommonStatus QS100_TCP_ConnectServer(uint8_t socketID, uint8_t serverIP[], uint16_t serverPort);
static CommonStatus QS100_TCP_SendDataToServer(uint8_t socketID, uint8_t sequence, uint8_t data[], uint16_t dataLen);
CommonStatus QS100_TCP_SendData(uint8_t serverIP[], uint16_t serverPort, uint8_t data[], uint16_t dataLen)
{
// 1. 联网
uint8_t cnt = 60;
while (QS100_GetIP() == COMMON_ERROR && --cnt)
{
Utils_Delay_s(1); // 1s查询一次联网状态
}
if (cnt == 0)
{
return COMMON_ERROR;
}
// 2. 打开Socket(TCP协议)
cnt = 20;
uint8_t socketID = 0;
while (QS100_OpenSocket(0, &socketID) == COMMON_ERROR && --cnt)
{
Utils_Delay_s(1); // 1s查询一次联网状态
}
if (cnt == 0)
{
return COMMON_ERROR;
}
// 3. 连接 TCP 服务器
cnt = 10;
while (QS100_TCP_ConnectServer(socketID, TCP_SERVER_IP, TCP_SERVER_PORT) == COMMON_ERROR && --cnt)
{
Utils_Delay_s(1); // 1s查询一次联网状态
}
if (cnt == 0)
{
return COMMON_ERROR;
}
// 4. 向 TCP 服务器发送数据
cnt = 10;
while (QS100_TCP_SendDataToServer(socketID, 5, data, dataLen) == COMMON_ERROR && --cnt)
{
Utils_Delay_s(1); // 1s查询一次联网状态
}
if (cnt == 0)
{
return COMMON_ERROR;
}
return COMMON_OK;
}
static CommonStatus QS100_GetIP(void)
{
// QS100_SendCmd("AT+CGATT=1\r\n");
// 发送指令
QS100_SendCmd("AT+CGATT?\r\n");
// 根据指令响应,判断是否联网
if (strstr((char *)respBuff, "+CGATT:1"))
{
return COMMON_OK;
}
return COMMON_ERROR;
}
static CommonStatus QS100_OpenSocket(uint16_t port, uint8_t *socketID)
{
// 拼接命令
uint8_t tmpCmd[50] = {0};
sprintf((char *)tmpCmd, "AT+NSOCR=STREAM,6,%d,0\r\n", port);
// 发送命令
QS100_SendCmd(tmpCmd);
// 如果响应OK,解析出socket 编号
if (strstr((char *)respBuff, "OK"))
{
sscanf((char *)respBuff, "%*[^:]:%hhu", socketID);
return COMMON_OK;
}
return COMMON_ERROR;
}
static CommonStatus QS100_TCP_ConnectServer(uint8_t socketID, uint8_t serverIP[], uint16_t serverPort)
{
// 拼接命令
uint8_t tmpCmd[50] = {0};
sprintf((char *)tmpCmd, "AT+NSOCO=%d,%s,%d\r\n", socketID, serverIP, serverPort);
// 发送命令
QS100_SendCmd(tmpCmd);
if (strstr((char *)respBuff, "OK"))
{
return COMMON_OK;
}
return COMMON_ERROR;
}
static CommonStatus QS100_TCP_SendDataToServer(uint8_t socketID, uint8_t sequence, uint8_t data[], uint16_t dataLen)
{
// 格式 AT+NSOSD=0,2,4444,0x200,1
// 1. 将数据转换成16进制字符串
uint8_t hexData[2 * dataLen + 1];
for (uint8_t i = 0; i < dataLen; i++)
{
sprintf((char *)&hexData[i * 2], "%02X", data[i]);
}
// 2. 拼接命令
uint8_t tmpCmd[512] = {0};
sprintf((char *)tmpCmd, "AT+NSOSD=%d,%d,%s,0x200,%d\r\n", socketID, dataLen, hexData, sequence);
// 3. 发送命令
QS100_SendCmd(tmpCmd);
if (strstr((char *)respBuff, "OK"))
{
// 4. 查询数据是否发送成功
// 4.1 清空命令缓冲区
memset((char *)tmpCmd, 0, strlen((char *)tmpCmd));
// 4.2 拼接命令
sprintf((char *)tmpCmd, "AT+SEQUENCE=%d,%d\r\n", socketID, sequence);
do
{
Utils_Delay_s(1);
// 4.3 每隔1s发送一次命令进行查询
QS100_SendCmd(tmpCmd);
} while ( respBuff[19] == '2' );
// 4.4 如果状态为1,表示发送成功
if (respBuff[19] == '1')
{
return COMMON_OK;
}
}
return COMMON_ERROR;
}
// 进入低功耗模式(PSM模式)
void QS100_EnterLowPower(void)
{
debug_printfln("NB-IoT设备进入低功耗模式...");
QS100_SendCmd("AT+FASTOFF=0\r\n");
}
// 退出低功耗模式(用NB_WKUP引脚唤醒)
void QS100_ExitLowPower(void)
{
debug_printfln("NB-IoT设备从睡眠中唤醒...");
HAL_GPIO_WritePin(NB_WKUP_GPIO_Port, NB_WKUP_Pin, GPIO_PIN_SET);
Utils_Delay_ms(150);
HAL_GPIO_WritePin(NB_WKUP_GPIO_Port, NB_WKUP_Pin, GPIO_PIN_RESET);
}
ds3553.c
/*
* @Author: wushengran
* @Date: 2024-11-05 16:07:52
* @Description:
*
* Copyright (c) 2024 by atguigu, All Rights Reserved.
*/
#include "ds3553.h"
// 初始化
void DS3553_Init(void)
{
MX_I2C1_Init();
}
// 定义内部静态函数,读取某个寄存器的值(传入地址)
static uint8_t DS3553_ReadReg(uint8_t regAddr)
{
// 定义变量保存读取的值
uint8_t result = 0;
// 1. CS拉低
DS3553_CS_L;
Utils_Delay_ms(4);
// 2. 调库I2C读取从设备数据
// 2.1 假写
HAL_I2C_Master_Transmit(&hi2c1, DS3553_ADDR_W, ®Addr, 1, 2000);
// 2.2 真读
HAL_I2C_Master_Receive(&hi2c1, DS3553_ADDR_R, &result, 1, 2000);
// 3. CS拉高
DS3553_CS_H;
Utils_Delay_ms(11);
return result;
}
// 读取芯片ID
uint8_t DS3553_ReadID(void)
{
return DS3553_ReadReg(DS3553_REG_CHIP_ID);
}
// 读取计步数值(24位)
uint32_t DS3553_ReadCNT(void)
{
uint8_t buff[3] = {0};
buff[0] = DS3553_ReadReg(DS3553_REG_STEP_CNT_L);
buff[1] = DS3553_ReadReg(DS3553_REG_STEP_CNT_M);
buff[2] = DS3553_ReadReg(DS3553_REG_STEP_CNT_H);
return buff[0] + (buff[1] << 8) + (buff[2] << 16);
}
at6558r.c
/*
* @Author: wushengran
* @Date: 2024-11-04 15:59:07
* @Description:
*
* Copyright (c) 2024 by atguigu, All Rights Reserved.
*/
#include "at6558r.h"
static void AT6558R_SendCmd(uint8_t *cmd);
// 定义接收GPS数据的缓冲区和长度
static uint8_t rxBuff[GPS_INFO_BUFF_SIZE];
static uint16_t rxSize;
// 初始化
void AT6558R_Init(void)
{
// 1. 初始化串口2
MX_USART2_UART_Init();
// 2. 配置使用中断方式接收变长数据,打开中断使能
HAL_UARTEx_ReceiveToIdle_IT(&huart2, rxBuff, GPS_INFO_BUFF_SIZE);
// 3. 发送指令进行配置
// 配置GPS工作模式
debug_printfln("GPS定位模块配置:工作模式为 GPS + 北斗");
AT6558R_SendCmd(GPS_MODE);
// 设置定位更新频率
debug_printfln("GPS定位模块配置:更新频率为 1Hz");
AT6558R_SendCmd(GPS_UPDATE_FREQ);
}
// 发送命令函数,传入$和*之间的内容
static void AT6558R_SendCmd(uint8_t *cmd)
{
// 计算校验和
uint16_t cs = cmd[0];
for (uint8_t i = 1; cmd[i] != '\0'; i++)
{
cs ^= cmd[i];
}
debug_printfln("cs = %X", cs);
// 将cs拼接到完整命令中
uint8_t tmpCmd[50] = {0};
sprintf((char *)tmpCmd, "$%s*%X\r\n", cmd, cs);
// 串口发送命令
HAL_UART_Transmit(&huart2, tmpCmd, strlen((char *)tmpCmd), 2000);
debug_printfln("tmpCmd = %s", tmpCmd);
}
void AT6558R_UartCallback(uint16_t size)
{
// 获取接收到的数据长度
rxSize = size;
// 重新开启中断接收方式
HAL_UARTEx_ReceiveToIdle_IT(&huart2, rxBuff, GPS_INFO_BUFF_SIZE);
}
// 读取GPS数据
void AT6558R_ReadData(uint8_t data[], uint16_t *dataLen)
{
// 先清空要保存数据的数组
memset(data, 0, strlen((char *)data));
*dataLen = 0;
// 根据rxSize,判断是否接收到数据
if (rxSize == 0)
{
return;
}
// 将接收到的数据复制到data中
if (strstr((char *)rxBuff, GPS_INFO_BEGIN) && strstr((char *)rxBuff, GPS_INFO_END))
{
memcpy(data, rxBuff, rxSize);
*dataLen = rxSize;
}
// 清零size
rxSize = 0;
}
// 进入低功耗模式,直接将 GPS_EN 拉低,断电
void AT6558R_EnterLowPower(void)
{
debug_printfln("GPS 模块电源关闭...");
HAL_GPIO_WritePin(GPS_EN_GPIO_Port, GPS_EN_Pin, GPIO_PIN_RESET);
}
// 退出低功耗模式
void AT6558R_ExitLowPower(void)
{
debug_printfln("GPS 模块电源打开...");
HAL_GPIO_WritePin(GPS_EN_GPIO_Port, GPS_EN_Pin, GPIO_PIN_SET);
}
communication.c
/*
* @Author: wushengran
* @Date: 2024-11-05 11:20:14
* @Description:
*
* Copyright (c) 2024 by atguigu, All Rights Reserved.
*/
#include "communication.h"
#include "cJSON.h"
// 启动通讯模块
void Communication_Start(void)
{
AT6558R_Init();
QS100_Init();
LoRa_Init();
}
// 接收GPS数据缓冲区
uint8_t gpsData[GPS_INFO_BUFF_SIZE];
uint16_t gpsDataLen;
// 接收 GPS 数据
void Communication_GetGpsInfo(UploadDataType *data)
{
debug_printfln("读取 GPS 定位信息...");
// 定义指针,指向 RMC 数据的位置
char *rmcStart;
// 轮询进行数据接收
while (1)
{
// 接收GPS数据
AT6558R_ReadData(gpsData, &gpsDataLen);
if (gpsDataLen > 0)
{
// 有数据,找到 RMC 的起始位置
// rmcStart = strstr((char *)gpsData, "$GNRMC");
rmcStart = "$GNRMC,070822.000,A,4006.81888,N,11621.89413,E,0.81,359.02,020624,,,A,V*02";
// 跳过不是A和V的所有字符,找到有效标记位
uint8_t valid = 0;
sscanf(rmcStart, "%*[^AV]%c", &valid);
// 如果是A,就是有效数据,退出轮询进行数据包装
if (valid == 'A')
{
debug_printfln("有效定位数据:\n%s", gpsData);
break;
}
// 如果是V,无效数据,继续循环读取接收到的数据
else
{
debug_printfln("无效定位数据:\n%s", gpsData);
}
}
}
// 解析 GPS 信息
// 数据示例:$GNRMC,070822.000,A,4006.81888,N,11621.89413,E,0.81,359.02,020624,,,A,V*02
// 定义保存时间和日期的字符串
char time[7] = {0}; // 时分秒,格式 hhmmss
char date[7] = {0}; // 日月年,格式 ddMMyy
// 提取信息到对应变量中
sscanf(rmcStart, "$GNRMC,%6c%*7c%lf,%c,%lf,%c,%lf,%*f,%6c",
time,
&data->lat, (char *)&data->latDir,
&data->lon, (char *)&data->lonDir,
&data->speed,
date);
// 合成dataTime,格式 yyyy-MM-dd hh:mm:ss
sprintf((char *)data->dateTime, "20%c%c-%c%c-%c%c %c%c:%c%c:%c%c",
date[4], date[5], date[2], date[3], date[0], date[1],
time[0], time[1], time[2], time[3], time[4], time[5]);
Utils_Time_UTC2Beijing(data->dateTime, data->dateTime);
// 调整经纬度信息,保存成 xxx.xxx 形式,单位是度
uint8_t lat_int = (uint8_t)(data->lat / 100); // 纬度的整数部分(单位度)
data->lat = lat_int + (data->lat - lat_int * 100) / 60;
uint8_t lon_int = (uint8_t)(data->lon / 100); // 经度的整数部分(单位度)
data->lon = lon_int + (data->lon - lon_int * 100) / 60;
}
static void Communication_FormatDataToJson(UploadDataType *data);
static CommonStatus Communication_SendDataByNBIoT(UploadDataType *data);
static CommonStatus Communication_SendDataByLoRa(UploadDataType *data);
// 发送要上传的数据(通过NB-IoT 或 LoRa)
void Communication_SendData(UploadDataType *data)
{
// 1. 将数据转换为JSON串
Communication_FormatDataToJson(data);
// 2. 通过NB-IoT发送
CommonStatus sendStatus = Communication_SendDataByNBIoT(data);
// 3. 如果发送失败,就再通过LoRa发送
if (sendStatus == COMMON_ERROR)
{
debug_printfln("NB-IoT发送失败,将通过LoRa发送数据...");
Communication_SendDataByLoRa(data);
}
}
static void Communication_FormatDataToJson(UploadDataType *data)
{
// 0. 将UID补充到数据中(16进制字符形式)
sprintf((char *)data->uid, "%08X%08X%08X", HAL_GetUIDw2(), HAL_GetUIDw1(), HAL_GetUIDw0());
// 1. 创建一个JSON对象
cJSON *cjson = cJSON_CreateObject();
// 2. 向JSON对象中逐个添加键值对
cJSON_AddStringToObject(cjson, "uid", (char *)data->uid);
cJSON_AddNumberToObject(cjson, "lon", data->lon);
cJSON_AddStringToObject(cjson, "lonDir", (char *)data->lonDir);
cJSON_AddNumberToObject(cjson, "lat", data->lat);
cJSON_AddStringToObject(cjson, "latDir", (char *)data->latDir);
cJSON_AddNumberToObject(cjson, "speed", data->speed);
cJSON_AddStringToObject(cjson, "dateTime", (char *)data->dateTime);
cJSON_AddNumberToObject(cjson, "stepNum", data->stepNum);
// 3. 生成JSON字符串
char *out = cJSON_PrintUnformatted(cjson);
data->jsonLen = strlen(out);
memcpy(data->json, out, data->jsonLen);
// 4. 释放cJSON对象(堆空间)
cJSON_Delete(cjson);
printData(data);
}
// --- 以NB-IoT方式发送数据
static CommonStatus Communication_SendDataByNBIoT(UploadDataType *data)
{
return QS100_TCP_SendData(TCP_SERVER_IP, TCP_SERVER_PORT, data->json, data->jsonLen);
}
// ---- 以LoRa方式发送数据
static CommonStatus Communication_SendDataByLoRa(UploadDataType *data)
{
return (CommonStatus)LoRa_SendData(data->json, data->jsonLen);
}
// 打印输出要上传的数据
void printData(UploadDataType *data)
{
printf("GPS 信息:\n");
printf(" 经度:%lf, 方向:%s\n", data->lon, data->lonDir);
printf(" 纬度:%lf, 方向:%s\n", data->lat, data->latDir);
printf(" 对地速度:%lf 节\n", data->speed);
printf(" 定位时间:%s\n", data->dateTime);
printf("计步信息:\n");
printf(" 运动步数:%u\n", data->stepNum);
printf("上传的数据:\n");
printf(" 数据长度:%d\n", data->jsonLen);
printf(" 数据:%.*s\n", data->jsonLen, data->json);
}