/* * YM310 Protocol Handler - v1.1 (Optimized) * * 功能:处理 MQTT 接收到的 JSON 协议,执行相应操作并回复。 * 依赖:cJSON, EasyFlash, RT-Thread, YM310 MQTT 驱动。 * * Version: 1.1.0 */ /*********************↓ define ↓************************/ #define DBG_TAG "Y-RPT" #define DBG_LVL DBG_INFO /*********************↑ define ↑************************/ /*********************↓ include ↓************************/ #include #include #include #include #include #include #include #include #include "ym310_mqtt.h" #include "ym310_protecl.h" #include "main.h" #include "ecode.h" /*********************↑ include ↑************************/ /*********************↓ extern ↓************************/ extern struct rtc_time time_now; extern char update_res; extern char upload_res; typedef struct { char username[32]; char password[32]; char host[64]; rt_uint16_t port; char filename[128]; } ftp_url_info_t; extern int ym310_ftp_parse_url(const char *url, ftp_url_info_t *info); extern void once_test_tig(void); extern rt_bool_t gpio_lamp_get_status(void); extern uint8_t read_platform_flag(void); extern void hv_save(uint32_t c); extern uint32_t hv_read(void); extern void RESET_ALL_PARA(void); /*********************↑ extern ↑************************/ /********************↓ declaration ↓***********************/ typedef enum { WORK_STATE_IDLE = 0, WORK_STATE_WORKING, WORK_STATE_LIGHT_PRT, WORK_STATE_COOLDOWN, WORK_STATE_WAIT_DAY, WORK_STATE_OTHER_PRT } Work_status; static void format_time(char *buf, size_t size, const struct rtc_time *tm); void ym310_data_report(rt_uint8_t client_idx); int ym310_warn_report(const char *detail); static cJSON *build_devinfo_report(void); static cJSON *build_data_report(void); static cJSON *build_paramconf_response(void); static cJSON *build_serverconf_response(void); static cJSON *build_simple_response(const char *cmd, int result); static void send_response(rt_uint8_t client_idx, cJSON *resp); static void handle_read(rt_uint8_t client_idx, cJSON *root); static void handle_set_scdparam(rt_uint8_t client_idx, cJSON *root); static void handle_thirdserverconf(rt_uint8_t client_idx, cJSON *root); static void handle_paramconf(rt_uint8_t client_idx, cJSON *root); static void handle_test(rt_uint8_t client_idx, cJSON *root); static void handle_reboot(rt_uint8_t client_idx, cJSON *root); static void handle_update_stm32(rt_uint8_t client_idx, cJSON *root); static void process_json(rt_uint8_t client_idx, const char *payload); static void handle_platform(rt_uint8_t client_idx, cJSON *root); static void handle_read_ecode(rt_uint8_t client_idx, cJSON *root); static void handle_read_update_url(rt_uint8_t client_idx, cJSON *root); static void handle_set_update_url(rt_uint8_t client_idx, cJSON *root); static void handle_sensor(rt_uint8_t client_idx, cJSON *root); static void handle_read_sensor(rt_uint8_t client_idx, cJSON *root); static void handle_read_status(rt_uint8_t client_idx, cJSON *root); static void handle_chg_light_type(rt_uint8_t client_idx, cJSON *root); static void handle_location(rt_uint8_t client_idx, cJSON *root); static void handle_upload_ecode(rt_uint8_t client_idx, cJSON *root); static void handle_get_devinfo(rt_uint8_t client_idx, cJSON *root); static void handle_FORCE_RESET(rt_uint8_t client_idx, cJSON *root); static void handle_lamp_proc_on(rt_uint8_t client_idx, cJSON *root); static void handle_lamp_proc_off(rt_uint8_t client_idx, cJSON *root); static void handle_SCDV3_UP_FIRMWARE(rt_uint8_t client_idx, cJSON *root); /********************↑ declaration ↑***********************/ /*********************↓ global variables ↓************************/ SYS_INFO sys_info = {0}; DEV_INFO dev_info = {0}; LIMIT limit = {0}; sensor_t sensor = {0}; SYS_STATUS sys_sta = {0}; // 不保存 uint8_t g_work_state = 0; uint8_t g_work_mode = 0; uint32_t g_work_elapsed_sec = 0; uint32_t g_work_total_sec = 0; uint8_t g_work_progress = 0; uint32_t g_cooldown_sec = 0; uint8_t g_work_start_hour = 0; uint8_t g_work_start_minute = 0; /*********************↑ global variables ↑************************/ /*********************↓ function definitions ↓***********************/ static void format_time(char *buf, size_t size, const struct rtc_time *tm) { rt_snprintf(buf, size, "%04d%02d%02d%02d%02d%02d", tm->year, tm->month, tm->date, tm->hour, tm->minute, tm->second); } char *version_with_time(void) { static char version[40]; char *MonthTable[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; uint8_t i; char time[40]; char month_s[10]; int month_v = 0; int day_v; int year_v; int hour_v; int min_v; int sec_v; rt_sprintf((char *)time, "%s %s\r\n", __DATE__, __TIME__); sscanf(time, "%s %d %d %d:%d:%d", month_s, &day_v, &year_v, &hour_v, &min_v, &sec_v); for (i = 0; i < sizeof(MonthTable) / sizeof(MonthTable[0]); i++) { if (rt_strstr(month_s, (char *)MonthTable[i])) { month_v = i + 1; break; } } rt_sprintf(version, "%d.%d.%d-%04d/%02d/%02d/%02d/%02d", SCD_VERSION / 100, SCD_VERSION % 100 / 10, SCD_VERSION % 10, year_v, month_v, day_v, hour_v, min_v); return version; } /** * @brief 处理读取 update_url 指令 * JSON格式: {"cmd":"read_update_url"} */ static void handle_read_update_url(rt_uint8_t client_idx, cJSON *root) { (void)root; /* 不需要解析ext */ cJSON *resp = cJSON_CreateObject(); if (!resp) { LOG_E("read_update_url: Failed to create response"); return; } cJSON_AddStringToObject(resp, "cmd", "read_update_url"); cJSON *ext = cJSON_CreateObject(); if (!ext) { cJSON_Delete(resp); LOG_E("read_update_url: Failed to create ext object"); return; } cJSON_AddItemToObject(resp, "ext", ext); cJSON_AddStringToObject(ext, "url", sys_info.update_url); cJSON_AddNumberToObject(ext, "len", strlen(sys_info.update_url)); LOG_I("read_update_url: url=%s", sys_info.update_url); send_response(client_idx, resp); } /** * @brief 处理设置 update_url 指令 * JSON格式: {"cmd":"set_update_url","ext":{"url":"ftp://..."}} */ static void handle_set_update_url(rt_uint8_t client_idx, cJSON *root) { cJSON *ext = cJSON_GetObjectItem(root, "ext"); if (!ext || !cJSON_IsObject(ext)) { LOG_E("set_update_url: missing ext object"); cJSON *resp = build_simple_response("设置失败:缺少 ext对象", -1); if (resp) send_response(client_idx, resp); return; } cJSON *url_json = cJSON_GetObjectItem(ext, "url"); if (!url_json || !cJSON_IsString(url_json)) { LOG_E("set_update_url: missing or invalid url string"); cJSON *resp = build_simple_response("设置失败:缺少url字段", -1); if (resp) send_response(client_idx, resp); return; } const char *new_url = url_json->valuestring; if (!new_url || strlen(new_url) == 0) { LOG_E("set_update_url: url is empty"); cJSON *resp = build_simple_response("设置失败:URL 为空字符串", -2); if (resp) send_response(client_idx, resp); return; } if (strlen(new_url) >= sizeof(sys_info.update_url)) { LOG_E("set_update_url: url too long (%d >= %d)", strlen(new_url), sizeof(sys_info.update_url)); cJSON *resp = build_simple_response("设置失败:URL太长 (128B MAX)", -3); if (resp) send_response(client_idx, resp); return; } ftp_url_info_t url_info; if (ym310_ftp_parse_url(new_url, &url_info) != RT_EOK) { LOG_E("set_update_url: invalid FTP URL format"); cJSON *resp = build_simple_response("设置失败:URL格式无效", -4); if (resp) send_response(client_idx, resp); return; } if (strlen(url_info.host) == 0 || strlen(url_info.filename) == 0) { LOG_E("set_update_url: parsed host or filename empty"); cJSON *resp = build_simple_response("设置失败:解析后关键字段为空", -5); if (resp) send_response(client_idx, resp); return; } memset(sys_info.update_url, 0, sizeof(sys_info.update_url)); strncpy(sys_info.update_url, new_url, sizeof(sys_info.update_url) - 1); sys_info.update_url[sizeof(sys_info.update_url) - 1] = '\0'; save_sys_info(); LOG_I("set_update_url: URL updated and saved: %s", sys_info.update_url); LOG_I(" parsed: host=%s, port=%d, file=%s", url_info.host, url_info.port, url_info.filename); cJSON *resp = cJSON_CreateObject(); if (resp) { cJSON_AddStringToObject(resp, "cmd", "set_update_url"); cJSON_AddStringToObject(resp, "result", "OK"); cJSON *resp_ext = cJSON_CreateObject(); if (resp_ext) { cJSON_AddStringToObject(resp_ext, "url", sys_info.update_url); cJSON_AddStringToObject(resp_ext, "host", url_info.host); cJSON_AddNumberToObject(resp_ext, "port", url_info.port); cJSON_AddStringToObject(resp_ext, "filename", url_info.filename); cJSON_AddItemToObject(resp, "ext", resp_ext); } send_response(client_idx, resp); } } static cJSON *build_data_report(void) { cJSON *root = cJSON_CreateObject(); if (!root) return NULL; cJSON_AddStringToObject(root, "cmd", "data"); cJSON *ext = cJSON_CreateObject(); if (!ext) { cJSON_Delete(root); return NULL; } cJSON_AddItemToObject(root, "ext", ext); char stamp[15]; format_time(stamp, sizeof(stamp), &time_now); cJSON_AddStringToObject(ext, "stamp", stamp); uint32_t start_time = 0; ef_get_env_blob("start_time", &start_time, sizeof(start_time), NULL); cJSON_AddStringToObject(ext, "imei", sys_info.imei); cJSON_AddStringToObject(ext, "iccid", sys_info.iccid); cJSON_AddStringToObject(ext, "stm8vs", version_with_time()); cJSON_AddStringToObject(ext, "dver", version_with_time()); char num_buf[16]; // csq rt_snprintf(num_buf, sizeof(num_buf), "%u", sys_info.csq); cJSON_AddStringToObject(ext, "csq", num_buf); // dtype rt_snprintf(num_buf, sizeof(num_buf), "%u", DEVICE_TYPE); cJSON_AddStringToObject(ext, "dtype", num_buf); // rcnt rt_snprintf(num_buf, sizeof(num_buf), "%u", start_time); cJSON_AddStringToObject(ext, "rcnt", num_buf); // ws rt_snprintf(num_buf, sizeof(num_buf), "%u", sys_sta.work_state); cJSON_AddStringToObject(ext, "ws", num_buf); // ds rt_snprintf(num_buf, sizeof(num_buf), "%u", dev_info.power_state); cJSON_AddStringToObject(ext, "ds", num_buf); // tt rt_snprintf(num_buf, sizeof(num_buf), "%u", dev_info.light_work_duration); cJSON_AddStringToObject(ext, "tt", num_buf); // ts rt_snprintf(num_buf, sizeof(num_buf), "%u", dev_info.time_ctrl_enable); cJSON_AddStringToObject(ext, "ts", num_buf); // st rt_snprintf(num_buf, sizeof(num_buf), "%u", dev_info.time_start); cJSON_AddStringToObject(ext, "st", num_buf); // et rt_snprintf(num_buf, sizeof(num_buf), "%u", dev_info.time_end); cJSON_AddStringToObject(ext, "et", num_buf); // vps if (sys_sta.undervolt_protect == VALID) { cJSON_AddStringToObject(ext, "vps", "1"); } else { cJSON_AddStringToObject(ext, "vps", "0"); } // tps if (sys_sta.temp_protect == 0x7A) { cJSON_AddStringToObject(ext, "tps", "1"); } else if (sys_sta.temp_protect == VALID) { cJSON_AddStringToObject(ext, "tps", "2"); } else { cJSON_AddStringToObject(ext, "tps", "0"); } // dps if (sys_sta.tilt_protect == VALID) { cJSON_AddStringToObject(ext, "dps", "1"); } else { cJSON_AddStringToObject(ext, "dps", "0"); } // rps if (sys_sta.rain_protect == VALID) { cJSON_AddStringToObject(ext, "rps", "1"); } else { cJSON_AddStringToObject(ext, "rps", "0"); } if (sys_sta.light_protect == VALID) { cJSON_AddStringToObject(ext, "lps", "1"); } else { cJSON_AddStringToObject(ext, "lps", "0"); } // ct rt_snprintf(num_buf, sizeof(num_buf), "%u", sys_sta.hv_count); cJSON_AddStringToObject(ext, "ct", num_buf); // 电压部分 char buf[16]; // 充电电压 int cv_int = sys_sta.charge_voltage / 1000; int cv_dec = sys_sta.charge_voltage % 1000; rt_snprintf(buf, sizeof(buf), "%d.%03d", cv_int, cv_dec); cJSON_AddStringToObject(ext, "cv", buf); // 电池电压 int bv_int = sys_sta.battery_voltage / 1000; int bv_dec = sys_sta.battery_voltage % 1000; rt_snprintf(buf, sizeof(buf), "%d.%03d", bv_int, bv_dec); cJSON_AddStringToObject(ext, "bv", buf); // 主板温度 int bt_int = sys_sta.board_temp / 10; rt_snprintf(buf, sizeof(buf), "%d", bt_int); cJSON_AddStringToObject(ext, "bt", buf); /* ========== 传感器数据联动逻辑 ========== * 优先级:SHT30 > (NTC温度 + PLAT湿度) > PLAT > 默认值 * - 温度:SHT30 > NTC > PLAT > 22.0 * - 湿度:SHT30 > PLAT > 33.0 */ uint8_t temp_source = 0; /* 温度来源:1=SHT,2=NTC,3=PLAT,4=默认 */ uint8_t humi_source = 0; /* 湿度来源:1=SHT,2=PLAT,3=默认 */ /* 1. 优先使用 SHT30 数据 */ if (sensor.valid_sht == VALID) { temp_source = 1; humi_source = 1; LOG_D("Sensor: using SHT30 data (temp=%d.%d°C, humi=%d.%d%%)", sensor.temp_sht / 10, sensor.temp_sht % 10, sensor.humi_sht / 10, sensor.humi_sht % 10); } /* 2. SHT30 不可用,尝试组合:NTC温度 + PLAT湿度 */ else if (sensor.valid_ntc == VALID && sensor.valid_plat == VALID) { temp_source = 2; humi_source = 2; LOG_D("Sensor: using NTC temp + PLAT humi (temp=%d.%d°C, humi=%d.%d%%)", sensor.temp_ntc / 10, sensor.temp_ntc % 10, sensor.humi_plat / 10, sensor.humi_plat % 10); } /* 4. 使用 NTC 温度,湿度使用默认值 */ else if (sensor.valid_ntc == VALID) { temp_source = 2; humi_source = 3; LOG_D("Sensor: using NTC temp only (temp=%d.%d°C), humidity default 33.0%%", sensor.temp_ntc / 10, sensor.temp_ntc % 10); } /* 3. 使用 PLAT 数据 */ else if (sensor.valid_plat == VALID) { temp_source = 3; humi_source = 2; LOG_D("Sensor: using PLAT data (temp=%d.%d°C, humi=%d.%d%%)", sensor.temp_plat / 10, sensor.temp_plat % 10, sensor.humi_plat / 10, sensor.humi_plat % 10); } /* 5. 所有传感器都无效,使用默认值 */ else { temp_source = 4; humi_source = 3; LOG_W("Sensor: ALL sensors invalid, using default values (temp=22.0°C, humi=33.0%%)"); } switch (temp_source) { case 1: cJSON_AddStringToObject(ext, "at_source", "SHT30"); rt_snprintf(buf, sizeof(buf), "%d.%d", sensor.temp_sht / 10, sensor.temp_sht % 10); cJSON_AddStringToObject(ext, "at", buf); break; case 2: cJSON_AddStringToObject(ext, "at_source", "NTC"); rt_snprintf(buf, sizeof(buf), "%d.%d", sensor.temp_ntc / 10, sensor.temp_ntc % 10); cJSON_AddStringToObject(ext, "at", buf); break; case 3: cJSON_AddStringToObject(ext, "at_source", "PLAT"); rt_snprintf(buf, sizeof(buf), "%d.%d", sensor.temp_plat / 10, sensor.temp_plat % 10); cJSON_AddStringToObject(ext, "at", buf); break; case 4: cJSON_AddStringToObject(ext, "at_source", "default"); rt_snprintf(buf, sizeof(buf), "%d.%d", 22 / 10, 22 % 10); cJSON_AddStringToObject(ext, "at", buf); break; default: break; } switch (humi_source) { case 1: cJSON_AddStringToObject(ext, "ah_source", "SHT30"); rt_snprintf(buf, sizeof(buf), "%d.%d", sensor.humi_sht / 10, sensor.humi_sht % 10); cJSON_AddStringToObject(ext, "ah", buf); break; case 2: cJSON_AddStringToObject(ext, "ah_source", "PLAT"); rt_snprintf(buf, sizeof(buf), "%d.%d", sensor.humi_plat / 10, sensor.humi_plat % 10); cJSON_AddStringToObject(ext, "ah", buf); break; case 3: cJSON_AddStringToObject(ext, "ah_source", "default"); rt_snprintf(buf, sizeof(buf), "%d.%d", 33 / 10, 33 % 10); cJSON_AddStringToObject(ext, "ah", buf); break; default: break; } if (sys_sta.lamp_fault == VALID) // 短路 { cJSON_AddStringToObject(ext, "voltval", "2"); } else if (sys_sta.lamp_fault == 0x7A) // 开路 { cJSON_AddStringToObject(ext, "voltval", "3"); } else if (gpio_lamp_get_status()) // 开 { cJSON_AddStringToObject(ext, "voltval", "1"); } else // 关 { cJSON_AddStringToObject(ext, "voltval", "0"); } /* 灯管故障 */ // cycle_off rt_snprintf(num_buf, sizeof(num_buf), "%u", sys_sta.idle_flag); cJSON_AddStringToObject(ext, "cycle_off", num_buf); // dat_f rt_snprintf(num_buf, sizeof(num_buf), "%u", dev_info.data_interval); cJSON_AddStringToObject(ext, "dat_f", num_buf); // clt_t rt_snprintf(num_buf, sizeof(num_buf), "%u", dev_info.clean_interval); cJSON_AddStringToObject(ext, "clt_t", num_buf); // clear_on rt_snprintf(num_buf, sizeof(num_buf), "%u", dev_info.clean_enable); cJSON_AddStringToObject(ext, "clear_on", num_buf); // gps rt_snprintf(num_buf, sizeof(num_buf), "%u", dev_info.GPS_MODE); cJSON_AddStringToObject(ext, "gps", num_buf); if (dev_info.GPS_MODE == 0) { cJSON_AddStringToObject(ext, "lat", dev_info.latitude_FORCE); cJSON_AddStringToObject(ext, "lng", dev_info.longitude_FORCE); } else if (dev_info.GPS_MODE == 1) { cJSON_AddStringToObject(ext, "lat", dev_info.latitude); cJSON_AddStringToObject(ext, "lng", dev_info.longitude); } cJSON_AddNumberToObject(ext, "light_pct", sys_sta.light_percent / 10 + (sys_sta.light_percent % 10 / 5)); cJSON_AddNumberToObject(ext, "lamp_current", sys_sta.lamp_current); if (dev_info.light_flag == 0) { cJSON_AddStringToObject(ext, "light_source", "太阳能板"); } else if (dev_info.light_flag == 1) { cJSON_AddStringToObject(ext, "light_source", "光敏管"); } cJSON_AddStringToObject(ext, "scd_ver", "3"); return root; } /** * @brief 构建数据上报响应(副平台格式) * JSON格式: {"cmd":"data","ext":{...}} * 对应查询指令: {"cmd":"read","ext":"data"} */ static cJSON *build_data_report_vice(void) { cJSON *root = cJSON_CreateObject(); if (!root) { LOG_E("build_data_report_vice: failed to create root"); return NULL; } cJSON_AddStringToObject(root, "cmd", "data"); cJSON *ext = cJSON_CreateObject(); if (!ext) { LOG_E("build_data_report_vice: failed to create ext"); cJSON_Delete(root); return NULL; } cJSON_AddItemToObject(root, "ext", ext); /* 时间戳 */ char stamp[15] = {0}; format_time(stamp, sizeof(stamp), &time_now); cJSON_AddStringToObject(ext, "stamp", stamp); /* 设备标识 */ cJSON_AddStringToObject(ext, "imei", sys_info.imei); cJSON_AddStringToObject(ext, "stm8vs", version_with_time()); /* 开关机状态 ds */ cJSON_AddNumberToObject(ext, "ds", dev_info.power_state); /* 时控切换标志位 tcs (等同于timctrl) */ cJSON_AddNumberToObject(ext, "tcs", dev_info.time_ctrl_enable); cJSON_AddNumberToObject(ext, "tbs", 0); // 备用字段,暂时为0 /* 充电电压 cv (mV -> V,保留整数) */ cJSON_AddNumberToObject(ext, "cv", sys_sta.charge_voltage / 1000); /* 高压计数 ct */ cJSON_AddNumberToObject(ext, "ct", sys_sta.hv_count); /* 欠压状态 vps */ cJSON_AddNumberToObject(ext, "vps", sys_sta.undervolt_protect == VALID ? 1 : 0); /* 自清虫标记位 lcm (默认0,暂未实现) */ cJSON_AddNumberToObject(ext, "lcm", 0); /* 清虫时间间隔 clt (分钟) */ cJSON_AddNumberToObject(ext, "clt", dev_info.clean_interval); /* GPS源 gps */ cJSON_AddNumberToObject(ext, "gps", dev_info.GPS_MODE); /* 重启次数 rcnt */ uint32_t start_time = 0; ef_get_env_blob("start_time", &start_time, sizeof(start_time), NULL); cJSON_AddNumberToObject(ext, "rcnt", start_time); /* 工作状态 ws */ cJSON_AddNumberToObject(ext, "ws", sys_sta.work_state); /* 经纬度 */ if (dev_info.GPS_MODE == 0) { cJSON_AddStringToObject(ext, "lat", dev_info.latitude_FORCE); cJSON_AddStringToObject(ext, "lng", dev_info.longitude_FORCE); } else if (dev_info.GPS_MODE == 1) { cJSON_AddStringToObject(ext, "lat", dev_info.latitude); cJSON_AddStringToObject(ext, "lng", dev_info.longitude); } /* 时控时间 */ cJSON_AddNumberToObject(ext, "st", dev_info.time_start); cJSON_AddNumberToObject(ext, "et", dev_info.time_end); cJSON_AddNumberToObject(ext, "tt", dev_info.light_work_duration); /* ========== 环境温度 at (浮点数转字符串) ========== */ int16_t temp_value = 0; if (sensor.valid_sht == VALID) temp_value = sensor.temp_sht; else if (sensor.valid_ntc == VALID) temp_value = sensor.temp_ntc; else if (sensor.valid_plat == VALID) temp_value = sensor.temp_plat; else temp_value = 220; // 默认22.0°C cJSON_AddNumberToObject(ext, "at", temp_value); // 直接添加整数,如 302 表示 30.2°C /* ========== 环境湿度 ah (浮点数转字符串) ========== */ int16_t humi_value = 0; if (sensor.valid_sht == VALID) humi_value = sensor.humi_sht; else if (sensor.valid_plat == VALID) humi_value = sensor.humi_plat; else humi_value = 330; // 默认33.0% cJSON_AddNumberToObject(ext, "ah", humi_value); // 直接添加整数,如 183 表示 18.3% /* 温控状态 tps */ if (sys_sta.temp_protect == 0x7A) cJSON_AddNumberToObject(ext, "tps", 2); // 过温保护 else if (sys_sta.temp_protect == VALID) cJSON_AddNumberToObject(ext, "tps", 1); // 低温保护 else cJSON_AddNumberToObject(ext, "tps", 0); // 正常 /* 雨控保护状态 rps */ cJSON_AddNumberToObject(ext, "rps", sys_sta.rain_protect == VALID ? 1 : 0); /* 倾倒保护状态 dps */ cJSON_AddNumberToObject(ext, "dps", sys_sta.tilt_protect == VALID ? 1 : 0); /* 光控保护状态 lps */ cJSON_AddNumberToObject(ext, "lps", sys_sta.light_protect == VALID ? 1 : 0); /* 电池电压 bv (mV) */ cJSON_AddNumberToObject(ext, "bv", sys_sta.battery_voltage); LOG_D("build_data_report_vice: temp=%d.%d, humi=%d.%d", temp_int, temp_dec, humi_int, humi_dec); return root; } /** * @brief 构建设备状态上报响应(副平台格式) * JSON格式: {"cmd":"status","ext":{...}} * 对应查询指令: {"cmd":"read","ext":"status"} */ static cJSON *build_status_report_vice(void) { cJSON *root = cJSON_CreateObject(); if (!root) return NULL; cJSON_AddStringToObject(root, "cmd", "status"); cJSON *ext = cJSON_CreateObject(); if (!ext) { cJSON_Delete(root); return NULL; } cJSON_AddItemToObject(root, "ext", ext); char stamp[15]; format_time(stamp, sizeof(stamp), &time_now); cJSON_AddStringToObject(ext, "stamp", stamp); cJSON_AddStringToObject(ext, "stm8vs", version_with_time()); cJSON_AddStringToObject(ext, "dver", version_with_time()); cJSON_AddStringToObject(ext, "imei", sys_info.imei); cJSON_AddStringToObject(ext, "iccid", sys_info.iccid); /* 设备类型 dtype */ cJSON_AddNumberToObject(ext, "dtype", DEVICE_TYPE); /* 信号强度 csq */ cJSON_AddNumberToObject(ext, "csq", sys_info.csq); /* 开关机状态 ds */ cJSON_AddNumberToObject(ext, "ds", dev_info.power_state); /* 时控相关 */ cJSON_AddNumberToObject(ext, "tcs", dev_info.time_ctrl_enable); cJSON_AddNumberToObject(ext, "tbs", 0); cJSON_AddNumberToObject(ext, "st", dev_info.time_start); cJSON_AddNumberToObject(ext, "et", dev_info.time_end); cJSON_AddNumberToObject(ext, "tt", dev_info.light_work_duration); /* 上报间隔 dattim (分钟) */ cJSON_AddNumberToObject(ext, "dattim", dev_info.data_interval); /* 清虫时间间隔 clt */ cJSON_AddNumberToObject(ext, "clt", dev_info.clean_interval); cJSON_AddNumberToObject(ext, "lcm", 0); /* 工作状态 ws */ cJSON_AddNumberToObject(ext, "ws", sys_sta.work_state); /* 保护状态 */ cJSON_AddNumberToObject(ext, "vps", sys_sta.undervolt_protect == VALID ? 1 : 0); cJSON_AddNumberToObject(ext, "tps", sys_sta.temp_protect == VALID ? 1 : 0); cJSON_AddNumberToObject(ext, "dps", sys_sta.tilt_protect == VALID ? 1 : 0); cJSON_AddNumberToObject(ext, "rps", sys_sta.rain_protect == VALID ? 1 : 0); cJSON_AddNumberToObject(ext, "lps", sys_sta.light_protect == VALID ? 1 : 0); /* GPS相关 */ cJSON_AddNumberToObject(ext, "gps", dev_info.GPS_MODE); cJSON_AddStringToObject(ext, "lat", dev_info.latitude); cJSON_AddStringToObject(ext, "lng", dev_info.longitude); /* 电池电压 voltval (mV,整数) */ cJSON_AddNumberToObject(ext, "voltval", sys_sta.battery_voltage); /* ========== 主板温度 bt (浮点数转字符串) ========== */ cJSON_AddNumberToObject(ext, "bt", sys_sta.board_temp); // board_temp 本来就是 *10 的值 return root; } static cJSON *build_paramconf_response(void) { cJSON *root = cJSON_CreateObject(); if (!root) return NULL; cJSON_AddStringToObject(root, "cmd", "Set_scdparam"); cJSON *ext = cJSON_CreateObject(); if (!ext) { cJSON_Delete(root); return NULL; } cJSON_AddItemToObject(root, "ext", ext); cJSON_AddNumberToObject(ext, "lcm", 0); cJSON_AddNumberToObject(ext, "clear_on", 0); cJSON_AddNumberToObject(ext, "vsunrise", limit.vsunrise); cJSON_AddNumberToObject(ext, "vsunset", limit.vsunset); cJSON_AddNumberToObject(ext, "vcutof", limit.vcutof); cJSON_AddNumberToObject(ext, "vfloat", limit.vfloat); cJSON_AddNumberToObject(ext, "vrecharge", limit.vrecharge); cJSON_AddNumberToObject(ext, "uvstart", limit.uvstart); cJSON_AddNumberToObject(ext, "uvoff", limit.uvoff); cJSON_AddNumberToObject(ext, "uvon", limit.uvon); cJSON_AddNumberToObject(ext, "overtemp", limit.overtemp); cJSON_AddNumberToObject(ext, "lowtemp", limit.lowtemp); cJSON_AddNumberToObject(ext, "unovert", limit.unovert); cJSON_AddNumberToObject(ext, "unlowt", limit.unlowt); cJSON_AddNumberToObject(ext, "battype", limit.battype); cJSON_AddNumberToObject(ext, "vstart", 0); cJSON_AddNumberToObject(ext, "version", 0); return root; } static cJSON *build_serverconf_response(void) { cJSON *root = cJSON_CreateObject(); if (!root) return NULL; cJSON_AddStringToObject(root, "cmd", "serverconf"); cJSON *ext = cJSON_CreateObject(); if (!ext) { cJSON_Delete(root); return NULL; } cJSON_AddItemToObject(root, "ext", ext); const SERVER_CFG *cfg = &sys_info.customer_cfg; cJSON_AddStringToObject(ext, "sub", cfg->sub_topic); cJSON_AddStringToObject(ext, "pub", cfg->pub_topic); cJSON_AddStringToObject(ext, "ip", cfg->host); cJSON_AddNumberToObject(ext, "port", cfg->port); cJSON_AddNumberToObject(ext, "qos", cfg->qos); cJSON_AddStringToObject(ext, "lastwill", cfg->lastwill); cJSON_AddStringToObject(ext, "uid", cfg->username); cJSON_AddStringToObject(ext, "pwd", cfg->password); cJSON_AddStringToObject(ext, "cid", cfg->client_id); cJSON_AddNumberToObject(ext, "enable", (cfg->enable == VALID) ? 1 : 0); return root; } static cJSON *build_simple_response(const char *cmd, int result) { cJSON *root = cJSON_CreateObject(); if (!root) return NULL; cJSON_AddStringToObject(root, "cmd", cmd); cJSON_AddStringToObject(root, "result", result == 0 ? "ok" : "error"); return root; } static void send_response(rt_uint8_t client_idx, cJSON *resp) { if (!resp) return; char *json_str = cJSON_PrintUnformatted(resp); if (json_str) { LOG_D("Send response: %s", json_str); const char *pub_topic = g_mqtt_instances[client_idx].config.pub_topic; ym310_mqtt_send_to(client_idx, pub_topic, json_str, rt_strlen(json_str), g_mqtt_instances[client_idx].config.qos); cJSON_free(json_str); } cJSON_Delete(resp); } static void handle_platform(rt_uint8_t client_idx, cJSON *root) { cJSON *ext = cJSON_GetObjectItem(root, "ext"); if (!ext || !cJSON_IsObject(ext)) { LOG_E("platform missing ext object"); return; } cJSON *type = cJSON_GetObjectItem(ext, "type"); if (!type || !cJSON_IsString(type)) { LOG_E("platform missing type string"); return; } const char *type_str = type->valuestring; uint8_t new_flag; if (strcmp(type_str, "MAIN") == 0) { new_flag = 0x00; /* 主平台标志(非VALID) */ Recode_e_code(e_platform_chg, 0, "指令切换->主平台"); } else if (strcmp(type_str, "VICE") == 0) { new_flag = PLATFORM_VICE_MAGIC; /* VALID */ Recode_e_code(e_platform_chg, 1, "指令切换->副平台"); } else if (strcmp(type_str, "120.27.222.26") == 0) { new_flag = PLATFORM_VICE_MAGIC; /* VALID */ Recode_e_code(e_platform_chg, 2, "平台切换->副平台"); } else { LOG_W("Unknown platform type: %s", type_str); return; } ef_set_env_blob(EF_PLATFORM_FLAG, &new_flag, sizeof(new_flag)); LOG_I("Platform flag set to %s, will reboot to take effect", type_str); cJSON *resp = build_simple_response("platform", 0); if (resp) send_response(client_idx, resp); rt_thread_mdelay(1000); rt_hw_cpu_reset(); } static void handle_read(rt_uint8_t client_idx, cJSON *root) { cJSON *ext = cJSON_GetObjectItem(root, "ext"); if (!ext || !cJSON_IsString(ext)) { LOG_E("read cmd missing ext string"); return; } const char *ext_str = ext->valuestring; if (strcmp(ext_str, "data") == 0) { cJSON *resp = NULL; if (read_platform_flag() == 0) { resp = build_data_report(); } else { resp = build_data_report_vice(); } if (resp) { send_response(client_idx, resp); } } else if (strcmp(ext_str, "status") == 0 && (read_platform_flag() == 1)) { cJSON *resp = build_status_report_vice(); // 新函数 if (resp) send_response(client_idx, resp); } else if (strcmp(ext_str, "paramconf") == 0) { cJSON *resp = build_paramconf_response(); if (resp) send_response(client_idx, resp); } else if (strcmp(ext_str, "thirdserverconf") == 0) { cJSON *resp = build_serverconf_response(); if (resp) send_response(client_idx, resp); } else { LOG_W("Unknown read ext: %s", ext_str); } } static void handle_set_scdparam(rt_uint8_t client_idx, cJSON *root) { cJSON *ext = cJSON_GetObjectItem(root, "ext"); if (!ext || !cJSON_IsObject(ext)) { LOG_E("Set_scdparam missing ext object"); return; } cJSON *item; char need_save = 0; // vsunrise: 日出电压 item = cJSON_GetObjectItem(ext, "vsunrise"); if (item && cJSON_IsNumber(item)) { int val = item->valueint; if (val >= 1000 && val <= 5000) // 1-5V { limit.vsunrise = val; need_save = 1; } else { LOG_W("vsunrise value %d out of range[1000,5000]", val); } } // vsunset: 日落电压 item = cJSON_GetObjectItem(ext, "vsunset"); if (item && cJSON_IsNumber(item)) { int val = item->valueint; if (val >= 1000 && val <= 5000) // 1-5V { limit.vsunset = val; need_save = 1; } else { LOG_W("vsunset value %d out of range[1000,5000]", val); } } // vcutof: 截止电压 item = cJSON_GetObjectItem(ext, "vcutof"); if (item && cJSON_IsNumber(item)) { int val = item->valueint; if (val >= 1000 && val <= 5000) // 1-5V { limit.vcutof = val; need_save = 1; } else { LOG_W("vcutof value %d out of range [1000,5000]", val); } } // vfloat: 浮充电压 item = cJSON_GetObjectItem(ext, "vfloat"); if (item && cJSON_IsNumber(item)) { int val = item->valueint; if (val >= 1000 && val <= 5000) // 1-5V { limit.vfloat = val; need_save = 1; } else { LOG_W("vfloat value %d out of range [1000,5000]", val); } } // vrecharge: 再次开启充电电压 item = cJSON_GetObjectItem(ext, "vrecharge"); if (item && cJSON_IsNumber(item)) { int val = item->valueint; if (val >= 1000 && val <= 5000) // 1-5V { limit.vrecharge = val; need_save = 1; } else { LOG_W("vrecharge value %d out of range[1000,5000]", val); } } item = cJSON_GetObjectItem(ext, "uvstart"); if (item && cJSON_IsNumber(item)) { int val = item->valueint; if (val >= 1000 && val <= 5000) // 1-5V { limit.uvstart = val; need_save = 1; } else { LOG_W("uvstart value %d out of range[1000,5000]", val); } } // uvoff: 欠压关闭电压 item = cJSON_GetObjectItem(ext, "uvoff"); if (item && cJSON_IsNumber(item)) { int val = item->valueint; if (val >= 1000 && val <= 5000) // 1-5V { limit.uvoff = val; need_save = 1; } else { LOG_W("uvoff value %d out of range[1000,5000]", val); } } // uvon: 欠压开始电压 item = cJSON_GetObjectItem(ext, "uvon"); if (item && cJSON_IsNumber(item)) { int val = item->valueint; if (val >= 1000 && val <= 5000) // 1-5V { limit.uvon = val; need_save = 1; } else { LOG_W("uvon value %d out of range[1000,5000]", val); } } // overtemp: 过温保护温度 item = cJSON_GetObjectItem(ext, "overtemp"); if (item && cJSON_IsNumber(item)) { int val = item->valueint; if (val >= 30 && val <= 80) { limit.overtemp = val * 10; need_save = 1; } else { LOG_W("overtemp value %d out of range[30,80]", val); } } // lowtemp: 低温保护温度,范围 -20°C - 5°C item = cJSON_GetObjectItem(ext, "lowtemp"); if (item && cJSON_IsNumber(item)) { int val = item->valueint; if (val >= -20 && val <= 10) { limit.lowtemp = val * 10; need_save = 1; } else { LOG_W("lowtemp value %d out of range [-20,10]", val); } } // unovert: 过温恢复温度 item = cJSON_GetObjectItem(ext, "unovert"); if (item && cJSON_IsNumber(item)) { int val = item->valueint; if (val >= 20 && val <= 70) { limit.unovert = val * 10; need_save = 1; } else { LOG_W("unovert value %d out of range [20,70]", val); } } // unlowt: 低温恢复温度 item = cJSON_GetObjectItem(ext, "unlowt"); if (item && cJSON_IsNumber(item)) { int val = item->valueint; if (val >= -15 && val <= 15) { limit.unlowt = val * 10; need_save = 1; } else { LOG_W("unlowt value %d out of range [-15,15]", val); } } // battype: 电池类型,0-铅酸,1-锂电池,2-胶体电池等 item = cJSON_GetObjectItem(ext, "battype"); if (item && cJSON_IsNumber(item)) { int val = item->valueint; if (val >= 0 && val <= 5) // 假设支持0-5种电池类型 { limit.battype = val; need_save = 1; } else { LOG_W("battype value %d out of range [0,5]", val); } } if (need_save) { save_limit(); LOG_I("Limit params updated"); cJSON *resp = build_simple_response("Set_scdparam", 0); if (resp) send_response(client_idx, resp); } else { LOG_W("No valid parameters to update"); cJSON *resp = build_simple_response("Set_scdparam", -1); // -1 表示参数无效 if (resp) send_response(client_idx, resp); } } static void handle_thirdserverconf(rt_uint8_t client_idx, cJSON *root) { cJSON *ext = cJSON_GetObjectItem(root, "ext"); if (!ext || !cJSON_IsObject(ext)) { LOG_E("thirdserverconf missing ext object"); return; } SERVER_CFG *cfg = &sys_info.customer_cfg; cJSON *item; #define UPDATE_STR(json_field, member) \ do \ { \ item = cJSON_GetObjectItem(ext, json_field); \ if (item && cJSON_IsString(item)) \ rt_strncpy(cfg->member, item->valuestring, sizeof(cfg->member) - 1); \ } while (0) #define UPDATE_INT(json_field, member) \ do \ { \ item = cJSON_GetObjectItem(ext, json_field); \ if (item && cJSON_IsNumber(item)) \ cfg->member = item->valueint; \ } while (0) UPDATE_STR("sub", sub_topic); UPDATE_STR("pub", pub_topic); UPDATE_STR("ip", host); UPDATE_INT("port", port); UPDATE_INT("qos", qos); UPDATE_STR("lastwill", lastwill); UPDATE_STR("uid", username); UPDATE_STR("pwd", password); UPDATE_STR("cid", client_id); #undef UPDATE_STR #undef UPDATE_INT cfg->enable = VALID; save_sys_info(); LOG_I("Server config updated, enable set to VALID (will take effect after reboot)"); Recode_e_code(e_mqtt_cmd, 2, "平台指令:修改对接服务器信息"); cJSON *resp = build_simple_response("thirdserverconf", 0); if (resp) send_response(client_idx, resp); } static void handle_paramconf(rt_uint8_t client_idx, cJSON *root) { cJSON *ext = cJSON_GetObjectItem(root, "ext"); if (!ext || !cJSON_IsObject(ext)) { LOG_E("paramconf missing ext object"); return; } cJSON *item; char need_save = 0; char need_restart = 0; item = cJSON_GetObjectItem(ext, "timctrl"); if (item && cJSON_IsString(item)) { if (atoi(item->valuestring) == 0 || atoi(item->valuestring) == 1) { if (dev_info.time_ctrl_enable != atoi(item->valuestring)) { dev_info.time_ctrl_enable = atoi(item->valuestring); need_save = 1; need_restart = 1; if (atoi(item->valuestring)) { Recode_e_code(e_mqtt_cmd, 9, "平台指令:改为时控模式"); } else { Recode_e_code(e_mqtt_cmd, 9, "平台指令:改为光控模式"); } } } } item = cJSON_GetObjectItem(ext, "st"); if (item && cJSON_IsString(item)) { if (atoi(item->valuestring) >= 0 && atoi(item->valuestring) <= 23) { if (dev_info.time_start != atoi(item->valuestring)) { dev_info.time_start = atoi(item->valuestring); need_save = 1; need_restart = 1; Recode_e_code(e_mqtt_cmd, 3, "平台指令:修改时控开启时间"); } } } item = cJSON_GetObjectItem(ext, "et"); if (item && cJSON_IsString(item)) { if (atoi(item->valuestring) >= 0 && atoi(item->valuestring) <= 23) { if (dev_info.time_end != atoi(item->valuestring)) { dev_info.time_end = atoi(item->valuestring); need_save = 1; need_restart = 1; Recode_e_code(e_mqtt_cmd, 4, "平台指令:修改时控结束时间"); } } } item = cJSON_GetObjectItem(ext, "dattim"); if (item && cJSON_IsString(item)) { if (atoi(item->valuestring) >= 5 && atoi(item->valuestring) <= 180) { if (dev_info.data_interval != atoi(item->valuestring)) { dev_info.data_interval = atoi(item->valuestring); need_save = 1; Recode_e_code(e_mqtt_cmd, 7, "平台指令:修改上报间隔"); } } } item = cJSON_GetObjectItem(ext, "clt"); if (item && cJSON_IsString(item)) dev_info.clean_interval = atoi(item->valuestring); item = cJSON_GetObjectItem(ext, "tt"); if (item && cJSON_IsString(item)) { if (atoi(item->valuestring) >= 0 && atoi(item->valuestring) <= 10) { if (dev_info.light_work_duration != atoi(item->valuestring)) { dev_info.light_work_duration = atoi(item->valuestring); need_save = 1; need_restart = 1; Recode_e_code(e_mqtt_cmd, 5, "平台指令:修改光控时长"); } } } item = cJSON_GetObjectItem(ext, "ts"); if (item && cJSON_IsString(item)) { if (atoi(item->valuestring) >= 0 && atoi(item->valuestring) <= 1) { if (dev_info.time_ctrl_enable != atoi(item->valuestring)) { dev_info.time_ctrl_enable = atoi(item->valuestring); need_save = 1; need_restart = 1; if (atoi(item->valuestring)) { Recode_e_code(e_mqtt_cmd, 9, "平台指令:改为时控模式"); } else { Recode_e_code(e_mqtt_cmd, 9, "平台指令:改为光控模式"); } } } } item = cJSON_GetObjectItem(ext, "ds"); if (item && cJSON_IsString(item)) { if (atoi(item->valuestring) == 0 || atoi(item->valuestring) == 1) { if (dev_info.power_state != atoi(item->valuestring)) { dev_info.power_state = atoi(item->valuestring); need_save = 1; if (dev_info.power_state) { Recode_e_code(e_mqtt_cmd, 6, "平台指令:开机"); } else { Recode_e_code(e_mqtt_cmd, 0, "平台指令:关机"); } } } } /* 新增:解析 lng 和 lat 字符串 */ item = cJSON_GetObjectItem(ext, "lng"); if (item && cJSON_IsString(item)) { rt_strncpy(dev_info.longitude, item->valuestring, sizeof(dev_info.longitude) - 1); } item = cJSON_GetObjectItem(ext, "lat"); if (item && cJSON_IsString(item)) { rt_strncpy(dev_info.latitude, item->valuestring, sizeof(dev_info.latitude) - 1); } if (need_restart) { if ((dev_info.power_state == 1) && (g_work_state != 0)) { dev_info.power_state = 0; rt_thread_mdelay(300); dev_info.power_state = 1; Recode_e_code(e_CMD_restartwork, 0, "平台指令生效:复位工作状态"); } } if (need_save) { save_dev_info(); LOG_I("Device params updated"); cJSON *resp = build_simple_response("paramconf", 0); if (resp) { send_response(client_idx, resp); } ym310_data_report(0); } else { LOG_W("No valid parameters to update"); cJSON *resp = build_simple_response("paramconf", -1); // -1 表示参数无效 if (resp) { send_response(client_idx, resp); } } } static void handle_test(rt_uint8_t client_idx, cJSON *root) { (void)root; LOG_I("Test command received"); once_test_tig(); cJSON *resp = build_simple_response("test", 0); if (resp) send_response(client_idx, resp); } static void handle_chg_light_type(rt_uint8_t client_idx, cJSON *root) { (void)root; LOG_I("handle_chg_light_type"); cJSON *resp = NULL; resp = NULL; if (dev_info.light_flag == 0) { dev_info.light_flag = 1; cJSON *resp = build_simple_response("切换光控源=光敏二极管", 0); Recode_e_code(e_mqtt_cmd, 8, "平台指令:切换光控源=光敏二极管"); } else { dev_info.light_flag = 0; cJSON *resp = build_simple_response("切换光控源=太阳能板", 0); Recode_e_code(e_mqtt_cmd, 9, "平台指令:切换光控源=太阳能板"); } save_dev_info(); if (resp) { send_response(client_idx, resp); } } static void handle_reboot(rt_uint8_t client_idx, cJSON *root) { (void)root; LOG_I("Reboot command received, will restart in 1 second"); cJSON *resp = build_simple_response("reboot", 0); if (resp) send_response(client_idx, resp); rt_thread_mdelay(1000); rt_hw_cpu_reset(); } static void handle_update_stm32(rt_uint8_t client_idx, cJSON *root) { (void)root; LOG_I("STM32 update command received"); cJSON *resp = build_simple_response("update_stm32", 0); if (resp) { send_response(client_idx, resp); } char up_flag = VALID; ef_set_env_blob("up_flag", &up_flag, sizeof(up_flag)); rt_thread_mdelay(500); Recode_e_code(e_update, 0, "升级:接收到下发的指令"); rt_thread_mdelay(500); rt_hw_cpu_reset(); } static void handle_SCDV3_UP_FIRMWARE(rt_uint8_t client_idx, cJSON *root) { (void)root; LOG_I("handle_SCDV3_UP_FIRMWARE"); cJSON *resp = build_simple_response("SCDV3_UP_FIRMWARE", 0); if (resp) { send_response(client_idx, resp); } char up_flag = VALID; ef_set_env_blob("up_flag", &up_flag, sizeof(up_flag)); rt_thread_mdelay(500); Recode_e_code(e_update, 0, "升级:接收到下发的专用指令"); rt_thread_mdelay(500); rt_hw_cpu_reset(); } /* 开启客户MQTT(仅修改标志,下次重启生效) */ static void handle_enable_customer(rt_uint8_t client_idx, cJSON *root) { (void)root; sys_info.customer_cfg.enable = VALID; save_sys_info(); LOG_I("Customer MQTT enable flag set to VALID (will take effect after reboot)"); cJSON *resp = build_simple_response("enable_customer", 0); if (resp) send_response(client_idx, resp); } /* 关闭客户MQTT */ static void handle_disable_customer(rt_uint8_t client_idx, cJSON *root) { (void)root; sys_info.customer_cfg.enable = 0; save_sys_info(); LOG_I("Customer MQTT enable flag set to 0 (will take effect after reboot)"); cJSON *resp = build_simple_response("disable_customer", 0); if (resp) send_response(client_idx, resp); } /* 字符串转整数(支持小数)例如 "2.5" -> 25, "30" -> 300 */ static int16_t str_to_temp_humi(const char *str) { int integer = 0; int decimal = 0; int dot_pos = -1; int len = strlen(str); /* 查找小数点位置 */ for (int i = 0; i < len; i++) { if (str[i] == '.') { dot_pos = i; break; } } if (dot_pos == -1) { /* 无小数点,如 "30" -> 300 */ integer = atoi(str); return (int16_t)(integer * 10); } else { /* 有小数点,如 "2.5" -> 25 */ char int_part[16] = {0}; char dec_part[16] = {0}; /* 提取整数部分 */ strncpy(int_part, str, dot_pos); integer = atoi(int_part); /* 提取小数部分(只取第一位) */ if (dot_pos + 1 < len) { dec_part[0] = str[dot_pos + 1]; decimal = atoi(dec_part); } return (int16_t)(integer * 10 + decimal); } } /* 实现sensor处理函数 */ static void handle_sensor(rt_uint8_t client_idx, cJSON *root) { (void)client_idx; /* 无需回复 */ cJSON *ext = cJSON_GetObjectItem(root, "ext"); if (!ext || !cJSON_IsObject(ext)) { LOG_E("sensor missing ext object"); return; } cJSON *temp_json = cJSON_GetObjectItem(ext, "new_tem"); cJSON *humi_json = cJSON_GetObjectItem(ext, "new_hum"); if (!temp_json || !cJSON_IsString(temp_json)) { LOG_E("sensor missing or invalid new_tem"); return; } if (!humi_json || !cJSON_IsString(humi_json)) { LOG_E("sensor missing or invalid new_hum"); return; } /* 解析温度字符串,直接转换为整数存储(如"2.5" -> 25)*/ sensor.temp_plat = str_to_temp_humi(temp_json->valuestring); /* 解析湿度字符串,直接转换为整数存储(如"30" -> 300)*/ sensor.humi_plat = str_to_temp_humi(humi_json->valuestring); /* 设置有效标志 */ sensor.valid_plat = VALID; LOG_D("sensor[PLATFORM]: temp=%d.%d°C, humi=%d.%d%%", sensor.temp_plat / 10, sensor.temp_plat % 10, sensor.humi_plat / 10, sensor.humi_plat % 10); } /** * @brief 处理读取传感器数据指令 * JSON格式: {"cmd":"read_sensor"} */ static void handle_read_sensor(rt_uint8_t client_idx, cJSON *root) { (void)root; /* 不需要解析ext */ cJSON *resp = cJSON_CreateObject(); if (!resp) { LOG_E("read_sensor: Failed to create response"); return; } cJSON_AddStringToObject(resp, "cmd", "read_sensor"); cJSON *ext = cJSON_CreateObject(); if (!ext) { cJSON_Delete(resp); LOG_E("read_sensor: Failed to create ext object"); return; } cJSON_AddItemToObject(resp, "ext", ext); /* ========== 平台下发数据 ========== */ cJSON *platform = cJSON_CreateObject(); if (platform) { if (sensor.valid_plat == VALID) { char temp_str[16], humi_str[16]; rt_snprintf(temp_str, sizeof(temp_str), "%d.%d", sensor.temp_plat / 10, sensor.temp_plat % 10); rt_snprintf(humi_str, sizeof(humi_str), "%d.%d", sensor.humi_plat / 10, sensor.humi_plat % 10); cJSON_AddStringToObject(platform, "temp", temp_str); cJSON_AddStringToObject(platform, "humi", humi_str); cJSON_AddStringToObject(platform, "status", "valid"); } else { cJSON_AddStringToObject(platform, "temp", "0.0"); cJSON_AddStringToObject(platform, "humi", "0.0"); cJSON_AddStringToObject(platform, "status", "invalid"); } cJSON_AddItemToObject(ext, "platform", platform); } /* ========== SHT30传感器数据 ========== */ cJSON *sht30 = cJSON_CreateObject(); if (sht30) { if (sensor.valid_sht == VALID) { char temp_str[16], humi_str[16]; rt_snprintf(temp_str, sizeof(temp_str), "%d.%d", sensor.temp_sht / 10, sensor.temp_sht % 10); rt_snprintf(humi_str, sizeof(humi_str), "%d.%d", sensor.humi_sht / 10, sensor.humi_sht % 10); cJSON_AddStringToObject(sht30, "temp", temp_str); cJSON_AddStringToObject(sht30, "humi", humi_str); cJSON_AddStringToObject(sht30, "status", "valid"); } else { cJSON_AddStringToObject(sht30, "temp", "0.0"); cJSON_AddStringToObject(sht30, "humi", "0.0"); cJSON_AddStringToObject(sht30, "status", "invalid"); } cJSON_AddItemToObject(ext, "sht30", sht30); } /* ========== NTC温度传感器数据 ========== */ cJSON *ntc = cJSON_CreateObject(); if (ntc) { if (sensor.valid_ntc == VALID) { char temp_str[16]; rt_snprintf(temp_str, sizeof(temp_str), "%d.%d", sensor.temp_ntc / 10, sensor.temp_ntc % 10); cJSON_AddStringToObject(ntc, "temp", temp_str); cJSON_AddStringToObject(ntc, "status", "valid"); } else { cJSON_AddStringToObject(ntc, "temp", "0.0"); cJSON_AddStringToObject(ntc, "status", "invalid"); } cJSON_AddItemToObject(ext, "ntc", ntc); } /* 添加时间戳 */ char stamp[20]; format_time(stamp, sizeof(stamp), &time_now); cJSON_AddStringToObject(ext, "timestamp", stamp); LOG_I("read_sensor: platform=%s, sht30=%s, ntc=%s", sensor.valid_plat == VALID ? "valid" : "invalid", sensor.valid_sht == VALID ? "valid" : "invalid", sensor.valid_ntc == VALID ? "valid" : "invalid"); send_response(client_idx, resp); } /* 静态JSON缓冲区(全局或线程局部) */ static char s_warn_json_buf[256]; static uint8_t s_warn_busy = 0; /* 占用标志,防止重入 */ /** * @brief 通用预警消息上报函数(栈空间优化版 - 使用静态缓冲区) * @param detail 预警详细信息(字符串) * @return int 0-成功,-1-失败,-2-缓冲区忙 */ int ym310_warn_report(const char *detail) { char stamp[15]; int len; int ret = -1; char *real_detail = NULL; char buf[48] = {0}; sprintf(buf, "成功升级至:%s", version_with_time()); if (strstr(detail, "UPDATE_SUCC") != NULL) { real_detail = buf; } else { real_detail = detail; } /* 记录错误码 */ Recode_e_code(e_WARN, 0, (char *)real_detail); /* ========== 1. 参数校验 ========== */ if (!real_detail || (real_detail) == 0) { LOG_E("warn_report: detail is empty"); return -1; } /* ========== 2. 检查缓冲区是否被占用(防止重入) ========== */ if (s_warn_busy) { LOG_W("warn_report: buffer busy, skip: %s", real_detail); return -2; } /* ========== 3. 检查MQTT连接 ========== */ if (!ym310_mqtt_is_connected(0)) { LOG_W("warn_report: MQTT not connected, skip: %s", real_detail); return -1; } /* ========== 4. 检查IMEI ========== */ if (strlen(sys_info.imei) == 0) { LOG_W("warn_report: IMEI not ready, skip: %s", real_detail); return -1; } /* ========== 5. 占用缓冲区 ========== */ s_warn_busy = 1; /* ========== 6. 获取时间戳 ========== */ format_time(stamp, sizeof(stamp), &time_now); /* ========== 7. 手动构建JSON(避免cJSON栈开销) ========== */ len = rt_snprintf(s_warn_json_buf, sizeof(s_warn_json_buf), "{\"cmd\":\"warn\",\"ext\":{\"detail\":\"%s\"}}", real_detail); /* ========== 8. 检查JSON长度 ========== */ if (len > 0 && len < sizeof(s_warn_json_buf)) { // /* 打印日志 */ // LOG_I("WARN: %s", detail); // LOG_D("WARN payload: %s", s_warn_json_buf); /* 发送MQTT消息 */ if (ym310_mqtt_send_primary(s_warn_json_buf, len) == RT_EOK) { ret = 0; } else { LOG_E("warn_report: send failed"); ret = -3; } } else { LOG_E("warn_report: JSON too long or failed, len=%d", len); ret = -4; } /* ========== 9. 释放缓冲区 ========== */ s_warn_busy = 0; return ret; } /* 调试命令查看sensor状态 */ static void show_sensor(int argc, char **argv) { rt_kprintf("\n========== SENSOR STATUS ==========\n"); /* 平台数据 */ rt_kprintf("[PLAT] "); if (sensor.valid_plat == VALID) { rt_kprintf("temp=%d.%d°C, humi=%d.%d%%\n", sensor.temp_plat / 10, sensor.temp_plat % 10, sensor.humi_plat / 10, sensor.humi_plat % 10); } else { rt_kprintf("invalid\n"); } /* SHT30数据 */ rt_kprintf("[SHT ] "); if (sensor.valid_sht == VALID) { rt_kprintf("temp=%d.%d°C, humi=%d.%d%%\n", sensor.temp_sht / 10, sensor.temp_sht % 10, sensor.humi_sht / 10, sensor.humi_sht % 10); } else { rt_kprintf("invalid\n"); } /* NTC数据 */ rt_kprintf("[NTC ] "); if (sensor.valid_ntc == VALID) { rt_kprintf("temp=%d.%d°C\n", sensor.temp_ntc / 10, sensor.temp_ntc % 10); } else { rt_kprintf("invalid\n"); } rt_kprintf("==================================\n"); } MSH_CMD_EXPORT(show_sensor, show sensor data); static void process_json(rt_uint8_t client_idx, const char *payload) { cJSON *root = cJSON_Parse(payload); if (!root) { LOG_E("JSON parse error: %s", payload); return; } cJSON *cmd_json = cJSON_GetObjectItem(root, "cmd"); if (!cmd_json || !cJSON_IsString(cmd_json)) { LOG_E("No cmd field or invalid"); cJSON_Delete(root); return; } const char *cmd = cmd_json->valuestring; LOG_D("Received cmd: %s", cmd); if (strcmp(cmd, "read") == 0) { handle_read(client_idx, root); } else if (strcmp(cmd, "Set_scdparam") == 0) { handle_set_scdparam(client_idx, root); } else if (strcmp(cmd, "thirdserverconf") == 0) { handle_thirdserverconf(client_idx, root); } else if (strcmp(cmd, "paramconf") == 0) { handle_paramconf(client_idx, root); } else if (strcmp(cmd, "test") == 0) { handle_test(client_idx, root); } else if (strcmp(cmd, "reboot") == 0) { handle_reboot(client_idx, root); } else if (strcmp(cmd, "update_stm32") == 0) { handle_update_stm32(client_idx, root); } else if (strcmp(cmd, "platform") == 0) { handle_platform(client_idx, root); } else if (strcmp(cmd, "enable_customer") == 0) { handle_enable_customer(client_idx, root); } else if (strcmp(cmd, "disable_customer") == 0) { handle_disable_customer(client_idx, root); } else if (strcmp(cmd, "read_ecode") == 0) { handle_read_ecode(client_idx, root); } else if (strcmp(cmd, "read_update_url") == 0) { handle_read_update_url(client_idx, root); } else if (strcmp(cmd, "set_update_url") == 0) { handle_set_update_url(client_idx, root); } else if (strcmp(cmd, "sensor") == 0) { handle_sensor(client_idx, root); } else if (strcmp(cmd, "read_sensor") == 0) { handle_read_sensor(client_idx, root); } else if (strcmp(cmd, "read_status") == 0) { handle_read_status(client_idx, root); } else if (strcmp(cmd, "Light_control_handoff") == 0) { handle_chg_light_type(client_idx, root); } else if (strcmp(cmd, "location") == 0) { handle_location(client_idx, root); } else if (strcmp(cmd, "upload_ecode") == 0) { handle_upload_ecode(client_idx, root); } else if (strcmp(cmd, "get_devinfo") == 0) { handle_get_devinfo(client_idx, root); } else if (strcmp(cmd, "FORCE_RESET") == 0) { handle_FORCE_RESET(client_idx, root); } else if (strcmp(cmd, "lamp_proc_on") == 0) { handle_lamp_proc_on(client_idx, root); } else if (strcmp(cmd, "lamp_proc_off") == 0) { handle_lamp_proc_off(client_idx, root); } else if (strcmp(cmd, "SCDV3_UP_FIRMWARE") == 0) { handle_SCDV3_UP_FIRMWARE(client_idx, root); } else { LOG_W("Unknown cmd: %s", cmd); } cJSON_Delete(root); } #include void ym310_protocol_process(rt_uint8_t client_idx, const char *topic, const char *payload, rt_uint16_t len) { (void)topic; if (!payload || len == 0) { LOG_E("Empty payload"); return; } char *buf = rt_malloc(len + 1); if (!buf) { LOG_E("Out of memory"); return; } rt_memcpy(buf, payload, len); buf[len] = '\0'; LOG_D("Processing payload (%d): %s", len, buf); process_json(client_idx, buf); rt_free(buf); } void ym310_data_report(rt_uint8_t client_idx) { char res = 1; if (client_idx == 1 && sys_info.customer_cfg.enable != VALID) { return; } cJSON *root = NULL; if (read_platform_flag()) { root = build_data_report_vice(); } else { root = build_data_report(); } if (!root) { LOG_E("Failed to build data report"); return; } char *json_str = cJSON_PrintUnformatted(root); if (json_str) { LOG_D("Data report to MQTT[%d]: %s", client_idx, json_str); if (client_idx == 0) { if (ym310_mqtt_send_primary(json_str, rt_strlen(json_str)) != RT_EOK) { res = 0; } } else if (client_idx == 1) { if (ym310_mqtt_send_secondary(json_str, rt_strlen(json_str)) != RT_EOK) { res = 0; } } else { LOG_E("Invalid client_idx: %d", client_idx); res = 0; } cJSON_free(json_str); } cJSON_Delete(root); // rt_memory_info(&total, &used, &max_used); // LOG_W("After report: total=%d, used=%d, max=%d", total, used, max_used); if (read_platform_flag()) // 上报status { root = build_status_report_vice(); if (root) { char *json_str = cJSON_PrintUnformatted(root); if (json_str) { LOG_D("Status report to MQTT[%d]: %s", client_idx, json_str); ym310_mqtt_send_primary(json_str, rt_strlen(json_str)); cJSON_free(json_str); } cJSON_Delete(root); } } if (res) { hv_save(0); } } /** * @brief 发送升级结果到 MQTT * @param client_idx MQTT 实例索引(通常使用主 MQTT,即 0) * @param update_res 升级结果标志(VALID 成功,0x9E 写入失败,0x9F 校验失败) */ static void report_update_result(rt_uint8_t client_idx) { if (update_res < 0x9A || update_res > 0x9F) { return; } cJSON *root = cJSON_CreateObject(); if (root == RT_NULL) return; cJSON_AddStringToObject(root, "cmd", "update_result"); const char *status = "unknown"; const char *desc = ""; switch (update_res) { case 0x9A: status = "fail"; desc = "firmware download fail"; break; case VALID: status = "ok"; desc = "FIRMWARE UPDATE SUCCESS"; // Recode_e_code(e_update_WSUCC, 0, "升级写入固件成功"); break; case 0x9E: status = "error"; desc = "firmware release failed"; // Recode_e_code(e_update_WFAIL, 1, "升级写入固件失败"); break; case 0x9F: status = "error"; desc = "verification failed"; // Recode_e_code(e_update_WFAIL, 2, "升级写入固件失败(校验未通过)"); break; default: cJSON_Delete(root); return; break; } cJSON_AddStringToObject(root, "result", status); cJSON_AddStringToObject(root, "desc", desc); cJSON_AddNumberToObject(root, "code", update_res); cJSON_AddStringToObject(root, "version", version_with_time()); send_response(client_idx, root); update_res = 0; ef_set_env_blob("update_res", &update_res, sizeof(update_res)); ym310_warn_report("UPDATE_SUCC"); } static void report_upload_result(rt_uint8_t client_idx) { if (upload_res < 0x9A || upload_res > 0x9F) { return; } cJSON *root = cJSON_CreateObject(); if (root == RT_NULL) return; cJSON_AddStringToObject(root, "cmd", "upload_result"); const char *status = "unknown"; const char *desc = ""; switch (upload_res) { case 0x9A: status = "error"; desc = "LOG UPLOAD FAIL"; break; case VALID: status = "ok"; desc = "LOG UPLOAD SUCCESS"; break; default: cJSON_Delete(root); return; break; } cJSON_AddStringToObject(root, "result", status); cJSON_AddStringToObject(root, "desc", desc); cJSON_AddNumberToObject(root, "upload_res", upload_res); send_response(client_idx, root); upload_res = 0; ef_set_env_blob("upload_res", &upload_res, sizeof(upload_res)); } /*********************↑ function definitions ↑***********************/ /*********************↓ data report ↓***********************/ static char report_once = 0; // 触发一次上报 #define MAX_WAIT_SEC 30 * 60 // half an hour /* 数据上报线程入口 */ static void data_report_thread_entry(void *param) { uint16_t interval_min; // LOG_I("Data report thread started"); uint16_t wait = 0; uint32_t used_sec = 0; /* 等待 MQTT0 连接成功 */ while (1) { if (ym310_mqtt_is_connected(0)) { break; } // rt_kprintf("ym310_mqtt_is_connected = %d \n", ym310_mqtt_is_connected(0)); if (wait++ > 0 && wait % 15 == 0) { LOG_I("Waiting for MQTT0 connection..."); if (wait >= MAX_WAIT_SEC) { Recode_e_code(e_mqtt0_outtime, wait, "MQTT0 超时未连接"); rt_hw_cpu_reset(); } } rt_thread_mdelay(1000); } rt_thread_mdelay(10000); //rt_thread_mdelay(33000); LOG_D("MQTT0 connected, start reporting"); /* 开机立即上报一次 */ ym310_data_report(0); ym310_data_report(1); report_update_result(0); report_upload_result(0); while (1) { /* 读取当前上报间隔 */ interval_min = dev_info.data_interval; if (interval_min == 0) { interval_min = 10; /* 默认10分钟 */ LOG_D("data_interval is 0, use default %d minutes", interval_min); } if (used_sec >= interval_min * 60) { LOG_D("Reporting data (interval=%d min)", interval_min); ym310_data_report(0); ym310_data_report(1); used_sec = 1; } else { used_sec++; } if (used_sec > 0 && used_sec % 60 == 0) { // rt_kprintf("ready for report %d/%d min \n", used_sec / 60, interval_min); // rt_memory_info(&total, &used, &max_used); // LOG_I("NOW MEM: total=%d, used=%d, max=%d", total, used, max_used); } rt_thread_mdelay(1000); if (report_once) { report_once = 0; LOG_I("Manual report triggered for MQTT"); ym310_data_report(0); ym310_data_report(1); rt_thread_mdelay(3000); } } } static int start_datarpt(void) { rt_thread_t s_report_thread = RT_NULL; /* 创建数据上报线程 */ s_report_thread = rt_thread_create("data_rpt", data_report_thread_entry, RT_NULL, 2048, /* 栈大小,根据需要调整 */ 19, /* 优先级,低于MQTT线程 */ 10); if (s_report_thread != RT_NULL) { rt_thread_startup(s_report_thread); } else { LOG_E("Failed to create data report thread"); } return 0; } INIT_APP_EXPORT(start_datarpt); /*********************↑ data report ↑***********************/ void report_now(void) { report_once = 1; } MSH_CMD_EXPORT(report_now, Trigger a data report immediately); static void handle_read_ecode(rt_uint8_t client_idx, cJSON *root) { cJSON *ext = cJSON_GetObjectItem(root, "ext"); if (!ext || !cJSON_IsObject(ext)) { LOG_E("read_ecode missing ext object"); return; } cJSON *start_json = cJSON_GetObjectItem(ext, "start"); cJSON *count_json = cJSON_GetObjectItem(ext, "count"); if (!start_json || !cJSON_IsNumber(start_json) || !count_json || !cJSON_IsNumber(count_json)) { LOG_E("read_ecode missing or invalid start/count"); return; } int start = start_json->valueint; int count = count_json->valueint; int total = ecode_get_count(); /* ========== 指令1: 读取当前存储条数 (start=0, count=0) ========== */ if (start == 0 && count == 0) { cJSON *resp = cJSON_CreateObject(); cJSON_AddStringToObject(resp, "cmd", "read_ecode"); cJSON_AddNumberToObject(resp, "total", total); cJSON_AddStringToObject(resp, "status", "count_only"); cJSON_AddStringToObject(resp, "msg", "current stored records count"); send_response(client_idx, resp); LOG_I("read_ecode count_only: total=%d", total); return; } /* ========== 指令2: 读取所有存储的ecode (start=99, count=99) ========== */ if (start == 99 && count == 99) { start = 0; count = total; LOG_I("read_ecode read_all mode: total=%d, will packet automatically", total); } /* ========== 指令3: 按起始位置和条数读取 (正常逻辑) ========== */ /* 参数校验 */ if (start < 0 || (total > 0 && start >= total)) { cJSON *err = cJSON_CreateObject(); cJSON_AddStringToObject(err, "cmd", "read_ecode"); cJSON_AddStringToObject(err, "error", "start out of range"); cJSON_AddNumberToObject(err, "total", total); send_response(client_idx, err); return; } if (count <= 0) { cJSON *err = cJSON_CreateObject(); cJSON_AddStringToObject(err, "cmd", "read_ecode"); cJSON_AddStringToObject(err, "error", "count must be positive"); cJSON_AddNumberToObject(err, "total", total); send_response(client_idx, err); return; } /* MQTT分包参数配置 */ #define MQTT_MAX_PAYLOAD_SIZE_USE 950 /* MQTT最大负载,留有余量 */ #define REPORT_INTERVAL_MS 2000 /* 分包间隔2秒 */ #define JSON_OVERHEAD_PER_RECORD 50 /* 每条记录JSON格式开销(字段名、括号、逗号等) */ #define JSON_BASE_OVERHEAD 50 /* 基础JSON开销(cmd、start等字段) */ /* 计算实际需要读取的数量 */ int remaining = (start + count > total) ? (total - start) : count; int current_start = start; int packet_seq = 0; LOG_I("read_ecode start=%d, request_count=%d, actual_remaining=%d, total=%d", start, count, remaining, total); /* 动态分包上报 - 根据实际记录大小决定每包数量 */ while (remaining > 0) { packet_seq++; /* 预估本包可以容纳的记录数 */ int records_this_packet = 0; int estimated_size = JSON_BASE_OVERHEAD; /* 逐条检查,直到接近限制 */ for (int i = 0; i < remaining; i++) { ecode_record_t rec; if (ecode_read(current_start + i, &rec) != 0) { /* 读取失败,跳过这条 */ continue; } /* 计算这条记录的实际大小: - detail字符串长度 - 时间字符串固定17字节 (YY/MM/DD HH:MM:SS) - 数字字段约10字节 - JSON格式开销50字节 */ int detail_len = strlen(rec.detail); int record_size = JSON_OVERHEAD_PER_RECORD + detail_len + 17 + 20; /* 检查加上这条是否会超出限制 */ if (estimated_size + record_size > MQTT_MAX_PAYLOAD_SIZE_USE && records_this_packet > 0) { /* 这条放不下了,且已经有记录,停止添加 */ break; } estimated_size += record_size; records_this_packet++; /* 至少放一条,防止死循环 */ if (records_this_packet == 1 && estimated_size > MQTT_MAX_PAYLOAD_SIZE_USE) { LOG_W("single record too large: %d bytes, will send anyway", estimated_size); break; } } /* 确保至少有一条 */ if (records_this_packet == 0) records_this_packet = 1; /* 限制不超过剩余数量 */ if (records_this_packet > remaining) records_this_packet = remaining; cJSON *resp = cJSON_CreateObject(); cJSON_AddStringToObject(resp, "cmd", "read_ecode"); cJSON_AddNumberToObject(resp, "start", current_start); cJSON_AddNumberToObject(resp, "total", total); cJSON_AddNumberToObject(resp, "packet_seq", packet_seq); cJSON_AddBoolToObject(resp, "has_more", remaining > records_this_packet); cJSON *records = cJSON_CreateArray(); int actual_read = 0; for (int i = 0; i < records_this_packet; i++) { ecode_record_t rec; if (ecode_read(current_start + i, &rec) == 0) { cJSON *item = cJSON_CreateObject(); cJSON_AddNumberToObject(item, "num", rec.num); /* 使用缓冲区接收时间字符串 */ char time_str[32]; rt_snprintf(time_str, sizeof(time_str), "%02d/%02d/%02d %02d:%02d:%02d", rec.time_stamp.tm_year, rec.time_stamp.tm_mon, rec.time_stamp.tm_mday, rec.time_stamp.tm_hour, rec.time_stamp.tm_min, rec.time_stamp.tm_sec); cJSON_AddStringToObject(item, "time", time_str); cJSON_AddNumberToObject(item, "code", rec.code); cJSON_AddNumberToObject(item, "para", rec.para); cJSON_AddStringToObject(item, "detail", rec.detail); cJSON_AddItemToArray(records, item); actual_read++; } else { LOG_W("failed to read ecode at index %d", current_start + i); } } cJSON_AddNumberToObject(resp, "count", actual_read); cJSON_AddItemToObject(resp, "records", records); /* 发送本包数据 */ send_response(client_idx, resp); LOG_I("read_ecode sent packet %d: start=%d, count=%d/%d, remaining=%d", packet_seq, current_start, actual_read, records_this_packet, remaining - records_this_packet); /* 更新计数器 */ current_start += records_this_packet; remaining -= records_this_packet; /* 如果还有数据,等待2秒后发送下一包 */ if (remaining > 0) { rt_thread_mdelay(REPORT_INTERVAL_MS); } } LOG_I("read_ecode completed: %d packets sent", packet_seq); } /** * @brief 格式化时长字符串(自动选择分钟或小时) * @param seconds 秒数 * @param buf 输出缓冲区 * @param size 缓冲区大小 */ static void format_duration_str(uint32_t seconds, char *buf, size_t size) { if (seconds >= 3600) { /* 大于等于1小时,显示小时和分钟 */ uint32_t hours = seconds / 3600; uint32_t minutes = (seconds % 3600) / 60; if (minutes > 0) rt_snprintf(buf, size, "%u小时%u分钟", hours, minutes); else rt_snprintf(buf, size, "%u小时", hours); } else if (seconds >= 60) { /* 大于等于1分钟,显示分钟 */ uint32_t minutes = seconds / 60; uint32_t secs = seconds % 60; if (secs > 0) rt_snprintf(buf, size, "%u分钟%u秒", minutes, secs); else rt_snprintf(buf, size, "%u分钟", minutes); } else { /* 小于1分钟,显示秒 */ rt_snprintf(buf, size, "%u秒", seconds); } } /** * @brief 处理读取系统状态指令 */ static void handle_read_status(rt_uint8_t client_idx, cJSON *root) { (void)root; cJSON *resp = cJSON_CreateObject(); if (!resp) return; cJSON_AddStringToObject(resp, "cmd", "read_status"); cJSON *ext = cJSON_CreateObject(); if (!ext) { cJSON_Delete(resp); return; } cJSON_AddItemToObject(resp, "ext", ext); /* 开关机状态 */ cJSON_AddStringToObject(ext, "开关机状态", dev_info.power_state ? "开机" : "关机"); const char *state_str = ""; switch (g_work_state) { case WORK_STATE_IDLE: state_str = "空闲中"; break; case WORK_STATE_WORKING: state_str = "工作中"; break; case WORK_STATE_LIGHT_PRT: state_str = "光控中"; break; case WORK_STATE_COOLDOWN: state_str = "冷却中"; break; case WORK_STATE_WAIT_DAY: state_str = "工作结束等待天亮"; break; case WORK_STATE_OTHER_PRT: state_str = "保护中"; break; default: break; } cJSON_AddStringToObject(ext, "工作状态", state_str); /* 工作模式 */ if (g_work_state == 1) { cJSON_AddStringToObject(ext, "工作模式", g_work_mode == 1 ? "光控模式" : "时控模式"); /* 开始时间 */ char start_time[16]; rt_snprintf(start_time, sizeof(start_time), "%02d:%02d", g_work_start_hour, g_work_start_minute); cJSON_AddStringToObject(ext, "开始时间", start_time); /* 已工作时长(分钟) */ uint32_t elapsed_min = g_work_elapsed_sec / 60; cJSON_AddNumberToObject(ext, "已工作时长(分钟)", elapsed_min); /* 进度百分比 */ cJSON_AddNumberToObject(ext, "进度百分比", g_work_progress); if (g_work_total_sec > 0) { uint32_t remain_min = (g_work_total_sec - g_work_elapsed_sec) / 60; cJSON_AddNumberToObject(ext, "剩余时长(分钟)", remain_min); } else { cJSON_AddStringToObject(ext, "工作模式类型", "自动模式(天亮结束)"); } if (dev_info.time_ctrl_enable == 0) { if (dev_info.light_work_duration > 0) { char duration[32]; rt_snprintf(duration, sizeof(duration), "%d小时(光控)", dev_info.light_work_duration); cJSON_AddStringToObject(ext, "配置工作时长", duration); } else { cJSON_AddStringToObject(ext, "配置工作时长", "自动模式"); } } else { char duration[32]; rt_snprintf(duration, sizeof(duration), "%d小时(时控)", calculate_hours_diff(dev_info.time_start, dev_info.time_end)); cJSON_AddStringToObject(ext, "配置工作时长", duration); } } /* 冷却状态 */ if (g_work_state == 2 && g_cooldown_sec > 0) { cJSON_AddNumberToObject(ext, "冷却剩余秒数", g_cooldown_sec); } /* 控制模式 */ cJSON_AddStringToObject(ext, "控制模式", dev_info.time_ctrl_enable ? "时控模式" : "光控模式"); if (dev_info.time_ctrl_enable == 0) { /* 光控模式详情 */ cJSON_AddStringToObject(ext, "光控来源", dev_info.light_flag == 0 ? "太阳能板" : "光敏二极管"); /* 亮度值(整数,0-1000表示0.0%-100.0%) */ cJSON_AddNumberToObject(ext, "亮度值(%)", sys_sta.light_percent / 10); /* 充电电压(mV) */ cJSON_AddNumberToObject(ext, "充电电压(mV)", sys_sta.charge_voltage); /* 光控保护状态 */ cJSON_AddStringToObject(ext, "光控保护", sys_sta.light_protect == VALID ? "激活(白天)" : "未激活(夜晚)"); } else { /* 时控模式详情 */ char time_range[32]; rt_snprintf(time_range, sizeof(time_range), "%02d:00 - %02d:00", dev_info.time_start, dev_info.time_end); cJSON_AddStringToObject(ext, "时控时段", time_range); cJSON_AddNumberToObject(ext, "当前小时", time_now.hour); /* 内联判断是否在时段内 */ int in_range; if (dev_info.time_start <= dev_info.time_end) { in_range = (time_now.hour >= dev_info.time_start && time_now.hour < dev_info.time_end); } else { in_range = (time_now.hour >= dev_info.time_start || time_now.hour < dev_info.time_end); } cJSON_AddStringToObject(ext, "是否在时段内", in_range ? "是" : "否"); } char off_lamp_proc = 0; ef_get_env_blob("off_lamp_proc", &off_lamp_proc, sizeof(off_lamp_proc), NULL); /* 保护状态 */ cJSON *protect = cJSON_CreateObject(); if (protect) { cJSON_AddStringToObject(protect, "光控保护", sys_sta.light_protect == VALID ? "激活" : "正常"); cJSON_AddStringToObject(protect, "欠压保护", sys_sta.undervolt_protect == VALID ? "激活" : "正常"); cJSON_AddStringToObject(protect, "高温保护", sys_sta.temp_protect == VALID ? "激活" : "正常"); cJSON_AddStringToObject(protect, "低温保护", sys_sta.temp_protect == 0x7A ? "激活" : "正常"); cJSON_AddStringToObject(protect, "倾倒保护", sys_sta.tilt_protect == VALID ? "激活" : "正常"); cJSON_AddStringToObject(protect, "雨控保护", sys_sta.rain_protect == VALID ? "激活" : "正常"); cJSON_AddStringToObject(protect, "灯管保护功能", off_lamp_proc == VALID ? "关闭" : "开启"); switch (sys_sta.lamp_fault) { case 0x7A: cJSON_AddStringToObject(protect, "灯管状态", "开路"); break; case VALID: cJSON_AddStringToObject(protect, "灯管状态", "短路"); break; default: cJSON_AddStringToObject(protect, "灯管状态", "正常"); break; } cJSON_AddItemToObject(ext, "保护状态", protect); } /* 电气参数 */ cJSON_AddNumberToObject(ext, "电池电压(mV)", sys_sta.battery_voltage); cJSON_AddNumberToObject(ext, "SHT温度(℃)", sensor.temp_sht / 10); cJSON_AddNumberToObject(ext, "NTC温度(℃)", sensor.temp_ntc / 10); cJSON_AddNumberToObject(ext, "气象温度(℃)", sensor.temp_plat / 10); cJSON_AddNumberToObject(ext, "主板温度(℃)", sys_sta.board_temp / 10); cJSON_AddNumberToObject(ext, "灯管电流", sys_sta.lamp_current); cJSON_AddNumberToObject(ext, "高压次数", sys_sta.hv_count); /* 时间戳 */ char stamp[20]; rt_snprintf(stamp, sizeof(stamp), "%04d-%02d-%02d %02d:%02d:%02d", time_now.year, time_now.month, time_now.date, time_now.hour, time_now.minute, time_now.second); cJSON_AddStringToObject(ext, "上报时间", stamp); send_response(client_idx, resp); } /** * @brief 处理手动下发定位指令 * JSON格式: {"cmd":"location","ext":{"lat": "34.4861845","lng": "113.3963165","type": "1"}} * * 功能:当GPS定位失效时,手动下发当前位置 * - 将经纬度字符串存储到 longitude_FORCE 和 latitude_FORCE * - 设置 GPS_MODE = 0(手动模式) * - 保存设备信息到 Flash */ static void handle_location(rt_uint8_t client_idx, cJSON *root) { cJSON *ext = cJSON_GetObjectItem(root, "ext"); if (!ext || !cJSON_IsObject(ext)) { LOG_E("location: missing ext object"); cJSON *resp = build_simple_response("location", -1); if (resp) send_response(client_idx, resp); return; } cJSON *type = cJSON_GetObjectItem(ext, "type"); if (!type || !cJSON_IsString(type)) { LOG_E("location: missing or invalid type field"); cJSON *resp = build_simple_response("location", -6); if (resp) send_response(client_idx, resp); return; } if (type->valuestring[0] == '0') { cJSON *lat_json = cJSON_GetObjectItem(ext, "lat"); cJSON *lng_json = cJSON_GetObjectItem(ext, "lng"); if (!lat_json || !cJSON_IsString(lat_json)) { LOG_E("location: missing or invalid lat field"); cJSON *resp = build_simple_response("location", -2); if (resp) send_response(client_idx, resp); return; } if (!lng_json || !cJSON_IsString(lng_json)) { LOG_E("location: missing or invalid lng field"); cJSON *resp = build_simple_response("location", -3); if (resp) send_response(client_idx, resp); return; } const char *lat_str = lat_json->valuestring; const char *lng_str = lng_json->valuestring; /* 验证经纬度字符串长度 */ if (strlen(lat_str) >= sizeof(dev_info.latitude_FORCE)) { LOG_E("location: lat string too long (%d >= %d)", strlen(lat_str), sizeof(dev_info.latitude_FORCE)); cJSON *resp = build_simple_response("location", -4); if (resp) send_response(client_idx, resp); return; } if (strlen(lng_str) >= sizeof(dev_info.longitude_FORCE)) { LOG_E("location: lng string too long (%d >= %d)", strlen(lng_str), sizeof(dev_info.longitude_FORCE)); cJSON *resp = build_simple_response("location", -5); if (resp) send_response(client_idx, resp); return; } /* 清空原有数据 */ memset(dev_info.latitude_FORCE, 0, sizeof(dev_info.latitude_FORCE)); memset(dev_info.longitude_FORCE, 0, sizeof(dev_info.longitude_FORCE)); /* 复制经纬度字符串 */ strncpy(dev_info.latitude_FORCE, lat_str, sizeof(dev_info.latitude_FORCE) - 1); strncpy(dev_info.longitude_FORCE, lng_str, sizeof(dev_info.longitude_FORCE) - 1); /* 设置GPS模式为手动模式 */ dev_info.GPS_MODE = 0; /* 保存设备信息到 Flash */ save_dev_info(); LOG_I("location: force location saved - lat=%s, lng=%s, GPS_MODE=0", dev_info.latitude_FORCE, dev_info.longitude_FORCE); /* 构建成功响应 */ cJSON *resp = cJSON_CreateObject(); if (resp) { cJSON_AddStringToObject(resp, "cmd", "location"); cJSON_AddStringToObject(resp, "result", "ok"); cJSON *resp_ext = cJSON_CreateObject(); if (resp_ext) { cJSON_AddStringToObject(resp_ext, "lat", dev_info.latitude_FORCE); cJSON_AddStringToObject(resp_ext, "lng", dev_info.longitude_FORCE); cJSON_AddNumberToObject(resp_ext, "gps_mode", dev_info.GPS_MODE); cJSON_AddItemToObject(resp, "ext", resp_ext); } send_response(client_idx, resp); } } if (type->valuestring[0] == '1') { /* 设置GPS模式为自动模式 */ dev_info.GPS_MODE = 1; /* 保存设备信息到 Flash */ save_dev_info(); /* 构建成功响应 */ cJSON *resp = cJSON_CreateObject(); if (resp) { cJSON_AddStringToObject(resp, "cmd", "location"); cJSON_AddStringToObject(resp, "result", "ok"); cJSON *resp_ext = cJSON_CreateObject(); if (resp_ext) { cJSON_AddNumberToObject(resp_ext, "gps_mode", dev_info.GPS_MODE); cJSON_AddItemToObject(resp, "ext", resp_ext); } send_response(client_idx, resp); } } } static void handle_upload_ecode(rt_uint8_t client_idx, cJSON *root) { (void)root; uint8_t flag = 0x9D; ef_set_env_blob("upload_ecode", &flag, sizeof(flag)); LOG_I("upload_ecode flag set, will upload on next reboot"); Recode_e_code(e_upload_ecode, 0, "收到上传EC日志指令"); cJSON *resp = build_simple_response("upload_ecode", 0); if (resp) { send_response(client_idx, resp); } rt_thread_mdelay(300); rt_hw_cpu_reset(); } static cJSON *build_devinfo_report(void) { cJSON *root = cJSON_CreateObject(); if (!root) return NULL; cJSON_AddStringToObject(root, "cmd", "devinfo"); cJSON *ext = cJSON_CreateObject(); if (!ext) { cJSON_Delete(root); return NULL; } cJSON_AddItemToObject(root, "ext", ext); cJSON *mqtt_vice = cJSON_CreateObject(); if (!mqtt_vice) { cJSON_Delete(root); return NULL; } cJSON_AddItemToObject(ext, "mqtt_vice", mqtt_vice); // cJSON *range = cJSON_CreateObject(); // if (!range) // { // cJSON_Delete(root); // return NULL; // } // cJSON_AddItemToObject(ext, "range", range); cJSON *work = cJSON_CreateObject(); if (!work) { cJSON_Delete(work); return NULL; } cJSON_AddItemToObject(ext, "work", work); cJSON *cjson_limit = cJSON_CreateObject(); if (!cjson_limit) { cJSON_Delete(cjson_limit); return NULL; } cJSON_AddItemToObject(ext, "limit", cjson_limit); cJSON *info = cJSON_CreateObject(); if (!info) { cJSON_Delete(info); return NULL; } cJSON_AddItemToObject(ext, "info", info); char stamp[15]; format_time(stamp, sizeof(stamp), &time_now); cJSON_AddStringToObject(ext, "stamp", stamp); uint32_t start_time = 0; ef_get_env_blob("start_time", &start_time, sizeof(start_time), NULL); cJSON_AddStringToObject(info, "IMEI", sys_info.imei); cJSON_AddStringToObject(info, "ICCID", sys_info.iccid); cJSON_AddStringToObject(info, "VERSION", version_with_time()); char num_buf[16]; // 信号强度 rt_snprintf(num_buf, sizeof(num_buf), "%u", sys_info.csq); cJSON_AddStringToObject(info, "CSQ", num_buf); // 开机次数 rt_snprintf(num_buf, sizeof(num_buf), "%u", start_time); cJSON_AddStringToObject(ext, "START_TIMES", num_buf); // 设备状态 rt_snprintf(num_buf, sizeof(num_buf), "%u", sys_sta.work_state); cJSON_AddStringToObject(ext, "DEV_STATUS", num_buf); // 开关机状态 rt_snprintf(num_buf, sizeof(num_buf), "%u", dev_info.power_state); cJSON_AddStringToObject(work, "DEV_IS_ON", num_buf); // 光控工作时长 rt_snprintf(num_buf, sizeof(num_buf), "%u", dev_info.light_work_duration); cJSON_AddStringToObject(work, "LIGHT_WORK_HOUR", num_buf); // 工作模式 rt_snprintf(num_buf, sizeof(num_buf), "%u", dev_info.time_ctrl_enable); cJSON_AddStringToObject(work, "WORK_MODE", num_buf); // 时控开始 rt_snprintf(num_buf, sizeof(num_buf), "%u", dev_info.time_start); cJSON_AddStringToObject(work, "TIME_CTL_START", num_buf); // 时控结束 rt_snprintf(num_buf, sizeof(num_buf), "%u", dev_info.time_end); cJSON_AddStringToObject(work, "TIME_CTL_END", num_buf); // 欠压保护 if (sys_sta.undervolt_protect == VALID) { cJSON_AddStringToObject(ext, "UV_PROT", "1"); // 欠压 } else { cJSON_AddStringToObject(ext, "UV_PROT", "0"); // 正常 } // 温控保护 if (sys_sta.temp_protect == 0x7A) // 低温 { cJSON_AddStringToObject(ext, "TEMP_PROT", "1"); } else if (sys_sta.temp_protect == VALID) // 高温 { cJSON_AddStringToObject(ext, "TEMP_PROT", "2"); } else // 正常 { cJSON_AddStringToObject(ext, "TEMP_PROT", "0"); } // 倾倒保护 if (sys_sta.tilt_protect == VALID) { cJSON_AddStringToObject(ext, "Tilt_PROT", "1"); } else { cJSON_AddStringToObject(ext, "Tilt_PROT", "0"); } // 雨控保护 if (sys_sta.rain_protect == VALID) { cJSON_AddStringToObject(ext, "Rain_PROT", "1"); } else { cJSON_AddStringToObject(ext, "Rain_PROT", "0"); } // 光控保护 if (sys_sta.light_protect == VALID) { cJSON_AddStringToObject(ext, "Light_PROT", "1"); } else { cJSON_AddStringToObject(ext, "Light_PROT", "0"); } // 高压计数 // ct rt_snprintf(num_buf, sizeof(num_buf), "%u", sys_sta.hv_count); cJSON_AddStringToObject(work, "HV_CNT", num_buf); // 电压部分 char buf[16]; // 充电电压 int cv_int = sys_sta.charge_voltage / 1000; int cv_dec = sys_sta.charge_voltage % 1000; rt_snprintf(buf, sizeof(buf), "%d.%03d", cv_int, cv_dec); cJSON_AddStringToObject(ext, "V_CHG", buf); // 电池电压 int bv_int = sys_sta.battery_voltage / 1000; int bv_dec = sys_sta.battery_voltage % 1000; rt_snprintf(buf, sizeof(buf), "%d.%03d", bv_int, bv_dec); cJSON_AddStringToObject(ext, "V_BAT", buf); // 主板温度 int bt_int = sys_sta.board_temp / 10; rt_snprintf(buf, sizeof(buf), "%d", bt_int); cJSON_AddStringToObject(ext, "T_BOARD", buf); /* ========== 传感器数据联动逻辑 ========== * 优先级:SHT30 > (NTC温度 + PLAT湿度) > PLAT > 默认值 * - 温度:SHT30 > NTC > PLAT > 22.0 * - 湿度:SHT30 > PLAT > 33.0 */ uint8_t temp_source = 0; /* 温度来源:1=SHT,2=NTC,3=PLAT,4=默认 */ uint8_t humi_source = 0; /* 湿度来源:1=SHT,2=PLAT,3=默认 */ /* 1. 优先使用 SHT30 数据 */ if (sensor.valid_sht == VALID) { temp_source = 1; humi_source = 1; LOG_D("Sensor: using SHT30 data (temp=%d.%d°C, humi=%d.%d%%)", sensor.temp_sht / 10, sensor.temp_sht % 10, sensor.humi_sht / 10, sensor.humi_sht % 10); } /* 2. SHT30 不可用,尝试组合:NTC温度 + PLAT湿度 */ else if (sensor.valid_ntc == VALID && sensor.valid_plat == VALID) { temp_source = 2; humi_source = 2; LOG_D("Sensor: using NTC temp + PLAT humi (temp=%d.%d°C, humi=%d.%d%%)", sensor.temp_ntc / 10, sensor.temp_ntc % 10, sensor.humi_plat / 10, sensor.humi_plat % 10); } /* 3. 使用 PLAT 数据 */ else if (sensor.valid_plat == VALID) { temp_source = 3; humi_source = 2; LOG_D("Sensor: using PLAT data (temp=%d.%d°C, humi=%d.%d%%)", sensor.temp_plat / 10, sensor.temp_plat % 10, sensor.humi_plat / 10, sensor.humi_plat % 10); } /* 4. 使用 NTC 温度,湿度使用默认值 */ else if (sensor.valid_ntc == VALID) { temp_source = 2; humi_source = 3; LOG_D("Sensor: using NTC temp only (temp=%d.%d°C), humidity default 33.0%%", sensor.temp_ntc / 10, sensor.temp_ntc % 10); } /* 5. 所有传感器都无效,使用默认值 */ else { temp_source = 4; humi_source = 3; LOG_W("Sensor: ALL sensors invalid, using default values (temp=22.0°C, humi=33.0%%)"); } switch (temp_source) { case 1: cJSON_AddStringToObject(ext, "temp_source", "SHT30"); rt_snprintf(buf, sizeof(buf), "%d.%d", sensor.temp_sht / 10, sensor.temp_sht % 10); cJSON_AddStringToObject(ext, "TEMP", buf); break; case 2: cJSON_AddStringToObject(ext, "temp_source", "NTC"); rt_snprintf(buf, sizeof(buf), "%d.%d", sensor.temp_ntc / 10, sensor.temp_ntc % 10); cJSON_AddStringToObject(ext, "TEMP", buf); break; case 3: cJSON_AddStringToObject(ext, "temp_source", "PLAT"); rt_snprintf(buf, sizeof(buf), "%d.%d", sensor.temp_plat / 10, sensor.temp_plat % 10); cJSON_AddStringToObject(ext, "TEMP", buf); break; case 4: cJSON_AddStringToObject(ext, "temp_source", "default"); rt_snprintf(buf, sizeof(buf), "%d.%d", 22 / 10, 22 % 10); cJSON_AddStringToObject(ext, "TEMP", buf); break; default: break; } switch (humi_source) { case 1: cJSON_AddStringToObject(ext, "humi_source", "SHT30"); rt_snprintf(buf, sizeof(buf), "%d.%d", sensor.humi_sht / 10, sensor.humi_sht % 10); cJSON_AddStringToObject(ext, "HUMI", buf); break; case 2: cJSON_AddStringToObject(ext, "humi_source", "PLAT"); rt_snprintf(buf, sizeof(buf), "%d.%d", sensor.humi_plat / 10, sensor.humi_plat % 10); cJSON_AddStringToObject(ext, "HUMI", buf); break; case 3: cJSON_AddStringToObject(ext, "humi_source", "default"); rt_snprintf(buf, sizeof(buf), "%d.%d", 33 / 10, 33 % 10); cJSON_AddStringToObject(ext, "HUMI", buf); break; default: break; } // 灯管状态 if (sys_sta.lamp_fault == VALID) // 短路 { cJSON_AddStringToObject(ext, "LAMP_STA", "2"); } else if (sys_sta.lamp_fault == 0x7A) // 开路 { cJSON_AddStringToObject(ext, "LAMP_STA", "3"); } else if (gpio_lamp_get_status()) // 开 { cJSON_AddStringToObject(ext, "LAMP_STA", "1"); } else // 关 { cJSON_AddStringToObject(ext, "LAMP_STA", "0"); } /* 灯管故障 */ // 数据间隔 rt_snprintf(num_buf, sizeof(num_buf), "%u", dev_info.data_interval); cJSON_AddStringToObject(info, "DATA_INR", num_buf); // GPS模式 rt_snprintf(num_buf, sizeof(num_buf), "%u", dev_info.GPS_MODE); cJSON_AddStringToObject(info, "GPS_MODE", num_buf); if (dev_info.GPS_MODE == 0) { cJSON_AddStringToObject(info, "LAT", dev_info.latitude_FORCE); cJSON_AddStringToObject(info, "LON", dev_info.longitude_FORCE); } else if (dev_info.GPS_MODE == 1) { cJSON_AddStringToObject(info, "LAT", dev_info.latitude); cJSON_AddStringToObject(info, "LON", dev_info.longitude); } cJSON_AddNumberToObject(ext, "Light_Percent", sys_sta.light_percent / 10 + (sys_sta.light_percent % 10 / 5)); cJSON_AddNumberToObject(ext, "Lamp_Current", sys_sta.lamp_current); if (dev_info.light_flag == 0) { cJSON_AddStringToObject(work, "Light_Source", "太阳能板"); } else if (dev_info.light_flag == 1) { cJSON_AddStringToObject(work, "Light_Source", "光敏管"); } /** * @brief 下面是新增的第一部分 设备工作状态 * */ const char *state_str = ""; switch (g_work_state) { case WORK_STATE_IDLE: state_str = "空闲中"; break; case WORK_STATE_WORKING: state_str = "工作中"; break; case WORK_STATE_LIGHT_PRT: state_str = "光控中"; break; case WORK_STATE_COOLDOWN: state_str = "冷却中"; break; case WORK_STATE_WAIT_DAY: state_str = "工作结束等待天亮(光控)"; break; case WORK_STATE_OTHER_PRT: state_str = "保护中"; break; default: break; } cJSON_AddStringToObject(work, "WORK_STATUS", state_str); /* 工作模式 */ if (g_work_state == 1) { /* 进度百分比 */ cJSON_AddNumberToObject(work, "WORK_PERCENT", g_work_progress); if (g_work_total_sec > 0) { uint32_t remain_min = (g_work_total_sec - g_work_elapsed_sec) / 60; cJSON_AddNumberToObject(work, "Remaining_Min", remain_min); } } cJSON_AddNumberToObject(ext, "TEMP_SHT", sensor.temp_sht / 10); cJSON_AddNumberToObject(ext, "TEMP_NTC", sensor.temp_ntc / 10); cJSON_AddNumberToObject(ext, "TEMP_PLAT", sensor.temp_plat / 10); /** * @brief 下面是保护阈值 * */ cJSON_AddNumberToObject(cjson_limit, "V-SunRise", limit.vsunrise); cJSON_AddNumberToObject(cjson_limit, "V-SunSet", limit.vsunset); cJSON_AddNumberToObject(cjson_limit, "UV-OFF", limit.uvoff); cJSON_AddNumberToObject(cjson_limit, "UV-ON", limit.uvon); cJSON_AddNumberToObject(cjson_limit, "TEMP-OVER", limit.overtemp / 10); cJSON_AddNumberToObject(cjson_limit, "TEMP-UNOVER", limit.unovert / 10); cJSON_AddNumberToObject(cjson_limit, "TEMP-LOW", limit.lowtemp / 10); cJSON_AddNumberToObject(cjson_limit, "TEMP-UNLOW", limit.unlowt / 10); // /** // * @brief 以下是控制类的信息(上下限) // * // */ // cJSON_AddNumberToObject(range, "DATA_INR_MIN", 5); // cJSON_AddNumberToObject(range, "DATA_INR_MAX", 180); // cJSON_AddNumberToObject(range, "DEV_IS_ON_MIN", 0); // cJSON_AddNumberToObject(range, "DEV_IS_ON_MAX", 1); // cJSON_AddNumberToObject(range, "GPS_MODE_MIN", 0); // cJSON_AddNumberToObject(range, "GPS_MODE_MAX", 1); // cJSON_AddNumberToObject(range, "LIGHT_WORK_HOUR_MIN", 0); // cJSON_AddNumberToObject(range, "LIGHT_WORK_HOUR_MAX", 10); // cJSON_AddNumberToObject(range, "TIME_CTL_END_MIN", 0); // cJSON_AddNumberToObject(range, "TIME_CTL_END_MAX", 23); // cJSON_AddNumberToObject(range, "TIME_CTL_START_MIN", 0); // cJSON_AddNumberToObject(range, "TIME_CTL_START_MAX", 23); // cJSON_AddNumberToObject(range, "WORK_MODE_MIN", 0); // cJSON_AddNumberToObject(range, "WORK_MODE_MAX", 1); // cJSON_AddNumberToObject(range, "V-SunRise_MIN", 1000); // cJSON_AddNumberToObject(range, "V-SunRise_MAX", 5000); // cJSON_AddNumberToObject(range, "V-SunSet_MIN", 1000); // cJSON_AddNumberToObject(range, "V-SunSet_MAX", 5000); // cJSON_AddNumberToObject(range, "UV-OFF_MIN", 1000); // cJSON_AddNumberToObject(range, "UV-OFF_MAX", 5000); // cJSON_AddNumberToObject(range, "UV-ON_MIN", 1000); // cJSON_AddNumberToObject(range, "UV-ON_MAX", 5000); // cJSON_AddNumberToObject(range, "TEMP-OVER_MIN", 300); // cJSON_AddNumberToObject(range, "TEMP-OVER_MAX", 800); // cJSON_AddNumberToObject(range, "TEMP-UNOVER_MIN", 200); // cJSON_AddNumberToObject(range, "TEMP-UNOVER_MAX", 700); // cJSON_AddNumberToObject(range, "TEMP-LOW_MIN", -20); // cJSON_AddNumberToObject(range, "TEMP-LOW_MAX", 10); // cJSON_AddNumberToObject(range, "TEMP-UNLOW_MIN", -15); // cJSON_AddNumberToObject(range, "TEMP-UNLOW_MAX", 15); // LOG_I(" sub_topic : %s", sys_info.customer_cfg.sub_topic[0] ? sys_info.customer_cfg.sub_topic : "(empty)"); // LOG_I(" pub_topic : %s", sys_info.customer_cfg.pub_topic[0] ? sys_info.customer_cfg.pub_topic : "(empty)"); // LOG_I(" host : %s", sys_info.customer_cfg.host[0] ? sys_info.customer_cfg.host : "(empty)"); // LOG_I(" port : %u", sys_info.customer_cfg.port); // LOG_I(" qos : %u", sys_info.customer_cfg.qos); // LOG_I(" enable : 0x%02X %s", sys_info.customer_cfg.enable, cJSON_AddStringToObject(mqtt_vice, "HOST", sys_info.customer_cfg.host); cJSON_AddNumberToObject(mqtt_vice, "PORT", sys_info.customer_cfg.port); cJSON_AddStringToObject(mqtt_vice, "USER", sys_info.customer_cfg.username); cJSON_AddStringToObject(mqtt_vice, "PWD", sys_info.customer_cfg.password); cJSON_AddStringToObject(mqtt_vice, "CID", sys_info.customer_cfg.client_id); cJSON_AddStringToObject(mqtt_vice, "PUB_T", sys_info.customer_cfg.pub_topic); cJSON_AddStringToObject(mqtt_vice, "SUB_T", sys_info.customer_cfg.sub_topic); if (sys_info.customer_cfg.enable == VALID) { cJSON_AddStringToObject(mqtt_vice, "ENABLE", "ON"); } else { cJSON_AddStringToObject(mqtt_vice, "ENABLE", "OFF"); } return root; } static void handle_get_devinfo(rt_uint8_t client_idx, cJSON *root) { (void)root; LOG_I("handle_get_devinfo EXE"); // Recode_e_code(e_mqtt_cmd, 10, "执行上传设备信息命令"); cJSON *resp = build_devinfo_report(); if (resp) { send_response(client_idx, resp); } } static void handle_FORCE_RESET(rt_uint8_t client_idx, cJSON *root) { (void)root; LOG_I("handle_FORCE_RESET EXE"); RESET_ALL_PARA(); cJSON *resp = build_simple_response("FORCE_RESET", 0); if (resp) { send_response(client_idx, resp); } rt_thread_mdelay(300); rt_hw_cpu_reset(); } static void handle_lamp_proc_on(rt_uint8_t client_idx, cJSON *root) { (void)root; char off_lamp_proc = 0; ef_set_env_blob("off_lamp_proc", &off_lamp_proc, sizeof(off_lamp_proc)); cJSON *resp = build_simple_response("lamp_proc_on", 0); if (resp) { send_response(client_idx, resp); } } static void handle_lamp_proc_off(rt_uint8_t client_idx, cJSON *root) { (void)root; char off_lamp_proc = VALID; ef_set_env_blob("off_lamp_proc", &off_lamp_proc, sizeof(off_lamp_proc)); cJSON *resp = build_simple_response("lamp_proc_off", 0); if (resp) { send_response(client_idx, resp); } }