From 8d3080daefb506c818962ddb1d50bb8d19a19456 Mon Sep 17 00:00:00 2001 From: czq <860517298@qq.com> Date: Fri, 6 Jun 2025 16:53:42 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9A=82=E5=AD=98=20=E4=BD=BF=E7=94=A8MQTT?= =?UTF-8?q?=E8=BF=9B=E8=A1=8C=E6=95=B0=E6=8D=AE=E6=94=B6=E5=8F=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bsp/inc/bsp_ml307r.h | 13 ++ bsp/src/bsp_ml307r.c | 314 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 324 insertions(+), 3 deletions(-) diff --git a/bsp/inc/bsp_ml307r.h b/bsp/inc/bsp_ml307r.h index d4a8cf6..9cf419e 100644 --- a/bsp/inc/bsp_ml307r.h +++ b/bsp/inc/bsp_ml307r.h @@ -15,7 +15,20 @@ #define ML307_UART_TX_PIN GPIO_Pin_13 //PB13 #define ML307_UART_RX_PIN GPIO_Pin_12 //PB12 +// MQTT服务器配置 +// #define MQTT_HOST "120.25.151.173" // MQTT服务器地址 +#define MQTT_HOST "8.135.10.183" // MQTT服务器地址 +// #define MQTT_PORT 1883 // MQTT服务器端口 +#define MQTT_PORT 25237 // MQTT服务器端口 +#define MQTT_USER "guest" // MQTT用户名 +#define MQTT_PASS "guest" // MQTT密码 +#define CLIENT_ID "MKL-ZW001" // 客户端ID前缀 +// MQTT主题定义 +// #define MQTT_TOPIC_REV "/mkl/zw001/order/" // 订阅主题前缀,后面会拼接设备SN +#define MQTT_TOPIC_REV "test" +// #define MQTT_TOPIC_UP "/mkl/zw001/push/data" // 发布主题 +#define MQTT_TOPIC_UP "test" // 发布主题 typedef struct __attribute__((packed)) { diff --git a/bsp/src/bsp_ml307r.c b/bsp/src/bsp_ml307r.c index ddd4595..f5db523 100644 --- a/bsp/src/bsp_ml307r.c +++ b/bsp/src/bsp_ml307r.c @@ -178,6 +178,29 @@ static int URC_Module_Ready_Cb(at_urc_info_t *info) return 0; } +// MQTT消息接收回调函数 +static int URC_MQTT_Publish_Cb(at_urc_info_t *info) +{ + logDebug("MQTT rece: %s\r\n", info->urcbuf); + + char topic[64] = {0}; + char payload[512] = {0}; + + if (sscanf(info->urcbuf, "+MQTTURC: \"publish\",%*d,%*d,\"%[^\"]\",", topic) >= 1) { + logDebug("topic: %s\r\n", topic); + + // 查找JSON数据的起始位置(查找第一个'{'字符) + char *json_start = strchr(info->urcbuf, '{'); + if (json_start) { + logDebug("payload: %s\r\n", json_start); + + // 解析JSON负载 + + } + } + + return 0; +} /** * @brief urc订阅表 @@ -185,6 +208,7 @@ static int URC_Module_Ready_Cb(at_urc_info_t *info) static const urc_item_t urc_table[] = { {.prefix = "+CPIN: READY", .endmark = '\n', .handler = URC_Module_Ready_Cb},//模块准备就绪 + {.prefix = "publish", .endmark = '\n', .handler = URC_MQTT_Publish_Cb},//MQTT消息接收 }; @@ -199,6 +223,23 @@ void Module_String2Hex(char *hex_arry, char *str) } } +// 添加全局变量用于存储设备SN +char device_sn[16] = {0}; + +// 获取设备SN(基于IMEI) +static void BSP_Get_Device_SN(void) +{ + // 使用IMEI的后12位作为设备SN + if (strlen(Module_Info_t.imei) >= 12) { + strncpy(device_sn, Module_Info_t.imei + (strlen(Module_Info_t.imei) - 12), 12); + device_sn[12] = '\0'; + logDebug("Device SN: %s\r\n", device_sn); + } else { + strcpy(device_sn, "000000000000"); + logDebug("Using default SN: %s\r\n", device_sn); + } +} + static void Module_Get_Imei_Cb(at_response_t *r) { char imei[16] = {0}; @@ -391,6 +432,59 @@ static void Module_Timer(void) at_send_singlline(ml307r_obj, &attr, "AT+CCLK?"); } +// 添加时间戳转换函数 +static const uint16_t month_days[] = {0, 31, 28, 31, 30, 31, 30, + 31, 31, 30, 31, 30, 31}; // 每月的天数 + +// 判断是否为闰年 +static int is_leap_year(int year) +{ + return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0); +} + +static uint32_t utc_to_timestamp(const struct tm *timeinfo) +{ + int year = timeinfo->tm_year; + int month = timeinfo->tm_mon; // 假设月份从 1 开始 + int day = timeinfo->tm_mday; + int hour = timeinfo->tm_hour; + int min = timeinfo->tm_min; + int sec = timeinfo->tm_sec; + + int total_days = 0; + + // 累加整年的天数 + for (int y = 1970; y < year; y++) + { + total_days += 365; + if (is_leap_year(y)) + { + total_days += 1; + } + } + + // 累加当年的各个月份天数 + for (int m = 1; m < month; m++) + { + total_days += month_days[m]; + // 如果是闰年且月份超过2月,则多加一天 + if (m == 2 && is_leap_year(year)) + { + total_days += 1; + } + } + + // 加上当月天数 + total_days += day - 1; + + // 计算总秒数 + uint32_t timestamp = ((uint32_t)total_days * 24 * 60 * 60) + + ((uint32_t)hour * 60 * 60) + + ((uint32_t)min * 60) + + sec; + + return timestamp; +} Ts_Network_Service_Info_t Service_Info_t = {0}; @@ -513,10 +607,96 @@ static void BSP_Get_Module_Info(void) Module_Get_Iccid(); Module_Timer(); Module_NetWork_Info(); // 获取网络连接信息 + // BSP_Get_Device_SN(); // 获取设备SN // at_send_singlline(ml307r_obj, NULL, "AT+MIPMODE=0,1"); // 进入透传模式 } + +static int MQTT_CfgConnSub(at_env_t *e) +{ + char data[128] = {0}; + + switch (e->state) + { + case 0: + logDebug("MQTT config start!\r\n"); + e->println(e, "AT+MQTTCFG=\"pingresp\",0,1"); + e->reset_timer(e); + e->state++; + break; + case 1: + if (e->contains(e, "OK")) + { + tmos_memset(data, 0, sizeof(data)); + // 使用设备SN作为客户端ID的一部分 + sprintf(data, "AT+MQTTCONN=0,\"%s\",%d,\"%s-%s\",\"%s\",\"%s\"", + MQTT_HOST, MQTT_PORT, CLIENT_ID, device_sn, MQTT_USER, MQTT_PASS); + e->println(e, data); + e->reset_timer(e); + e->state++; + } + if (e->is_timeout(e, 5000)) + { + e->state--; + if (++e->i > 3) + { + logDebug("MQTT config error!\r\n"); + e->finish(e, AT_RESP_ERROR); + } + } + break; + case 2: + if (e->contains(e, "\"conn\",0,0")) + { + logDebug("MQTTCONN OK\r\n"); + tmos_memset(data, 0, sizeof(data)); + // 订阅主题 + sprintf(data, "AT+MQTTSUB=0,\"%s%s\",0", MQTT_TOPIC_REV, device_sn); + logDebug("MQTTSUB: %s\r\n", data); + e->println(e, data); + e->reset_timer(e); + e->state++; + } + if (e->is_timeout(e, 5000)) + { + e->state--; + if (++e->i > 3) + { + logDebug("MQTT connect error!\r\n"); + e->finish(e, AT_RESP_ERROR); + } + } + break; + case 3: + if (e->contains(e, "suback")) + { + logDebug("MQTTSUB OK\r\n"); + IotFlag_t.Connect_success_flag = 1; + e->finish(e, AT_RESP_OK); + } + if (e->is_timeout(e, 5000)) + { + e->state--; + if (++e->i > 3) + { + logDebug("MQTT subscribe error!\r\n"); + IotFlag_t.Connect_error_flag = 1; + e->finish(e, AT_RESP_ERROR); + } + } + break; + } + return 0; +} + +static void BSP_MQTT_CfgConnSub(void) +{ + at_do_work(ml307r_obj, NULL, MQTT_CfgConnSub); +} + + + /* * @brief 创建设备 * @return @@ -557,7 +737,8 @@ static int BSP_Module_Connect_CtWing_Handle(at_env_t *e) //连接服务器 static void BSP_Module_Connect_CtWing(void) { - at_do_work(ml307r_obj, NULL, BSP_Module_Connect_CtWing_Handle);//创建设备 + BSP_MQTT_CfgConnSub(); + // at_do_work(ml307r_obj, NULL, BSP_Module_Connect_CtWing_Handle);//创建设备 } @@ -577,13 +758,13 @@ typedef struct { uint16_t length; // JSON字符串长度 } Ts4GJsonData; -/** +/********************************** * @brief 生成4G模组上报的JSON数据 * * @param p4GData JSON数据结构体指针 * @param cmd 命令类型,与蓝牙命令类型保持一致 * @return uint8_t 0表示成功 - */ + uint8_t BSP_4G_Generate_JsonData(Ts4GJsonData *p4GData, TeFrameCmd cmd) { // 首先清零JSON缓冲区 @@ -647,7 +828,94 @@ uint8_t BSP_4G_Generate_JsonData(Ts4GJsonData *p4GData, TeFrameCmd cmd) return 0; } +********************/ +/** + * @brief 生成4G模组上报的JSON数据 + * + * @param p4GData JSON数据结构体指针 + * @param cmd 命令类型,与蓝牙命令类型保持一致 + * @return uint8_t 0表示成功 + */ +uint8_t BSP_4G_Generate_JsonData(Ts4GJsonData *p4GData, TeFrameCmd cmd) +{ + // 首先清零JSON缓冲区 + memset(p4GData->json_buffer, 0, JSON_BUFFER_SIZE); + + // 获取阀门数据 + TsValveData ValveData; + // 填充阀门数据,与蓝牙上报相同 + ValveData.switch_status = gValveData.switch_status; + ValveData.temp = ValveRawData.atom_temp; + ValveData.in_pressure = ValveRawData.in_press_raw; + ValveData.out_pressure = ValveRawData.out_press_raw; + ValveData.atm_pressure = ValveRawData.atom_press; + ValveData.type = kZbf; + ValveData.bat = BSP_ReadVbat(); + ValveData.humi = 50; + ValveData.rssi = gValveData.rssi; + + // 解析信号强度,这里简单处理为0-5的整数值 + int signal_strength = 0; + long rssi = strtol(Service_Info_t.rssi, NULL, 16); + if (rssi > -90) signal_strength = 5; + else if (rssi > -100) signal_strength = 4; + else if (rssi > -110) signal_strength = 3; + else if (rssi > -120) signal_strength = 2; + else if (rssi > -130) signal_strength = 1; + + // 获取当前时间戳 + uint32_t timestamp = 0; + if (Network_Time_t.tm_year > 0) { + struct tm timeinfo; + timeinfo.tm_year = Network_Time_t.tm_year; + timeinfo.tm_mon = Network_Time_t.tm_mon; + timeinfo.tm_mday = Network_Time_t.tm_mday; + timeinfo.tm_hour = Network_Time_t.tm_hour; + timeinfo.tm_min = Network_Time_t.tm_min; + timeinfo.tm_sec = Network_Time_t.tm_sec; + timestamp = utc_to_timestamp(&timeinfo); + } + + // 生成MQTT格式的JSON字符串 + p4GData->length = snprintf(p4GData->json_buffer, JSON_BUFFER_SIZE, + "{" + "\"sn\":\"%s\"," + "\"t\":%u," + "\"type\":%d," + "\"data\":{" + "\"switch_status\":%d," + "\"temp\":%d," + "\"in_pressure\":%d," + "\"out_pressure\":%d," + "\"atm_pressure\":%d," + "\"type\":%d," + "\"bat\":%d," + "\"humi\":%d," + "\"rssi\":%d," + "\"signal\":%d" + "}" + "}", + device_sn, + timestamp, + cmd, + ValveData.switch_status, + ValveData.temp, + ValveData.in_pressure, + ValveData.out_pressure, + ValveData.atm_pressure, + ValveData.type, + ValveData.bat, + ValveData.humi, + ValveData.rssi, + signal_strength + ); + + // 打印JSON数据信息 + logDebug("MQTT JSON数据 (cmd=%d):\r\n%s", cmd, p4GData->json_buffer); + + return 0; +} /** * @brief 生成4G模组响应JSON数据 * @@ -680,6 +948,7 @@ uint8_t BSP_4G_Generate_Response(Ts4GJsonData *p4GData, TeFrameCmd responseCmd, Ts4GJsonData g4GJsonData; char *json_data; +/******************************************* static int SendMessage(at_env_t *e) { switch (e->state) @@ -717,6 +986,45 @@ static int SendMessage(at_env_t *e) } return 0; } +**************************************************/ +static int SendMessage(at_env_t *e) +{ + switch (e->state) + { + case 0: + logDebug("MQTT SendMessage start!\r\n"); + // 使用MQTT发布命令发送JSON数据 + e->println(e, "AT+MQTTPUB=0,\"%s\",0,0,0,0,\"%s\"", MQTT_TOPIC_UP, json_data); + e->reset_timer(e); + e->state++; + break; + case 1: + if (e->contains(e, "OK")) + { + logDebug("MQTT SendMessage end!\r\n"); + IotFlag_t.send_complete_flag = 1; + // 发送完成后释放json_data内存 + tmos_msg_deallocate((uint8_t *)json_data); + json_data = NULL; + e->finish(e, AT_RESP_OK); + } + if (e->is_timeout(e, 5000)) + { + e->state--; + if (++e->i > 3) + { + logDebug("MQTT SendMessage error!\r\n"); + IotFlag_t.send_complete_flag = 1; + // 发送失败也需要释放json_data内存 + tmos_msg_deallocate((uint8_t *)json_data); + json_data = NULL; + e->finish(e, AT_RESP_ERROR); + } + } + break; + } + return 0; +} static void BSP_SendMessage(void) {