畜牧定位器

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, &regAddr, 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);
}

OneNET平台NB-IOT接入开发文档 目录 第一章 文档说明 5 第二章 基于OneNET平台的NB设备接入及应用开发流程总体综述 6 2.1 NB设备接入OneNET平台流程图 6 2.2 应用开发流程图 6 第三章 NB设备接入OneNET平台 8 3.1 终端设备接入OneNET平台前的准备工作 8 3.1.1 SDK移植到MCU 9 3.1.2 SDK移植到NB通信模组 9 3.1.3 SDK移植到NB芯片 10 3.2 终端设备接入OneNET平台步骤 11 3.2.1 OneNET平台创建产品及设备 12 3.2.2 终端设备软硬件初始化 15 3.2.3 终端创建设备及资源 15 3.2.4 登录OneNET平台 16 3.2.5 平台订阅&发现设备资源 17 第四章 第三方应用开发 18 4.1第三方应用接入OneNET平台 18 4.1.1第三方应用平台接入验证程序 19 4.1.2 OneNET平台配置第三方应用平台 19 4.2 OneNET平台数据推送 20 4.1.1 明文消息 21 4.1.2 密文消息 22 4.1.3 消息相关字段说明 22 4.1.4 加密算法详述 23 4.2 API接口 24 4.2.1创建设备 25 4.2.2查看单个设备信息 26 4.2.3删除设备 27 4.2.4读设备资源 27 4.2.5写设备资源 28 4.2.6下发命令 29 4.2.7获取资源列表 29 4.2.8订阅 30 4.2.9离线命令 31 4.2.10 触发器 35 4.2.11批量查询设备状态 40 4.2.12批量查询设备最新数据 40 4.2.13查看数据点 41 第五章 接入实例 46 5.1 MCU侧工作流程说明 47 5.1.1创建设备(dev) 47 5.1.2向设备添加资源 48 5.1.3登录请求 49 5.2 OneNET平台侧数据收发流程说明 50 5.2.1数据接收 50 5.2.2指令下发 51 第六章 资源下载 57 6.1 数据推送SDK下载 57 6.2 NB-IoT API下载 57 6.3 NB-IoT 开发板资料下载 57 第七章 NB-IoT接入协议说明 58 7.1 基于NB-IoT的LWM2M协议 58 7.1.1 LWM2M协议逻辑实体与逻辑接口 59 7.1.2 LWM2M协议栈 60 7.2基于NB-IoT的CoAP协议 61 7.2.1 CoAP协议栈 61 7.2.2 块传输 63 7.2.3 安全传输 65 第八章 常见问题 66
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值