diff --git a/.gitignore b/.gitignore index 99e06cb..5e00332 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,6 @@ application/samples/radar/ application/samples/peripheral/ application/samples/bt CMakeLists.txt + +# YoYo AI version control directory +.yoyo/ diff --git a/application/samples/wifi/ohos_connect/hilink_adapt/entry/hilink_ble_main.c b/application/samples/wifi/ohos_connect/hilink_adapt/entry/hilink_ble_main.c index 5c183c9..cb99e5b 100755 --- a/application/samples/wifi/ohos_connect/hilink_adapt/entry/hilink_ble_main.c +++ b/application/samples/wifi/ohos_connect/hilink_adapt/entry/hilink_ble_main.c @@ -42,6 +42,7 @@ //static bool g_switch = 0; int sid_switch; static bool g_autoUpdate = 0; +static int ble_adv_time = 0; static HILINK_BT_DevInfo g_btDevInfo = {0}; extern int set_get_ble_mac(void); extern int HILINK_Printf(const char *format, ...); @@ -255,6 +256,7 @@ static void ReporFwvCheckSum(void) } #endif +static int ble_sdk_running = 0; static void HILINK_BT_StateChangeHandler(HILINK_BT_SdkStatus event, const void *param) { (void)param; @@ -269,10 +271,12 @@ static void HILINK_BT_StateChangeHandler(HILINK_BT_SdkStatus event, const void * HILINK_SAL_ERROR("set addr err\n"); } /* 设置蓝牙广播格式,包括靠近发现、碰一碰等,下一次发送广播生效 */ - BLE_SetAdvType(BLE_ADV_NEARBY_V0); + BLE_SetAdvType(BLE_ADV_LOCAL_NAME); /* BLE配网广播控制:参数代表广播时间,0:停止;0xFFFFFFFF:一直广播,其他:广播指定时间后停止,单位秒 */ - (void)BLE_CfgNetAdvCtrl(BLE_ADV_TIME); + (void)BLE_CfgNetAdvCtrl(ble_adv_time); + ble_sdk_running = 1; + e_printf("callback set ble adv time: %ds\n", ble_adv_time); } } @@ -308,6 +312,18 @@ static int BleHandleCustomDataGet(const char *sid) // 参考链接 https://device.harmonyos.com/cn/docs/devicepartner/DevicePartner-Guides/device-development-ble-specifications-control-0000001482432154 static int BleHandleCustomData(const char *buff, unsigned int len) { + // 添加四开关面板的蓝牙处理 + extern int switch_panel_ble_handle_custom_data(const char *buff, unsigned int len); + + // 优先使用四开关面板的蓝牙处理逻辑 + int panel_result = switch_panel_ble_handle_custom_data(buff, len); + if (panel_result == 0) { + // 四开关面板成功处理了该指令 + return 0; + } + + // 如果面板处理失败,回退到原有的处理逻辑 + HILINK_SAL_NOTICE("开关面板处理失败,使用默认处理逻辑\r\n"); if (strcmp((char *)buff, "{}") == 0) { /* 上报全量数据 */ ReporSwitchStatus(); @@ -424,10 +440,20 @@ unsigned int GetWifiRecoveryType(void) { return (0x01 | 0x02); } +int start_hilink_ble_net_config(int32_t net_cfg_time_s) +{ + ble_adv_time = net_cfg_time_s; + e_printf("set ble adv time: %ds\n", ble_adv_time); + if (ble_sdk_running) { + BLE_CfgNetAdvCtrl(ble_adv_time); + } + return 0; +} int hilink_ble_main(void) { int ret = 0; + hfset_hilink_mode(SMTLK_BLE_FAST_CONNECT); int hilink_entry_mode=hfget_hilink_mode(); printf("hilink_entry_mode:%d\r\n",hilink_entry_mode); if(hilink_entry_mode != SMTLK_SOFTAP) @@ -448,6 +474,7 @@ int hilink_ble_main(void) HILINK_EnableSle(); HILINK_EnablePrescan(); HILINK_SetProtType(17); + e_printf("SMTLK_BLE_FAST_CONNECT\r\n"); } ret = HILINK_SetNetConfigMode(HILINK_NETCONFIG_OTHER); @@ -457,25 +484,27 @@ int hilink_ble_main(void) ret = BLE_SetAdvNameMpp(mpp, sizeof(mpp)); if (ret != 0) { HILINK_SAL_NOTICE("set adv name mpp failed\r\n"); - return -1; - } + return -1; + } - /* 注册SDK状态接收函数,可在初始化完成后发送广播 */ - ret = HILINK_BT_SetSdkEventCallback(HILINK_BT_StateChangeHandler); - if (ret != 0) { + /* 注册SDK状态接收函数,可在初始化完成后发送广播 */ + ret = HILINK_BT_SetSdkEventCallback(HILINK_BT_StateChangeHandler); + if (ret != 0) { HILINK_SAL_NOTICE("set event callback failed\r\n"); - return -1; - } + return -1; + } - /* 设置广播方式为靠近发现 */ - BLE_SetAdvType(BLE_ADV_NEARBY_V0); + /* 设置广播方式为靠近发现 */ + BLE_SetAdvType(BLE_ADV_LOCAL_NAME); - /* 初始化ble sdk */ - ret = BLE_CfgNetInit(&g_bleInitParam, &g_bleCfgNetCb); - if (ret != 0) { + /* 初始化ble sdk */ + ret = BLE_CfgNetInit(&g_bleInitParam, &g_bleCfgNetCb); + if (ret != 0) { HILINK_SAL_NOTICE("ble sdk init fail\r\n"); return -1; } + e_printf("ble sdk init success\r\n"); + //set_get_ble_mac(); } else if(hilink_entry_mode == SMTLK_SOFTAP) { @@ -520,6 +549,16 @@ int hilink_ble_main(void) HILINK_SAL_NOTICE("HILINK_Main start error"); return -1; } + e_printf("HILINK_Main start success\r\n"); hf_set_hilink_main_runing(); + // HILINK_RestoreFactorySettings(); return 0; -} \ No newline at end of file +} + +#ifndef CONFIG_SUPPORT_HILINK_INDIE_UPGRADE +// EKKO remove indie upgrade +int hilink_indie_upgrade_main(void) +{ + return 0; +} +#endif \ No newline at end of file diff --git a/application/samples/wifi/ohos_connect/hilink_adapt/product/device_profile.h b/application/samples/wifi/ohos_connect/hilink_adapt/product/device_profile.h index 67def20..a413ae2 100755 --- a/application/samples/wifi/ohos_connect/hilink_adapt/product/device_profile.h +++ b/application/samples/wifi/ohos_connect/hilink_adapt/product/device_profile.h @@ -17,15 +17,15 @@ extern "C" { */ -#define ProductId "2Q4S" -#define deviceTypeId "21S" +#define ProductId "2Q4G" +#define deviceTypeId "201" #define manufacturerID "gub" -#define deviceModel "SR-SW-020-10S" +#define deviceModel "S15" #define configName "SR_S" #define configType "witch" #define enterpriseEnglishName "SORONTEK" #define brandEn "SORONTEK" -#define deviceName "SORONTEK智能开关面板" +#define deviceName "SORONTEK智能面板" #define productSeries "" #define DEVICE_HIVERSION "1.0.0" diff --git a/application/samples/wifi/ohos_connect/hilink_adapt/product/hilink_device.c b/application/samples/wifi/ohos_connect/hilink_adapt/product/hilink_device.c index cd0042e..adff4ec 100755 --- a/application/samples/wifi/ohos_connect/hilink_adapt/product/hilink_device.c +++ b/application/samples/wifi/ohos_connect/hilink_adapt/product/hilink_device.c @@ -18,7 +18,7 @@ #include "hilink_device.h" #ifdef CONFIG_SUPPORT_HILINK_INDIE_UPGRADE #include "hilink_entry.h" - +#endif extern void handle_device_online(void); extern void handle_device_unbind(void); extern void handle_device_offline(void); @@ -40,7 +40,7 @@ static const HILINK_SvcInfo SVC_INFO[] = { { "switch", "switch" }, { "switch", "switch4" }, #ifdef CONFIG_SUPPORT_HILINK_INDIE_UPGRADE - //{ "checkSum", "checkSum" }, + { "checkSum", "checkSum" }, #endif }; @@ -177,14 +177,124 @@ int not_support_put(const char* svc_id, const char* payload, unsigned int len) HILINK_Printf("sid:%s NOT SUPPORT PUT function \r\n", svc_id); return 0; } +#if 0 // 服务处理函数 + +int handle_put_switch3(const char* svc_id, const char* payload, unsigned int len) +{ + cJSON* pJson = cJSON_Parse(payload); + if (!pJson) + return -1; + cJSON* on_item = cJSON_GetObjectItem(pJson, "on"); + if (on_item) + g_device_info.switch3_on = on_item->valueint; + cJSON* name_item = cJSON_GetObjectItem(pJson, "name"); + if (name_item) + g_device_info.switch3_name = name_item->valueint; + HILINK_Printf("%s:%d svcId:%s, payload:%s\r\n", __FUNCTION__, __LINE__, svc_id, payload); + cJSON_Delete(pJson); + return 0; +} + +int handle_get_switch3(const char* svc_id, const char* in, unsigned int in_len, char** out, unsigned int* out_len) +{ + *out_len = 64; + *out = (char*)malloc(*out_len); + if (*out == NULL) + return -1; + *out_len = sprintf_s(*out, *out_len, "{\"on\":%d, \"name\":%d}", g_device_info.switch3_on, g_device_info.switch3_name); + HILINK_Printf("%s:%d svcId:%s, *out:%s\r\n", __FUNCTION__, __LINE__, svc_id, *out); + return 0; +} + +int handle_put_switch2(const char* svc_id, const char* payload, unsigned int len) +{ + cJSON* pJson = cJSON_Parse(payload); + if (!pJson) + return -1; + cJSON* on_item = cJSON_GetObjectItem(pJson, "on"); + if (on_item) + g_device_info.switch2_on = on_item->valueint; + cJSON* name_item = cJSON_GetObjectItem(pJson, "name"); + if (name_item) + g_device_info.switch2_name = name_item->valueint; + HILINK_Printf("%s:%d svcId:%s, payload:%s\r\n", __FUNCTION__, __LINE__, svc_id, payload); + cJSON_Delete(pJson); + return 0; +} + +int handle_get_switch2(const char* svc_id, const char* in, unsigned int in_len, char** out, unsigned int* out_len) +{ + *out_len = 64; + *out = (char*)malloc(*out_len); + if (*out == NULL) + return -1; + *out_len = sprintf_s(*out, *out_len, "{\"on\":%d, \"name\":%d}", g_device_info.switch2_on, g_device_info.switch2_name); + HILINK_Printf("%s:%d svcId:%s, *out:%s\r\n", __FUNCTION__, __LINE__, svc_id, *out); + return 0; +} + +int handle_put_switch1(const char* svc_id, const char* payload, unsigned int len) +{ + cJSON* pJson = cJSON_Parse(payload); + if (!pJson) + return -1; + cJSON* on_item = cJSON_GetObjectItem(pJson, "on"); + if (on_item) + g_device_info.switch1_on = on_item->valueint; + cJSON* name_item = cJSON_GetObjectItem(pJson, "name"); + if (name_item) + g_device_info.switch1_name = name_item->valueint; + HILINK_Printf("%s:%d svcId:%s, payload:%s\r\n", __FUNCTION__, __LINE__, svc_id, payload); + cJSON_Delete(pJson); + return 0; +} + +int handle_get_switch1(const char* svc_id, const char* in, unsigned int in_len, char** out, unsigned int* out_len) +{ + *out_len = 64; + *out = (char*)malloc(*out_len); + if (*out == NULL) + return -1; + *out_len = sprintf_s(*out, *out_len, "{\"on\":%d, \"name\":%d}", g_device_info.switch1_on, g_device_info.switch1_name); + HILINK_Printf("%s:%d svcId:%s, *out:%s\r\n", __FUNCTION__, __LINE__, svc_id, *out); + return 0; +} + +int handle_put_switch4(const char* svc_id, const char* payload, unsigned int len) +{ + cJSON* pJson = cJSON_Parse(payload); + if (!pJson) + return -1; + cJSON* on_item = cJSON_GetObjectItem(pJson, "on"); + if (on_item) + g_device_info.switch4_on = on_item->valueint; + cJSON* name_item = cJSON_GetObjectItem(pJson, "name"); + if (name_item) + g_device_info.switch4_name = name_item->valueint; + HILINK_Printf("%s:%d svcId:%s, payload:%s\r\n", __FUNCTION__, __LINE__, svc_id, payload); + cJSON_Delete(pJson); + return 0; +} + +int handle_get_switch4(const char* svc_id, const char* in, unsigned int in_len, char** out, unsigned int* out_len) +{ + *out_len = 64; + *out = (char*)malloc(*out_len); + if (*out == NULL) + return -1; + *out_len = sprintf_s(*out, *out_len, "{\"on\":%d, \"name\":%d}", g_device_info.switch4_on, g_device_info.switch4_name); + HILINK_Printf("%s:%d svcId:%s, *out:%s\r\n", __FUNCTION__, __LINE__, svc_id, *out); + return 0; +} +#endif + HANDLE_SVC_INFO g_device_profile[] = { + {"switch3", handle_put_switch3, handle_get_switch3}, + {"switch2", handle_put_switch2, handle_get_switch2}, + {"switch1", handle_put_switch1, handle_get_switch1}, {"switch", handle_put_switch, handle_get_switch}, - {"switch1", handle_put_switch1, handle_get_switch1}, - {"switch2", handle_put_switch2, handle_get_switch2}, - {"switch3", handle_put_switch3, handle_get_switch3}, - {"switch4", handle_put_switch4, handle_get_switch4}, - // 故障不支持 HILINK_PutCharState,配置 not_support_put + {"switch4", handle_put_switch4, handle_get_switch4}, }; // 服务总数量 int g_device_profile_count = sizeof(g_device_profile) / sizeof(HANDLE_SVC_INFO); @@ -233,14 +343,29 @@ int handle_get_cmd(const char* svc_id, const char* in, unsigned int in_len, char } // 快速上报函数,用于上报服务状态信息 +// 支持蓝牙和云端双模式上报 int fast_report(const char* svc_id) { + // 引入外部的蓝牙控制函数 + extern bool switch_panel_ble_is_enabled(void); + extern int switch_panel_ble_fast_report(const char *svc_id); + + // 检查当前是否处于蓝牙控制模式 + if (switch_panel_ble_is_enabled()) { + // 蓝牙模式下通过蓝牙上报 + e_printf("[FAST_REPORT] 蓝牙模式上报: %s\r\n", svc_id); + return switch_panel_ble_fast_report(svc_id); + } + + // 云端模式下通过HiLink上报 + e_printf("[FAST_REPORT] 云端模式上报: %s\r\n", svc_id); + char* payload = NULL; int len; int err = handle_get_cmd(svc_id, NULL, 0, &payload, (unsigned int *)&len); if(err != 0) { - HILINK_Printf("get msg from %s failed \r\n", svc_id); - return err; + HILINK_Printf("get msg from %s failed \r\n", svc_id); + return err; } err = HILINK_ReportCharState(svc_id, payload, len); HILINK_Printf("report %s result is %d, payload:%s \r\n", svc_id, err, payload); @@ -255,9 +380,10 @@ int fast_report(const char* svc_id) */ int HILINK_PutCharState(const char *svcId, const char *payload, unsigned int len) { - e_printf("[HILINK_PutCharState] 收到控制指令: svcId=%s, payload=%s\r\n", svcId, payload); + e_printf("收到控制指令: svcId=%s, payload=%s\r\n", svcId, payload); if ((svcId == NULL) || (payload == NULL)) { + e_printf("参数无效\r\n"); return -1; } @@ -268,7 +394,7 @@ int HILINK_PutCharState(const char *svcId, const char *payload, unsigned int len cJSON_Delete(json); int err = handle_put_cmd(svcId, payload, len); - e_printf("[HILINK_PutCharState] 控制指令处理完成,返回值: %d\r\n", err); + e_printf("控制指令处理完成,返回值: %d\r\n", err); return err; } #ifdef CONFIG_SUPPORT_HILINK_INDIE_UPGRADE diff --git a/application/ws63/hsf/hfuart.h b/application/ws63/hsf/hfuart.h index 9a726ee..2cba501 100755 --- a/application/ws63/hsf/hfuart.h +++ b/application/ws63/hsf/hfuart.h @@ -69,7 +69,12 @@ void HSF_API HF_Debug(int debug_level, const char *format , ... ); #define hfdbg_warn(...) HF_Debug(DEBUG_WARN,"[warnning %d %s %d]",hfsys_get_time(),__FUNCTION__,__LINE__); \ HF_Debug(DEBUG_WARN,__VA_ARGS__) #define u_printf(...) HF_Debug(DEBUG_LEVEL_USER,__VA_ARGS__) - +#define e_printf(...) \ + do { \ + HF_Debug(DEBUG_WARN, "[Ekko]%d %s():%d: ", hfsys_get_time(), \ + __FUNCTION__, __LINE__); \ + HF_Debug(DEBUG_WARN, __VA_ARGS__); \ + } while (0) int hfuart_config(hfuart_handle_t huart, int baudrate, ENCOMPARITY_E parity, ENCOMBITS_E databits, ENCOMSTOPBITS_E stopbits, ENCOMUARTCTL_E fc); diff --git a/application/ws63/user_main/CMakeLists.txt b/application/ws63/user_main/CMakeLists.txt index 84c01d8..35a4d72 100755 --- a/application/ws63/user_main/CMakeLists.txt +++ b/application/ws63/user_main/CMakeLists.txt @@ -10,6 +10,7 @@ set(SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/switch_panel/switch_panel_keys.c ${CMAKE_CURRENT_SOURCE_DIR}/switch_panel/switch_panel_hilink.c ${CMAKE_CURRENT_SOURCE_DIR}/switch_panel/switch_panel_config.c + ${CMAKE_CURRENT_SOURCE_DIR}/switch_panel/switch_panel_ble.c ) if (DEFINES MATCHES "HF_MCU_OTA") diff --git a/application/ws63/user_main/switch_panel/switch_panel.h b/application/ws63/user_main/switch_panel/switch_panel.h index 989a9a2..b312132 100644 --- a/application/ws63/user_main/switch_panel/switch_panel.h +++ b/application/ws63/user_main/switch_panel/switch_panel.h @@ -78,24 +78,48 @@ typedef enum { } system_mode_t; //====================== 设备状态结构 ====================== -// 单个开关状态信息 +// 开关名字最大长度 +#define SWITCH_NAME_MAX_LEN 32 + +// 单个开关的持久化状态信息(需要保存到Flash) +typedef struct { + bool switch_on; // 开关状态 (true=开, false=关) - 持久化 + bool led_state; // LED状态 (true=白灯, false=黄灯) - 持久化 + char name[SWITCH_NAME_MAX_LEN]; // 开关名字 - 持久化 +} switch_persistent_info_t; + +// 单个开关的运行时状态信息(不需要持久化) typedef struct { - bool switch_on; // 开关状态 (true=开, false=关) - bool led_state; // LED状态 (true=白灯, false=黄灯) bool physical_key; // 物理按键状态 (true=松开, false=按下) -} switch_info_t; + uint32_t press_time; // 按键按下时间戳 + bool debounce_flag; // 防抖标志 + bool long_press_handled; // 长按处理标志 +} switch_runtime_info_t; - -// 系统设备状态 +// 持久化设备状态(需要保存到Flash) typedef struct { - switch_info_t switches[SWITCH_COUNT]; // 4个开关的状态 - bool master_switch; // 总开关状态 - bool panel_led; // 面板背光状态 - bool is_bound; // 设备绑定状态 - bool is_first_boot; // 是否第一次上电 - system_mode_t mode; // 系统工作模式 - uint32_t reserved[10]; // 保留字段 -} device_state_t; + switch_persistent_info_t switches[SWITCH_COUNT]; // 4个开关的持久化状态 + bool master_switch; // 总开关状态 - 持久化 + bool panel_led; // 面板背光状态 - 持久化 + bool is_bound; // 设备绑定状态 - 持久化 + bool is_first_boot; // 是否第一次上电 - 持久化 + uint32_t magic; // 魔数标识 + uint32_t version; // 版本号 + uint32_t reserved[8]; // 保留字段 +} device_persistent_state_t; + +// 运行时设备状态(不需要持久化,断电丢失) +typedef struct { + switch_runtime_info_t switches[SWITCH_COUNT]; // 4个开关的运行时状态 + system_mode_t mode; // 系统工作模式(运行时状态) + bool ble_mode_enabled; // 蓝牙模式是否启用 + uint32_t config_start_time; // 配网开始时间 + int config_key_id; // 触发配网的按键ID + bool config_led_blink_state; // 配网LED闪烁状态 + bool factory_test_running; // 产测是否运行中 + uint32_t last_save_time; // 上次保存时间 + uint32_t reserved[16]; // 保留字段 +} device_runtime_state_t; //====================== Flash存储相关 ====================== @@ -121,21 +145,34 @@ typedef struct { //====================== 配网相关定义 ====================== #define FACTORY_TEST_SSID "ShuorongSelfTest" // 产测热点名称 -#define CONFIG_ENTRY_COUNT 3 // 连续重启次数进入配网 //====================== 任务和定时器相关 ====================== -#define TASK_STACK_SIZE 2048 +#define TASK_STACK_SIZE 0x1000 #define TASK_PRIORITY_HIGH OSAL_TASK_PRIORITY_HIGH #define TASK_PRIORITY_NORM OSAL_TASK_PRIORITY_MIDDLE #define TASK_PRIORITY_LOW OSAL_TASK_PRIORITY_LOW +//====================== 异步上报系统相关 ====================== +// 定义服务ID的位掩码 +#define REPORT_SWITCH_MASK 0x01 // switch (总开关) +#define REPORT_SWITCH1_MASK 0x02 // switch1 +#define REPORT_SWITCH2_MASK 0x04 // switch2 +#define REPORT_SWITCH3_MASK 0x08 // switch3 +#define REPORT_SWITCH4_MASK 0x10 // switch4 +#define REPORT_ALL_MASK 0x1F // 所有开关 + +// 异步上报系统相关变量 +extern osal_task *g_report_task_handle; // 异步上报任务句柄 + //====================== 全局变量声明 ====================== -extern device_state_t g_device_state; +extern device_persistent_state_t g_persistent_state; +extern device_runtime_state_t g_runtime_state; extern timer_handle_t g_key_debounce_timer[SWITCH_COUNT]; -extern timer_handle_t g_config_timeout_timer; -extern timer_handle_t g_config_blink_timer; +// 配网定时器变量已移除,配网逻辑简化 extern osal_task *g_key_scan_task_handle; extern osal_task *g_config_task_handle; +extern osal_task *g_save_task_handle; // 异步保存任务句柄 +extern osal_task *g_report_task_handle; // 异步上报任务句柄 //====================== 函数声明 ====================== @@ -151,14 +188,41 @@ void set_panel_led(panel_led_state_t state); bool get_key_input(int key_id); // 设备状态管理函数 -int load_device_state(void); -int save_device_state(void); -void reset_device_state(void); +int load_persistent_state(void); +int save_persistent_state(void); // 异步保存(触发保存任务) +int save_persistent_state_sync(void); // 同步保存(直接写入Flash) +int save_system_init(void); // 初始化异步保存系统 +void save_system_deinit(void); // 清理异步保存系统 +void reset_persistent_state(void); +void init_runtime_state(void); void sync_hardware_state(void); void fast_report_switch(int switch_id); void fast_report_master_switch(void); void set_device_mode(system_mode_t mode); +// 状态访问便利函数 +bool get_switch_state(int switch_id); +bool get_master_switch_state(void); +bool is_device_bound(void); +bool is_first_boot(void); +system_mode_t get_device_mode(void); + +// 异步上报系统管理函数 +int report_system_init(void); // 初始化异步上报系统 +void report_system_deinit(void); // 清理异步上报系统 +void trigger_async_report(uint8_t report_mask); // 触发异步上报 +int async_report_task(void *arg); // 异步上报任务 + +// 快速上报函数(新的异步版本) +void fast_report_switch_async(int switch_id); // 异步上报单个开关 +void fast_report_master_switch_async(void); // 异步上报总开关 +void fast_report_all_switches_async(void); // 异步上报所有开关 + +// 开关名字操作函数 +const char* get_switch_name(int switch_id); +int set_switch_name(int switch_id, const char* name); +void init_default_switch_names(void); + // 开关控制函数 void update_switch_state(int switch_id, bool state); void update_master_switch(bool state); @@ -205,7 +269,19 @@ const char* get_mode_string(system_mode_t mode); // 定时器回调函数 void key_debounce_timer_callback(uintptr_t data); -void config_timeout_timer_callback(uintptr_t data); -void config_blink_timer_callback(uintptr_t data); +// 配网定时器回调函数已移除,配网逻辑现在在config_mode_task中直接处理 +//====================== 蓝牙控制相关函数 ====================== +// 蓝牙数据处理 +int switch_panel_ble_handle_custom_data(const char *buff, unsigned int len); + +// 蓝牙模式管理 +void switch_panel_ble_enable(void); +void switch_panel_ble_disable(void); +bool switch_panel_ble_is_enabled(void); + +// 蓝牙上报 +int switch_panel_ble_fast_report(const char *svc_id); + +int start_hilink_ble_net_config(int32_t net_cfg_time_s); #endif // __SWITCH_PANEL_H__ \ No newline at end of file diff --git a/application/ws63/user_main/switch_panel/switch_panel_ble.c b/application/ws63/user_main/switch_panel/switch_panel_ble.c new file mode 100755 index 0000000..fcb3a99 --- /dev/null +++ b/application/ws63/user_main/switch_panel/switch_panel_ble.c @@ -0,0 +1,284 @@ +#include "switch_panel.h" +#include "hilink_device.h" +#include "ble_cfg_net_api.h" +#include "cJSON.h" +#include "securec.h" + +//====================== 蓝牙控制相关常量 ====================== +#define SWITCH_BLE_REPORT "{\"data\":{\"on\":%d},\"sid\":\"%s\"}" +#define SWITCH_BLE_NAME_REPORT "{\"data\":{\"on\":%d,\"name\":\"%s\"},\"sid\":\"%s\"}" + +//====================== 蓝牙控制状态变量 ====================== +static bool g_ble_mode_enabled = false; // 蓝牙控制模式是否启用 + +//====================== 蓝牙上报函数 ====================== + +// 通过蓝牙上报总开关状态 +static void ble_report_master_switch(void) { + char buff[128] = {0}; + int ret = snprintf_s(buff, sizeof(buff), sizeof(buff) - 1, + SWITCH_BLE_REPORT, + g_persistent_state.master_switch ? 1 : 0, + "switch"); + if (ret <= 0) { + e_printf("[BLE] 总开关状态格式化失败: %d\r\n", ret); + return; + } + + unsigned int buffLen = strlen(buff); + ret = BLE_SendCustomData(CUSTOM_SEC_DATA, (const unsigned char *)buff, buffLen); + e_printf("[BLE] 上报总开关状态: %s, 结果: %d\r\n", buff, ret); +} + +// 通过蓝牙上报单个开关状态 +static void ble_report_switch_state(int switch_id) { + if (switch_id < 0 || switch_id >= SWITCH_COUNT) { + e_printf("[BLE] 无效的开关ID: %d\r\n", switch_id); + return; + } + + char buff[128] = {0}; + char svc_id[16] = {0}; + char switch_name[16] = {0}; + + snprintf_s(svc_id, sizeof(svc_id), sizeof(svc_id) - 1, "switch%d", switch_id + 1); + snprintf_s(switch_name, sizeof(switch_name), sizeof(switch_name) - 1, "开关%d", switch_id + 1); + + int ret = snprintf_s(buff, sizeof(buff), sizeof(buff) - 1, + SWITCH_BLE_NAME_REPORT, + g_persistent_state.switches[switch_id].switch_on ? 1 : 0, + switch_name, + svc_id); + if (ret <= 0) { + e_printf("[BLE] 开关%d状态格式化失败: %d\r\n", switch_id + 1, ret); + return; + } + + unsigned int buffLen = strlen(buff); + ret = BLE_SendCustomData(CUSTOM_SEC_DATA, (const unsigned char *)buff, buffLen); + e_printf("[BLE] 上报开关%d状态: %s, 结果: %d\r\n", switch_id + 1, buff, ret); +} + +// 通过蓝牙上报所有开关状态 +static void ble_report_all_switches(void) { + e_printf("[BLE] 开始上报所有开关状态\r\n"); + + // 上报总开关状态 + ble_report_master_switch(); + + // 上报所有子开关状态 + for (int i = 0; i < SWITCH_COUNT; i++) { + ble_report_switch_state(i); + osal_msleep(50); // 避免发送过快 + } + + e_printf("[BLE] 所有开关状态上报完成\r\n"); +} + +//====================== 蓝牙接收数据处理 ====================== + +// 处理总开关控制指令 +static int ble_handle_master_switch(cJSON *dataItem) { + cJSON *onItem = cJSON_GetObjectItem(dataItem, "on"); + if (onItem == NULL || !cJSON_IsNumber(onItem)) { + e_printf("[BLE] 总开关控制指令格式错误\r\n"); + return -1; + } + + bool new_state = (onItem->valueint != 0); + e_printf("[BLE] 接收到总开关控制指令: %s\r\n", new_state ? "开" : "关"); + + // 调用现有的总开关控制函数 + update_master_switch(new_state); + + // 通过蓝牙上报状态确认 + ble_report_master_switch(); + + return 0; +} + +// 处理子开关控制指令 +static int ble_handle_switch_control(const char *svc_id, cJSON *dataItem) { + // 解析开关ID (switch1 -> 0, switch2 -> 1, etc.) + int switch_id = -1; + if (strcmp(svc_id, "switch1") == 0) { + switch_id = 0; + } else if (strcmp(svc_id, "switch2") == 0) { + switch_id = 1; + } else if (strcmp(svc_id, "switch3") == 0) { + switch_id = 2; + } else if (strcmp(svc_id, "switch4") == 0) { + switch_id = 3; + } else { + e_printf("[BLE] 未知的开关ID: %s\r\n", svc_id); + return -1; + } + + cJSON *onItem = cJSON_GetObjectItem(dataItem, "on"); + if (onItem == NULL || !cJSON_IsNumber(onItem)) { + e_printf("[BLE] 开关%d控制指令格式错误\r\n", switch_id + 1); + return -1; + } + + bool new_state = (onItem->valueint != 0); + e_printf("[BLE] 接收到开关%d控制指令: %s\r\n", switch_id + 1, new_state ? "开" : "关"); + + // 调用现有的开关控制函数 + update_switch_state(switch_id, new_state); + + // 通过蓝牙上报状态确认 + ble_report_switch_state(switch_id); + + return 0; +} + +// 处理状态查询指令 +static int ble_handle_status_query(const char *svc_id) { + e_printf("[BLE] 接收到状态查询: %s\r\n", svc_id); + + if (strcmp(svc_id, "switch") == 0) { + // 查询总开关状态 + ble_report_master_switch(); + } else if (strncmp(svc_id, "switch", 6) == 0 && strlen(svc_id) == 7) { + // 查询子开关状态 (switch1, switch2, etc.) + int switch_id = svc_id[6] - '1'; // '1' -> 0, '2' -> 1, etc. + if (switch_id >= 0 && switch_id < SWITCH_COUNT) { + ble_report_switch_state(switch_id); + } else { + e_printf("[BLE] 无效的开关查询ID: %s\r\n", svc_id); + return -1; + } + } else { + e_printf("[BLE] 未知的查询ID: %s\r\n", svc_id); + return -1; + } + + return 0; +} + +//====================== 蓝牙数据处理主函数 ====================== + +// 处理蓝牙接收的自定义数据 +int switch_panel_ble_handle_custom_data(const char *buff, unsigned int len) { + if (buff == NULL || len == 0) { + e_printf("[BLE] 接收数据为空\r\n"); + return -1; + } + + e_printf("[BLE] 接收到数据: %s (长度: %u)\r\n", buff, len); + + // 处理空JSON请求 - 上报全量状态 + if (strcmp(buff, "{}") == 0) { + e_printf("[BLE] 收到全量状态请求\r\n"); + ble_report_all_switches(); + return 0; + } + + // 解析JSON数据 + cJSON *json = cJSON_Parse(buff); + if (json == NULL) { + e_printf("[BLE] JSON解析失败\r\n"); + return -1; + } + + int ret = -1; + + do { + // 获取服务ID + cJSON *sidItem = cJSON_GetObjectItem(json, "sid"); + if (sidItem == NULL || !cJSON_IsString(sidItem) || sidItem->valuestring == NULL) { + e_printf("[BLE] 缺少或无效的服务ID\r\n"); + break; + } + + const char *svc_id = sidItem->valuestring; + cJSON *dataItem = cJSON_GetObjectItem(json, "data"); + + // 处理特殊服务ID + if (strcmp(svc_id, "allservices") == 0) { + // H5连接时的状态同步请求 + e_printf("[BLE] H5连接,同步所有状态\r\n"); + ble_report_all_switches(); + ret = 0; + break; + } else if (strcmp(svc_id, "currentTime") == 0) { + // 时间同步(可选实现) + e_printf("[BLE] 接收到时间同步请求\r\n"); + ret = 0; + break; + } + + // 如果没有data字段,表示状态查询 + if (dataItem == NULL) { + ret = ble_handle_status_query(svc_id); + break; + } + + // 处理控制指令 + if (strcmp(svc_id, "switch") == 0) { + // 总开关控制 + ret = ble_handle_master_switch(dataItem); + } else if (strncmp(svc_id, "switch", 6) == 0 && strlen(svc_id) == 7) { + // 子开关控制 (switch1, switch2, switch3, switch4) + ret = ble_handle_switch_control(svc_id, dataItem); + } else { + e_printf("[BLE] 未知的服务ID: %s\r\n", svc_id); + } + + } while (0); + + cJSON_Delete(json); + return ret; +} + +//====================== 蓝牙模式管理 ====================== + +// 启用蓝牙控制模式 +void switch_panel_ble_enable(void) { + g_ble_mode_enabled = true; + e_printf("[BLE] 蓝牙控制模式已启用\r\n"); +} + +// 禁用蓝牙控制模式 +void switch_panel_ble_disable(void) { + g_ble_mode_enabled = false; + e_printf("[BLE] 蓝牙控制模式已禁用\r\n"); +} + +// 检查蓝牙控制模式是否启用 +bool switch_panel_ble_is_enabled(void) { + return g_ble_mode_enabled; +} + +//====================== 蓝牙上报接口 ====================== + +// 蓝牙模式下的快速上报函数 +int switch_panel_ble_fast_report(const char *svc_id) { + if (!g_ble_mode_enabled) { + return -1; // 蓝牙模式未启用 + } + + if (svc_id == NULL) { + e_printf("[BLE] 服务ID为空\r\n"); + return -1; + } + + e_printf("[BLE] 快速上报服务: %s\r\n", svc_id); + + if (strcmp(svc_id, "switch") == 0) { + ble_report_master_switch(); + } else if (strcmp(svc_id, "switch1") == 0) { + ble_report_switch_state(0); + } else if (strcmp(svc_id, "switch2") == 0) { + ble_report_switch_state(1); + } else if (strcmp(svc_id, "switch3") == 0) { + ble_report_switch_state(2); + } else if (strcmp(svc_id, "switch4") == 0) { + ble_report_switch_state(3); + } else { + e_printf("[BLE] 未知的服务ID: %s\r\n", svc_id); + return -1; + } + + return 0; +} \ No newline at end of file diff --git a/application/ws63/user_main/switch_panel/switch_panel_config.c b/application/ws63/user_main/switch_panel/switch_panel_config.c index 7a26cd0..dfb2d41 100644 --- a/application/ws63/user_main/switch_panel/switch_panel_config.c +++ b/application/ws63/user_main/switch_panel/switch_panel_config.c @@ -10,35 +10,190 @@ #include "timer.h" #include "soc_osal.h" #include "systick.h" +#include "wifi_hotspot.h" +#include "wifi_hotspot_config.h" +#include "lwip/netifapi.h" //====================== 配网相关变量 ====================== -static bool g_config_led_blink_state = false; -static bool g_panel_led_blink_state = false; -static uint32_t g_config_blink_start_time = 0; -static int g_config_key_id = -1; // 触发配网的按键 +int g_config_key_id = -1; // 触发配网的按键 - 移除static使其可被外部访问 + +//====================== 产测相关常量定义 ====================== +#define FACTORY_TEST_RSSI_THRESHOLD (-70) // WiFi信号强度阈值 -70dBm +#define WIFI_SCAN_AP_LIMIT 64 // 最大扫描AP数量 +#define WIFI_MAX_SSID_LEN 33 // SSID最大长度 +#define WIFI_MAC_LEN 6 // MAC地址长度 +#define WIFI_STA_SAMPLE_LOG "[FACTORY]" // 日志前缀 + +//====================== 产测WiFi状态枚举 ====================== +enum { + WIFI_STA_SAMPLE_INIT = 0, // 初始态 + WIFI_STA_SAMPLE_SCANING, // 扫描中 + WIFI_STA_SAMPLE_SCAN_DONE, // 扫描完成 + WIFI_STA_SAMPLE_FOUND_TARGET, // 匹配到目标AP +} wifi_state_enum; //====================== 产测相关变量 ====================== static bool g_factory_test_running = false; static int g_factory_test_step = 0; static uint32_t g_factory_test_start_time = 0; +static uint8_t g_wifi_state = WIFI_STA_SAMPLE_INIT; -//====================== 配网模式函数 ====================== +//====================== WiFi事件回调函数 ====================== +static void wifi_scan_state_changed(int32_t state, int32_t size) { + UNUSED(state); + UNUSED(size); + e_printf("%s WiFi扫描完成!\r\n", WIFI_STA_SAMPLE_LOG); + g_wifi_state = WIFI_STA_SAMPLE_SCAN_DONE; +} + +static void wifi_connection_changed(int32_t state, const wifi_linked_info_stru *info, int32_t reason_code) { + UNUSED(info); + UNUSED(reason_code); + // 产测模式下不需要连接,只需要扫描 +} + +static wifi_event_stru wifi_event_cb = { + .wifi_event_connection_changed = wifi_connection_changed, + .wifi_event_scan_state_changed = wifi_scan_state_changed, +}; + +//====================== WiFi扫描和产测函数 ====================== + +// 匹配目标AP并获取信号强度 +static int32_t get_match_network_rssi(const char* target_ssid, int32_t* rssi) { + int32_t ret; + uint32_t num = WIFI_SCAN_AP_LIMIT; + bool find_ap = false; + uint8_t bss_index; + + // 获取扫描结果 + uint32_t scan_len = sizeof(wifi_scan_info_stru) * WIFI_SCAN_AP_LIMIT; + wifi_scan_info_stru *result = osal_kmalloc(scan_len, OSAL_GFP_ATOMIC); + if (result == NULL) { + e_printf("%s 内存分配失败\r\n", WIFI_STA_SAMPLE_LOG); + return -1; + } + + memset_s(result, scan_len, 0, scan_len); + ret = wifi_sta_get_scan_info(result, &num); + if (ret != 0) { + e_printf("%s 获取扫描信息失败: %d\r\n", WIFI_STA_SAMPLE_LOG, ret); + osal_kfree(result); + return -1; + } + + // 筛选扫描到的WiFi网络,查找目标AP + for (bss_index = 0; bss_index < num; bss_index++) { + if (strlen(target_ssid) == strlen(result[bss_index].ssid)) { + if (memcmp(target_ssid, result[bss_index].ssid, strlen(target_ssid)) == 0) { + find_ap = true; + *rssi = result[bss_index].rssi; + e_printf("%s 找到目标AP: %s, 信号强度: %ddBm\r\n", + WIFI_STA_SAMPLE_LOG, target_ssid, *rssi); + break; + } + } + } + + osal_kfree(result); + + if (!find_ap) { + e_printf("%s 未找到目标AP: %s\r\n", WIFI_STA_SAMPLE_LOG, target_ssid); + return -1; + } + + return 0; +} + +// WiFi扫描并检查信号强度 +static int32_t wifi_scan_and_check(const char* target_ssid, int32_t rssi_threshold) { + int32_t rssi = 0; + g_wifi_state = WIFI_STA_SAMPLE_INIT; + + do { + osal_msleep(10); + + if (g_wifi_state == WIFI_STA_SAMPLE_INIT) { + e_printf("%s 开始WiFi扫描...\r\n", WIFI_STA_SAMPLE_LOG); + g_wifi_state = WIFI_STA_SAMPLE_SCANING; + + // 启动WiFi扫描 + if (wifi_sta_scan() != 0) { + e_printf("%s WiFi扫描启动失败\r\n", WIFI_STA_SAMPLE_LOG); + g_wifi_state = WIFI_STA_SAMPLE_INIT; + continue; + } + } else if (g_wifi_state == WIFI_STA_SAMPLE_SCAN_DONE) { + // 获取目标网络的信号强度 + if (get_match_network_rssi(target_ssid, &rssi) != 0) { + e_printf("%s 未找到目标AP: %s\r\n", WIFI_STA_SAMPLE_LOG, target_ssid); + return -1; + } + break; + } + } while (1); + + // 检查信号强度阈值 + if (rssi_threshold == 0) { + // 只检查是否找到目标AP,不检查信号强度 + return 0; + } + + e_printf("%s 信号强度测试: 期望 >= %ddBm, 实际 %ddBm\r\n", + WIFI_STA_SAMPLE_LOG, rssi_threshold, rssi); + + if (rssi >= rssi_threshold) { + return 0; // 测试通过 + } + + return -1; // 信号强度不足 +} + +// 初始化WiFi产测环境 +static int factory_test_wifi_init(void) { + // 注册WiFi事件回调 + if (wifi_register_event_cb(&wifi_event_cb) != 0) { + e_printf("%s WiFi事件回调注册失败\r\n", WIFI_STA_SAMPLE_LOG); + return -1; + } + + // 等待WiFi初始化完成 + int timeout = 100; // 10秒超时 + while (wifi_is_wifi_inited() == 0 && timeout-- > 0) { + msleep(100); + } + + if (timeout <= 0) { + e_printf("%s WiFi初始化超时\r\n", WIFI_STA_SAMPLE_LOG); + goto lab_err; + } + + // 创建STA接口 + if (wifi_sta_enable() != 0) { + e_printf("%s WiFi STA启动失败\r\n", WIFI_STA_SAMPLE_LOG); + goto lab_err; + } + + e_printf("%s WiFi产测环境初始化完成\r\n", WIFI_STA_SAMPLE_LOG); + return 0; +lab_err: + wifi_unregister_event_cb(&wifi_event_cb); + return -1; +} // 进入配网模式 void enter_config_mode(void) { - if (g_device_state.mode == MODE_CONFIG) { + if (g_runtime_state.mode == MODE_CONFIG) { e_printf("[CONFIG] 已在配网模式中\r\n"); return; } e_printf("[CONFIG] 进入配网模式\r\n"); + start_hilink_ble_net_config(CONFIG_TIMEOUT_MS/1000); // 更新设备模式 set_device_mode(MODE_CONFIG); - - // 记录配网开始时间 - g_config_blink_start_time = hfsys_get_time(); - + // 面板背光快闪1秒表示进入配网模式 panel_led_blink(); @@ -57,57 +212,17 @@ void enter_config_mode(void) { osal_kthread_set_priority(g_config_task_handle, TASK_PRIORITY_NORM); } - // 创建配网超时定时器 - if (!g_config_timeout_timer) { - int ret = uapi_timer_create(TIMER_INDEX_1, &g_config_timeout_timer); - if (ret != ERRCODE_SUCC) { - e_printf("[CONFIG] 创建配网超时定时器失败: %d\r\n", ret); - } - } - - if (g_config_timeout_timer) { - uapi_timer_start(g_config_timeout_timer, - CONFIG_TIMEOUT_MS * 1000, - config_timeout_timer_callback, - 0); - } - - // 创建闪烁定时器 - if (!g_config_blink_timer) { - int ret = uapi_timer_create(TIMER_INDEX_2, &g_config_blink_timer); - if (ret != ERRCODE_SUCC) { - e_printf("[CONFIG] 创建配网闪烁定时器失败: %d\r\n", ret); - } - } - - if (g_config_blink_timer) { - uint32_t blink_period = 1000000 / (LED_BLINK_FREQ_HZ * 2); // 半周期(微秒) - uapi_timer_start(g_config_blink_timer, - blink_period, - config_blink_timer_callback, - 0); - } - e_printf("[CONFIG] 配网模式已开启,超时时间: %d分钟\r\n", CONFIG_TIMEOUT_MS / 60000); } // 退出配网模式 void exit_config_mode(void) { - if (g_device_state.mode != MODE_CONFIG) { + if (g_runtime_state.mode != MODE_CONFIG) { return; } e_printf("[CONFIG] 退出配网模式\r\n"); - // 停止定时器 - if (g_config_timeout_timer) { - uapi_timer_stop(g_config_timeout_timer); - } - - if (g_config_blink_timer) { - uapi_timer_stop(g_config_blink_timer); - } - // 终止配网任务 if (g_config_task_handle) { osal_kthread_destroy(g_config_task_handle, 1); @@ -116,19 +231,17 @@ void exit_config_mode(void) { // 恢复正常LED状态 for (int i = 0; i < SWITCH_COUNT; i++) { - set_led_output(i, g_device_state.switches[i].led_state ? LED_WHITE : LED_YELLOW); + set_led_output(i, g_persistent_state.switches[i].led_state ? LED_WHITE : LED_YELLOW); } // 恢复面板背光状态 - set_panel_led(g_device_state.panel_led ? PANEL_LED_ON : PANEL_LED_OFF); + set_panel_led(g_persistent_state.panel_led ? PANEL_LED_ON : PANEL_LED_OFF); // 更新设备模式 set_device_mode(MODE_NORMAL); // 重置配网相关变量 g_config_key_id = -1; - g_config_led_blink_state = false; - g_panel_led_blink_state = false; e_printf("[CONFIG] 已退出配网模式\r\n"); } @@ -138,48 +251,67 @@ int config_mode_task(void *arg) { (void)arg; e_printf("[CONFIG] 配网任务开始\r\n"); + + // 记录配网开始时间 + g_runtime_state.config_start_time = hfsys_get_time(); - while (g_device_state.mode == MODE_CONFIG && !osal_kthread_should_stop()) { - // 检查是否有产测热点 - if (check_factory_test_wifi()) { - e_printf("[CONFIG] 检测到产测热点,进入产测模式\r\n"); - exit_config_mode(); - enter_factory_test_mode(); + // 只有在首次启动时才检查产测热点 + if (g_persistent_state.is_first_boot && check_factory_test_wifi()) { + e_printf("[CONFIG] 检测到产测热点且为首次启动,进入产测模式\r\n"); + exit_config_mode(); + enter_factory_test_mode(); + return 0; + } + + uint32_t config_start_time = g_runtime_state.config_start_time; + bool led_blink_state = false; + uint32_t last_blink_time = 0; + const uint32_t blink_interval = 500; // 500ms闪烁间隔 (1Hz) + + while (g_runtime_state.mode == MODE_CONFIG && !osal_kthread_should_stop()) { + uint32_t current_time = hfsys_get_time(); + uint32_t elapsed_time = current_time - config_start_time; + + // 检查是否超时 (10分钟) + if (elapsed_time >= CONFIG_TIMEOUT_MS) { + e_printf("[CONFIG] 配网超时,退出配网模式\r\n"); break; } - // 每秒检查一次 - osal_msleep(1000); - } - - e_printf("[CONFIG] 配网任务结束\r\n"); - return 0; -} - -// 配网LED闪烁 -void config_led_blink(void) { - uint32_t current_time = hfsys_get_time(); - uint32_t elapsed_time = current_time - g_config_blink_start_time; - - // 前3分钟闪烁,后7分钟常亮 - if (elapsed_time < CONFIG_BLINK_MS) { - // 只有触发配网的按键LED闪烁,其他LED保持常亮 - for (int i = 0; i < SWITCH_COUNT; i++) { - if (i == g_config_key_id) { - // 触发配网的按键LED闪烁 - g_config_led_blink_state = !g_config_led_blink_state; - set_led_output(i, g_config_led_blink_state ? LED_WHITE : LED_YELLOW); - } else { - // 其他按键LED保持常亮 + // LED控制逻辑 + if (elapsed_time < CONFIG_BLINK_MS) { // 前3分钟闪烁 + // 检查是否需要切换LED状态 + if (current_time - last_blink_time >= blink_interval) { + led_blink_state = !led_blink_state; + last_blink_time = current_time; + + // 只有触发配网的按键LED闪烁,其他LED保持黄灯常亮 + for (int i = 0; i < SWITCH_COUNT; i++) { + if (i == g_config_key_id) { + // 触发配网的按键LED闪烁 + set_led_output(i, led_blink_state ? LED_WHITE : LED_YELLOW); + } else { + // 其他按键LED保持黄灯常亮 + set_led_output(i, LED_YELLOW); + } + } + } + } else { // 后7分钟常亮 + // 所有LED保持黄灯常亮 + for (int i = 0; i < SWITCH_COUNT; i++) { set_led_output(i, LED_YELLOW); } } - } else { - // 超过3分钟后,所有LED保持常亮 - for (int i = 0; i < SWITCH_COUNT; i++) { - set_led_output(i, LED_YELLOW); - } + + // 每100ms检查一次 + osal_msleep(100); } + + e_printf("[CONFIG] 配网任务结束\r\n"); + g_config_task_handle = NULL; + // 退出配网模式 + exit_config_mode(); + return 0; } // 面板背光快闪 @@ -193,7 +325,7 @@ void panel_led_blink(void) { } // 恢复原状态 - set_panel_led(g_device_state.panel_led ? PANEL_LED_ON : PANEL_LED_OFF); + set_panel_led(g_persistent_state.panel_led ? PANEL_LED_ON : PANEL_LED_OFF); } //====================== 产测模式函数 ====================== @@ -299,34 +431,30 @@ void factory_test_sequence(void) { // 检查产测热点 bool check_factory_test_wifi(void) { - // 简化实现:直接返回false,实际应该扫描WiFi热点 - // TODO: 实际实现需要调用WiFi扫描接口查找 "ShuorongSelfTest" 热点 + // 使用WiFi扫描功能检测产测热点 "ShuorongSelfTest" + // 并校验信号强度 >= -70dBm - return false; // 默认未找到产测热点 -} - -//====================== 定时器回调函数 ====================== - -// 配网超时定时器回调 -void config_timeout_timer_callback(uintptr_t data) { - (void)data; + static bool wifi_initialized = false; - e_printf("[CONFIG] 配网超时,退出配网模式\r\n"); - - // 超时退出配网模式 - exit_config_mode(); -} - -// 配网闪烁定时器回调 -void config_blink_timer_callback(uintptr_t data) { - (void)data; - uint32_t blink_period = 1000000 / (LED_BLINK_FREQ_HZ * 2); // 半周期(微秒) - // 执行闪烁操作 - config_led_blink(); - if (g_config_blink_timer) { - uapi_timer_start(g_config_blink_timer, - blink_period, - config_blink_timer_callback, - 0); + // 初始化WiFi环境(只初始化一次) + if (!wifi_initialized) { + if (factory_test_wifi_init() != 0) { + e_printf("%s WiFi环境初始化失败\r\n", WIFI_STA_SAMPLE_LOG); + return false; + } + wifi_initialized = true; + + // 等待WiFi稳定 + // osal_msleep(1000); } -} \ No newline at end of file + + // 扫描并检查目标热点 + if (wifi_scan_and_check(FACTORY_TEST_SSID, FACTORY_TEST_RSSI_THRESHOLD) == 0) { + e_printf("%s 产测热点检测通过: %s, 信号强度满足要求\r\n", + WIFI_STA_SAMPLE_LOG, FACTORY_TEST_SSID); + return true; + } + + return false; // 未找到合格的产测热点 +} + diff --git a/application/ws63/user_main/switch_panel/switch_panel_hilink.c b/application/ws63/user_main/switch_panel/switch_panel_hilink.c index c8fc15c..5245662 100644 --- a/application/ws63/user_main/switch_panel/switch_panel_hilink.c +++ b/application/ws63/user_main/switch_panel/switch_panel_hilink.c @@ -15,68 +15,73 @@ static int handle_put_switch_common(int switch_id, const char* svc_id, // 处理设备上线事件 void handle_device_online(void) { - e_printf("[HILINK] 设备上线\r\n"); + e_printf("设备上线\r\n"); // 更新设备绑定状态 - g_device_state.is_bound = true; + g_persistent_state.is_bound = true; + + // 如果是首次启动,标记为非首次启动(首次绑定完成) + if (g_persistent_state.is_first_boot) { + g_persistent_state.is_first_boot = false; + e_printf("首次绑定完成,标记为非首次启动\r\n"); + } // 退出配网模式 - if (g_device_state.mode == MODE_CONFIG) { + if (g_runtime_state.mode == MODE_CONFIG) { exit_config_mode(); } + // 设备上线时禁用蓝牙模式,启用云端模式 + extern void switch_panel_ble_disable(void); + switch_panel_ble_disable(); + // 同步所有状态到云端 sync_cloud_state(); // 保存状态 - save_device_state(); + save_persistent_state(); } // 处理设备下线事件 void handle_device_offline(void) { - e_printf("[HILINK] 设备下线\r\n"); + e_printf("设备下线\r\n"); + + // 设备下线时启用蓝牙模式,支持本地控制 + extern void switch_panel_ble_enable(void); + switch_panel_ble_enable(); // 设备下线时保持现有状态,不做特殊处理 } // 处理设备解绑事件 void handle_device_unbind(void) { - e_printf("[HILINK] 设备解绑\r\n"); + e_printf("设备解绑\r\n"); // 更新设备绑定状态 - g_device_state.is_bound = false; - g_device_state.mode = MODE_UNBIND; + g_persistent_state.is_bound = false; + g_runtime_state.mode = MODE_UNBIND; // 重置为出厂默认状态 - reset_device_state(); - g_device_state.is_bound = false; // 保持未绑定状态 + reset_persistent_state(); + g_persistent_state.is_bound = false; // 保持未绑定状态 // 同步硬件状态 sync_hardware_state(); // 保存状态 - save_device_state(); + save_persistent_state(); - e_printf("[HILINK] 设备已重置为出厂默认状态\r\n"); + e_printf("设备已重置为出厂默认状态\r\n"); } // 同步所有状态到云端 void sync_cloud_state(void) { - e_printf("[HILINK] 开始同步状态到云端\r\n"); + e_printf("开始同步状态到云端\r\n"); - // 上报总开关状态 - extern int fast_report(const char* svc_id); - fast_report("switch"); + // 使用批量异步上报所有开关状态 + fast_report_all_switches_async(); - // 上报所有子开关状态 - for (int i = 0; i < SWITCH_COUNT; i++) { - char svc_id[16] = {0}; - snprintf(svc_id, sizeof(svc_id), "switch%d", i + 1); - fast_report(svc_id); - // osDelay(pdMS_TO_TICKS(100)); // 防止上报过快 - } - - e_printf("[HILINK] 状态同步完成\r\n"); + e_printf("状态同步完成\r\n"); } //====================== HiLink 服务处理函数 ====================== @@ -84,24 +89,20 @@ void sync_cloud_state(void) { // 处理总开关PUT命令 int handle_put_switch(const char* svc_id, const char* payload, unsigned int len) { if (!svc_id || !payload) { - e_printf("[HILINK] handle_put_switch 参数无效\r\n"); + e_printf("handle_put_switch 参数无效\r\n"); return -1; } - e_printf("[HILINK] 收到总开关控制命令: %s\r\n", payload); + e_printf("收到总开关控制命令: %s\r\n", payload); cJSON* json = cJSON_Parse(payload); if (!json) { - e_printf("[HILINK] JSON解析失败\r\n"); + e_printf("JSON解析失败\r\n"); return -1; } cJSON* on_item = cJSON_GetObjectItem(json, "on"); - if (on_item && cJSON_IsBool(on_item)) { - bool state = cJSON_IsTrue(on_item); - update_master_switch(state); - e_printf("[HILINK] 总开关设置为: %s\r\n", state ? "开" : "关"); - } + update_master_switch(cJSON_GetNumberValue(on_item)); cJSON_Delete(json); return 0; @@ -120,10 +121,10 @@ int handle_get_switch(const char* svc_id, const char* in, unsigned int in_len, return -1; } - *out_len = snprintf(*out, *out_len, "{\"on\":%s}", - g_device_state.master_switch ? "true" : "false"); + *out_len = snprintf(*out, *out_len, "{\"on\":%d}", + g_persistent_state.master_switch ? 1 : 0); - e_printf("[HILINK] 返回总开关状态: %s\r\n", *out); + e_printf("返回总开关状态: %s\r\n", *out); return 0; } @@ -177,32 +178,25 @@ int handle_get_switch4(const char* svc_id, const char* in, unsigned int in_len, static int handle_put_switch_common(int switch_id, const char* svc_id, const char* payload, unsigned int len) { if (switch_id < 0 || switch_id >= SWITCH_COUNT || !svc_id || !payload) { - e_printf("[HILINK] handle_put_switch%d 参数无效\r\n", switch_id + 1); + e_printf("handle_put_switch%d 参数无效\r\n", switch_id + 1); return -1; } - e_printf("[HILINK] 收到开关%d控制命令: %s\r\n", switch_id + 1, payload); + e_printf("收到开关%d控制命令: %s\r\n", switch_id + 1, payload); cJSON* json = cJSON_Parse(payload); if (!json) { - e_printf("[HILINK] JSON解析失败\r\n"); + e_printf("JSON解析失败\r\n"); return -1; } cJSON* on_item = cJSON_GetObjectItem(json, "on"); - if (on_item && cJSON_IsBool(on_item)) { - bool state = cJSON_IsTrue(on_item); - update_switch_state(switch_id, state); - e_printf("[HILINK] 开关%d设置为: %s\r\n", - switch_id + 1, state ? "开" : "关"); + if (on_item) { + update_switch_state(switch_id, cJSON_GetNumberValue(on_item)); } - - // 检查是否有name字段(预留功能) cJSON* name_item = cJSON_GetObjectItem(json, "name"); - if (name_item && cJSON_IsString(name_item)) { - e_printf("[HILINK] 开关%d名称: %s\r\n", - switch_id + 1, cJSON_GetStringValue(name_item)); - // TODO: 将来可以存储开关名称 + if (name_item) { + set_switch_name(switch_id, cJSON_GetStringValue(name_item)); } cJSON_Delete(json); @@ -223,13 +217,12 @@ static int handle_get_switch_common(int switch_id, const char* svc_id, return -1; } - // 返回开关状态和名称 *out_len = snprintf(*out, *out_len, - "{\"on\":%s,\"name\":\"开关%d\"}", - g_device_state.switches[switch_id].switch_on ? "true" : "false", - switch_id + 1); + "{\"on\":%d,\"name\":\"%s\"}", + g_persistent_state.switches[switch_id].switch_on ? 1 : 0, + g_persistent_state.switches[switch_id].name); - e_printf("[HILINK] 返回开关%d状态: %s\r\n", switch_id + 1, *out); + e_printf("返回开关%d状态: %s\r\n", switch_id + 1, *out); return 0; } @@ -237,24 +230,10 @@ static int handle_get_switch_common(int switch_id, const char* svc_id, // 检查设备是否在线 bool is_device_online(void) { - return g_device_state.is_bound; + return g_persistent_state.is_bound; } // 获取设备当前模式 system_mode_t get_current_mode(void) { - return g_device_state.mode; -} - -// 设置设备工作模式 -void set_device_mode(system_mode_t mode) { - if (g_device_state.mode != mode) { - system_mode_t old_mode = g_device_state.mode; - g_device_state.mode = mode; - - e_printf("[MODE] 设备模式切换: %s -> %s\r\n", - get_mode_string(old_mode), get_mode_string(mode)); - - // 保存模式变更 - save_device_state(); - } + return g_runtime_state.mode; } \ No newline at end of file diff --git a/application/ws63/user_main/switch_panel/switch_panel_keys.c b/application/ws63/user_main/switch_panel/switch_panel_keys.c index 8e5c18e..09c9ac4 100644 --- a/application/ws63/user_main/switch_panel/switch_panel_keys.c +++ b/application/ws63/user_main/switch_panel/switch_panel_keys.c @@ -19,35 +19,35 @@ static key_timer_param_t g_timer_params[SWITCH_COUNT]; // 更新单个开关状态 void update_switch_state(int switch_id, bool state) { if (switch_id < 0 || switch_id >= SWITCH_COUNT) { - e_printf("[SWITCH] 无效的开关ID: %d\r\n", switch_id); + e_printf("无效的开关ID: %d\r\n", switch_id); return; } // 检查总开关是否允许操作 - if (!g_device_state.master_switch && state) { - e_printf("[SWITCH] 总开关关闭,不允许开启开关%d\r\n", switch_id + 1); + if (!g_persistent_state.master_switch && state) { + e_printf("总开关关闭,不允许开启开关%d\r\n", switch_id + 1); return; } // 更新开关状态 - if (g_device_state.switches[switch_id].switch_on != state) { - g_device_state.switches[switch_id].switch_on = state; + if (g_persistent_state.switches[switch_id].switch_on != state) { + g_persistent_state.switches[switch_id].switch_on = state; // 更新LED指示灯(开关开启时LED白灯,关闭时LED黄灯) - g_device_state.switches[switch_id].led_state = state; + g_persistent_state.switches[switch_id].led_state = state; // 同步硬件状态 set_switch_output(switch_id, state); set_led_output(switch_id, state ? LED_WHITE : LED_YELLOW); - e_printf("[SWITCH] 开关%d 状态更新: %s\r\n", + e_printf("开关%d 状态更新: %s\r\n", switch_id + 1, state ? "开" : "关"); // 立即保存状态 - save_device_state(); + save_persistent_state(); // 上报状态给云端 - if (g_device_state.is_bound) { + if (g_persistent_state.is_bound) { fast_report_switch(switch_id); } } @@ -55,19 +55,19 @@ void update_switch_state(int switch_id, bool state) { // 更新总开关状态 void update_master_switch(bool state) { - if (g_device_state.master_switch != state) { - g_device_state.master_switch = state; + if (g_persistent_state.master_switch != state) { + g_persistent_state.master_switch = state; - e_printf("[SWITCH] 总开关状态更新: %s\r\n", state ? "开" : "关"); + e_printf("总开关状态更新: %s\r\n", state ? "开" : "关"); // 应用总开关控制逻辑 apply_master_switch_control(); // 立即保存状态 - save_device_state(); + save_persistent_state(); // 上报状态给云端 - if (g_device_state.is_bound) { + if (g_persistent_state.is_bound) { fast_report_master_switch(); } } @@ -75,18 +75,42 @@ void update_master_switch(bool state) { // 应用总开关控制逻辑 void apply_master_switch_control(void) { - for (int i = 0; i < SWITCH_COUNT; i++) { - if (!g_device_state.master_switch) { - // 总开关关闭时,关闭所有开关但保持LED状态 - set_switch_output(i, false); - } else { - // 总开关开启时,恢复各开关的原状态 - set_switch_output(i, g_device_state.switches[i].switch_on); - } + + if (!g_persistent_state.master_switch) { + // 总开关关闭时,强制关闭所有子开关 + e_printf("总开关关闭,强制关闭所有子开关\r\n"); - // LED状态保持不变,始终显示实际的开关状态 - set_led_output(i, g_device_state.switches[i].led_state ? LED_WHITE : LED_YELLOW); + for (int i = 0; i < SWITCH_COUNT; i++) { + // 如果子开关之前是开着的,需要同步状态 + if (g_persistent_state.switches[i].switch_on) { + g_persistent_state.switches[i].switch_on = false; + g_persistent_state.switches[i].led_state = false; + e_printf("强制关闭子开关%d\r\n", i + 1); + } + + // 更新硬件状态 + set_switch_output(i, false); + set_led_output(i, LED_YELLOW); + } + } else { + // 总开关开启时,恢复各开关的原状态(不改变子开关状态) + // e_printf("总开关开启,恢复各子开关原有状态\r\n"); + + // for (int i = 0; i < SWITCH_COUNT; i++) { + // // 硬件状态跟随子开关的实际状态 + // set_switch_output(i, g_persistent_state.switches[i].switch_on); + // set_led_output(i, g_persistent_state.switches[i].led_state ? LED_WHITE : LED_YELLOW); + // } } + // 立即保存状态 + save_persistent_state(); + + // 同步所有子开关状态到云端 + if (g_persistent_state.is_bound) { + fast_report_all_switches_async(); + } + + e_printf("所有子开关状态已同步\r\n"); } //====================== 按键检测与处理 ====================== @@ -149,7 +173,7 @@ int key_scan_task(void *arg) { } g_key_states[i] = current_state; - g_device_state.switches[i].physical_key = current_state; + g_runtime_state.switches[i].physical_key = current_state; } // 检查是否达到长按时间(在按下期间监测) @@ -178,19 +202,19 @@ void handle_key_press(int key_id) { e_printf("[KEY] 处理按键%d 短按事件\r\n", key_id + 1); // 在配网模式下,忽略短按事件 - if (g_device_state.mode == MODE_CONFIG) { + if (g_runtime_state.mode == MODE_CONFIG) { e_printf("[KEY] 配网模式下,忽略短按事件\r\n"); return; } // 在产测模式下,忽略短按事件 - if (g_device_state.mode == MODE_FACTORY_TEST) { + if (g_runtime_state.mode == MODE_FACTORY_TEST) { e_printf("[KEY] 产测模式下,忽略短按事件\r\n"); return; } // 正常模式下切换开关状态 - bool current_state = g_device_state.switches[key_id].switch_on; + bool current_state = g_persistent_state.switches[key_id].switch_on; update_switch_state(key_id, !current_state); } @@ -204,13 +228,19 @@ void handle_key_long_press(int key_id) { // 只有第一个按键支持长按进入配网模式 if (key_id == 0) { - e_printf("[KEY] 长按第一个按键,进入配网模式\r\n"); + e_printf("[KEY] 长按第一个按键,检查配网条件\r\n"); - // 只有在正常模式下才能进入配网模式 - if (g_device_state.mode == MODE_NORMAL) { + // 只有在正常模式下且设备未绑定时才能进入配网模式 + if (g_runtime_state.mode == MODE_NORMAL && !g_persistent_state.is_bound) { + extern int g_config_key_id; + g_config_key_id = key_id; // 设置触发配网的按键ID enter_config_mode(); } else { - e_printf("[KEY] 非正常模式,不能进入配网模式\r\n"); + if (g_persistent_state.is_bound) { + e_printf("[KEY] 设备已绑定,不能进入配网模式\r\n"); + } else { + e_printf("[KEY] 非正常模式,不能进入配网模式\r\n"); + } } } else { e_printf("[KEY] 非第一个按键的长按,忽略\r\n"); @@ -226,15 +256,19 @@ int key_system_init(void) { ret = uapi_timer_init(); if (ret != ERRCODE_SUCC) { e_printf("[KEY] 定时器初始化失败: %d\r\n", ret); + // return HF_FAIL; + } + ret = uapi_timer_adapter(TIMER_INDEX_1, TIMER_1_IRQN, 1); + if (ret != 0) { + e_printf("定时器适配器初始化失败\r\n"); return HF_FAIL; } - // 初始化定时器参数 for (int i = 0; i < SWITCH_COUNT; i++) { g_timer_params[i].key_id = i; // 创建防抖定时器 - ret = uapi_timer_create(TIMER_INDEX_0, &g_key_debounce_timer[i]); + ret = uapi_timer_create(TIMER_INDEX_1, &g_key_debounce_timer[i]); if (ret != ERRCODE_SUCC) { e_printf("[KEY] 创建按键%d防抖定时器失败: %d\r\n", i + 1, ret); return HF_FAIL; @@ -242,7 +276,7 @@ int key_system_init(void) { // 初始化按键状态 g_key_states[i] = get_key_input(i); - g_device_state.switches[i].physical_key = g_key_states[i]; + g_runtime_state.switches[i].physical_key = g_key_states[i]; } // 创建按键扫描任务 @@ -256,7 +290,7 @@ int key_system_init(void) { } // 设置任务优先级 - ret = osal_kthread_set_priority(g_key_scan_task_handle, TASK_PRIORITY_NORM); + ret = osal_kthread_set_priority(g_key_scan_task_handle, TASK_PRIORITY_HIGH); if (ret != 0) { e_printf("[KEY] 设置按键扫描任务优先级失败: %d\r\n", ret); } @@ -265,28 +299,12 @@ int key_system_init(void) { return HF_SUCCESS; } -//====================== 快速上报函数 ====================== - -// 快速上报单个开关状态 +// 快速上报单个开关状态(兼容旧接口,内部使用异步上报) void fast_report_switch(int switch_id) { - if (switch_id < 0 || switch_id >= SWITCH_COUNT) { - return; - } - - char svc_id[16] = {0}; - snprintf(svc_id, sizeof(svc_id), "switch%d", switch_id + 1); - - // 使用已有的fast_report函数 - extern int fast_report(const char* svc_id); - fast_report(svc_id); - - e_printf("[REPORT] 已上报开关%d状态\r\n", switch_id + 1); + fast_report_switch_async(switch_id); } -// 快速上报总开关状态 +// 快速上报总开关状态(兼容旧接口,内部使用异步上报) void fast_report_master_switch(void) { - extern int fast_report(const char* svc_id); - fast_report("switch"); - - e_printf("[REPORT] 已上报总开关状态\r\n"); + fast_report_master_switch_async(); } \ No newline at end of file diff --git a/application/ws63/user_main/switch_panel/switch_panel_main.c b/application/ws63/user_main/switch_panel/switch_panel_main.c index 942189d..335cd54 100644 --- a/application/ws63/user_main/switch_panel/switch_panel_main.c +++ b/application/ws63/user_main/switch_panel/switch_panel_main.c @@ -7,29 +7,270 @@ #include "switch_panel.h" //====================== 全局变量 ====================== -device_state_t g_device_state = { +device_persistent_state_t g_persistent_state = { .is_first_boot = true, .is_bound = false, .master_switch = false, .panel_led = true, - .mode = MODE_NORMAL, .switches = { - {.switch_on = false, .led_state = false, .physical_key = true}, - {.switch_on = false, .led_state = false, .physical_key = true}, - {.switch_on = false, .led_state = false, .physical_key = true}, - {.switch_on = false, .led_state = false, .physical_key = true} + {.switch_on = false, .led_state = false}, + {.switch_on = false, .led_state = false}, + {.switch_on = false, .led_state = false}, + {.switch_on = false, .led_state = false} + }, + .magic = DEVICE_DATA_MAGIC, + .version = DEVICE_DATA_VERSION +}; + +device_runtime_state_t g_runtime_state = { + .mode = MODE_NORMAL, + .ble_mode_enabled = false, + .config_start_time = 0, + .config_key_id = -1, + .config_led_blink_state = false, + .factory_test_running = false, + .last_save_time = 0, + .switches = { + {.physical_key = true, .press_time = 0, .debounce_flag = false, .long_press_handled = false}, + {.physical_key = true, .press_time = 0, .debounce_flag = false, .long_press_handled = false}, + {.physical_key = true, .press_time = 0, .debounce_flag = false, .long_press_handled = false}, + {.physical_key = true, .press_time = 0, .debounce_flag = false, .long_press_handled = false} } }; + timer_handle_t g_key_debounce_timer[SWITCH_COUNT] = {0}; -timer_handle_t g_config_timeout_timer = 0; -timer_handle_t g_config_blink_timer = 0; +// 配网定时器变量已移除,配网逻辑简化 osal_task *g_key_scan_task_handle = NULL; osal_task *g_config_task_handle = NULL; +osal_task *g_save_task_handle = NULL; // 异步保存任务句柄 +osal_task *g_report_task_handle = NULL; // 异步上报任务句柄 static bool g_initialized = false; static uint32_t g_config_start_time = 0; static bool g_config_led_state = false; +//====================== 异步保存系统 ====================== +#include "osal_mutex.h" +#include "osal_semaphore.h" + +static osal_mutex g_save_mutex; // 保护持久化状态的互斥锁 +static osal_semaphore g_save_semaphore; // 触发保存的信号量 +static bool g_save_system_running = false; // 异步保存系统是否运行中 +static bool g_save_mutex_initialized = false; // 互斥锁是否已初始化 +static bool g_save_sem_initialized = false; // 信号量是否已初始化 + +//====================== 异步上报系统实现 ====================== +#include "osal_semaphore.h" +#include "osal_mutex.h" + +static osal_semaphore g_report_semaphore; // 触发上报的信号量 +static osal_mutex g_report_mutex; // 保护上报掩码的互斥锁 +static volatile uint8_t g_report_mask = 0; // 待上报的服务ID位掩码 +static bool g_report_system_running = false; // 异步上报系统是否运行中 +static bool g_report_sem_initialized = false; // 信号量是否已初始化 +static bool g_report_mutex_initialized = false; // 互斥锁是否已初始化 + +// 异步上报任务 +int async_report_task(void *arg) { + (void)arg; + + e_printf("异步上报任务启动\r\n"); + + // 引用外部的fast_report函数 + extern int fast_report(const char* svc_id); + + while (g_report_system_running && !osal_kthread_should_stop()) { + // 等待上报信号量 + if (osal_sem_down(&g_report_semaphore) == OSAL_SUCCESS) { + if (!g_report_system_running) { + break; // 系统关闭 + } + + // 获取当前的上报掩码(使用互斥锁保护) + uint8_t current_mask = 0; + if (g_report_mutex_initialized && osal_mutex_lock(&g_report_mutex) == OSAL_SUCCESS) { + current_mask = g_report_mask; + g_report_mask = 0; // 清零掩码 + osal_mutex_unlock(&g_report_mutex); + } else { + current_mask = g_report_mask; + g_report_mask = 0; + } + + if (current_mask == 0) { + continue; // 无上报任务 + } + + e_printf("开始异步上报,掩码: 0x%02X\r\n", current_mask); + + // 上报总开关 + if (current_mask & REPORT_SWITCH_MASK) { + fast_report("switch"); + e_printf("上报总开关状态\r\n"); + osal_msleep(50); // 防止上报过快 + } + + // 上报各个子开关 + for (int i = 0; i < SWITCH_COUNT; i++) { + uint8_t switch_mask = (REPORT_SWITCH1_MASK << i); + if (current_mask & switch_mask) { + char svc_id[16] = {0}; + snprintf(svc_id, sizeof(svc_id), "switch%d", i + 1); + fast_report(svc_id); + e_printf("上报开关%d状态\r\n", i + 1); + osal_msleep(50); // 防止上报过快 + } + } + + e_printf("异步上报完成\r\n"); + } + } + + e_printf("异步上报任务退出\r\n"); + return 0; +} + +// 初始化异步上报系统 +int report_system_init(void) { + // 初始化互斥锁 + if (osal_mutex_init(&g_report_mutex) != OSAL_SUCCESS) { + e_printf("初始化上报互斥锁失败\r\n"); + return HF_FAIL; + } + g_report_mutex_initialized = true; + + // 初始化信号量(初始值为0) + if (osal_sem_init(&g_report_semaphore, 0) != OSAL_SUCCESS) { + e_printf("初始化上报信号量失败\r\n"); + osal_mutex_destroy(&g_report_mutex); + g_report_mutex_initialized = false; + return HF_FAIL; + } + g_report_sem_initialized = true; + + // 启动异步上报任务 + g_report_system_running = true; + g_report_task_handle = osal_kthread_create((osal_kthread_handler)async_report_task, + NULL, + "report_task", + TASK_STACK_SIZE); + if (g_report_task_handle == NULL) { + e_printf("创建异步上报任务失败\r\n"); + g_report_system_running = false; + if (g_report_sem_initialized) { + osal_sem_destroy(&g_report_semaphore); + g_report_sem_initialized = false; + } + if (g_report_mutex_initialized) { + osal_mutex_destroy(&g_report_mutex); + g_report_mutex_initialized = false; + } + return HF_FAIL; + } + + // 设置任务优先级为低优先级,避免影响实时性 + osal_kthread_set_priority(g_report_task_handle, TASK_PRIORITY_LOW); + + e_printf("异步上报系统初始化成功\r\n"); + return HF_SUCCESS; +} + +// 清理异步上报系统 +void report_system_deinit(void) { + e_printf("清理异步上报系统\r\n"); + + // 停止上报系统 + g_report_system_running = false; + + // 唤醒上报任务使其退出 + if (g_report_sem_initialized) { + osal_sem_up(&g_report_semaphore); + } + + // 等待任务退出 + if (g_report_task_handle) { + osal_kthread_destroy(g_report_task_handle, 1); + g_report_task_handle = NULL; + } + + // 清理同步原语 + if (g_report_sem_initialized) { + osal_sem_destroy(&g_report_semaphore); + g_report_sem_initialized = false; + } + + if (g_report_mutex_initialized) { + osal_mutex_destroy(&g_report_mutex); + g_report_mutex_initialized = false; + } + + // 清零上报掩码 + g_report_mask = 0; + + e_printf("异步上报系统清理完成\r\n"); +} + +// 触发异步上报 +void trigger_async_report(uint8_t report_mask) { + if (!g_report_system_running || !g_report_sem_initialized) { + e_printf("异步上报系统未初始化,使用同步上报\r\n"); + // 如果异步系统未初始化,回退到同步上报 + extern int fast_report(const char* svc_id); + if (report_mask & REPORT_SWITCH_MASK) { + fast_report("switch"); + } + for (int i = 0; i < SWITCH_COUNT; i++) { + if (report_mask & (REPORT_SWITCH1_MASK << i)) { + char svc_id[16] = {0}; + snprintf(svc_id, sizeof(svc_id), "switch%d", i + 1); + fast_report(svc_id); + } + } + return; + } + + // 使用互斥锁保护更新上报掩码 + bool should_signal = false; + if (g_report_mutex_initialized && osal_mutex_lock(&g_report_mutex) == OSAL_SUCCESS) { + uint8_t old_mask = g_report_mask; + g_report_mask |= report_mask; + should_signal = (old_mask == 0); + osal_mutex_unlock(&g_report_mutex); + } else { + uint8_t old_mask = g_report_mask; + g_report_mask |= report_mask; + should_signal = (old_mask == 0); + } + + // 如果是新的上报请求,唤醒上报任务 + if (should_signal) { + osal_sem_up(&g_report_semaphore); + e_printf("已触发异步上报,掩码: 0x%02X\r\n", report_mask); + } else { + e_printf("合并上报请求,掩码: 0x%02X\r\n", g_report_mask); + } +} + +// 异步上报单个开关 +void fast_report_switch_async(int switch_id) { + if (switch_id < 0 || switch_id >= SWITCH_COUNT) { + return; + } + + uint8_t switch_mask = (REPORT_SWITCH1_MASK << switch_id); + trigger_async_report(switch_mask); +} + +// 异步上报总开关 +void fast_report_master_switch_async(void) { + trigger_async_report(REPORT_SWITCH_MASK); +} + +// 异步上报所有开关 +void fast_report_all_switches_async(void) { + trigger_async_report(REPORT_ALL_MASK); +} + //====================== 存储管理函数 ====================== // 计算数据校验码 @@ -48,8 +289,9 @@ static uint8_t calculate_checksum(const uint8_t* data, int len) static bool read_device_data_from_addr(uint32_t addr, uint8_t* data, uint32_t len) { int total_size = sizeof(device_data_t) + len; - uint8_t checksum = 0; device_data_t *data_all = malloc(total_size); + uint8_t checksum; + if (data_all == NULL) { e_printf("内存分配失败\r\n"); return false; @@ -88,7 +330,7 @@ lab_err: // 写入数据到指定地址 static bool write_device_data_to_addr(uint32_t addr, uint8_t* data, uint32_t len) { - int ret = hfuflash_erase_page(addr, 1); + uint32_t ret = hfuflash_erase_page(addr, 1); if (ret != HF_SUCCESS) { e_printf("擦除地址0x%x的Flash页失败,错误码:%d\r\n", addr, ret); return false; @@ -99,7 +341,8 @@ static bool write_device_data_to_addr(uint32_t addr, uint8_t* data, uint32_t len e_printf("内存分配失败\r\n"); return false; } - memset(data_all, 0, total_size); + memset_s(data_all, total_size, 0, total_size); + data_all->data_len = len; memcpy_s(data_all->data, len, data, len); data_all->checksum = calculate_checksum(data_all->data, data_all->data_len); data_all->magic = DEVICE_DATA_MAGIC; @@ -115,10 +358,10 @@ static bool write_device_data_to_addr(uint32_t addr, uint8_t* data, uint32_t len } -// 从 Flash 加载设备状态 -int load_device_state(void) { +// 从 Flash 加载持久化状态 +int load_persistent_state(void) { int ret = 0; - device_state_t state; + device_persistent_state_t state; bool valid = false; // 尝试读取主数据区 @@ -131,96 +374,252 @@ int load_device_state(void) { // 两个存储区都失败,使用默认状态 if (!valid) { - e_printf("[STORAGE] 存储区数据损坏,使用默认状态\r\n"); - reset_device_state(); - save_device_state(); + e_printf("存储区数据损坏,使用默认状态\r\n"); + reset_persistent_state(); + save_persistent_state(); return 0; } - // 更新设备控制状态 - e_printf("设备状态恢复:\r\n"); - e_printf("首次启动: %d => %d\r\n", g_device_state.is_first_boot, state.is_first_boot); - e_printf("配网状态: %d => %d\r\n", g_device_state.is_bound, state.is_bound); - e_printf("总开关: %d => %d\r\n", g_device_state.master_switch, state.master_switch); - e_printf("面板背光: %d => %d\r\n", g_device_state.panel_led, state.panel_led); - e_printf("工作模式: %d => %d\r\n", g_device_state.mode, state.mode); + + // 更新持久化状态 + e_printf("持久化状态恢复:\r\n"); + e_printf("首次启动: %d => %d\r\n", g_persistent_state.is_first_boot, state.is_first_boot); + e_printf("配网状态: %d => %d\r\n", g_persistent_state.is_bound, state.is_bound); + e_printf("总开关: %d => %d\r\n", g_persistent_state.master_switch, state.master_switch); + e_printf("面板背光: %d => %d\r\n", g_persistent_state.panel_led, state.panel_led); + for (int i = 0; i < SWITCH_COUNT; i++) { - e_printf("开关%d: %d => %d, LED%d: %d => %d, 物理按键%d: %d => %d\r\n", - i + 1, g_device_state.switches[i].switch_on, state.switches[i].switch_on, - i + 1, g_device_state.switches[i].led_state, state.switches[i].led_state, - i + 1, g_device_state.switches[i].physical_key, state.switches[i].physical_key); + e_printf("开关%d(%s): %d => %d, LED%d: %d => %d\r\n", + i + 1, state.switches[i].name, + g_persistent_state.switches[i].switch_on, state.switches[i].switch_on, + i + 1, g_persistent_state.switches[i].led_state, state.switches[i].led_state); } - memcpy(&g_device_state, &state, sizeof(device_state_t)); + memcpy(&g_persistent_state, &state, sizeof(device_persistent_state_t)); return 0; } -// 保存设备状态到 Flash -int save_device_state(void) { - int ret = 0; - static device_state_t state = {}; - bool valid = false; - if (state.is_first_boot == g_device_state.is_first_boot && - state.is_bound == g_device_state.is_bound && - state.master_switch == g_device_state.master_switch && - state.panel_led == g_device_state.panel_led && - state.mode == g_device_state.mode) - { - for (int i = 0; i < SWITCH_COUNT; i++) { - if (state.switches[i].switch_on != g_device_state.switches[i].switch_on - || state.switches[i].led_state != g_device_state.switches[i].led_state - || state.switches[i].physical_key != g_device_state.switches[i].physical_key) { - valid = true; - break; +//====================== 异步保存系统实现 ====================== + +// 异步保存任务 +static int save_data_task(void *arg) { + (void)arg; + + e_printf("异步保存任务启动\r\n"); + + while (g_save_system_running && !osal_kthread_should_stop()) { + // 等待保存信号量 + if (osal_sem_down(&g_save_semaphore) == OSAL_SUCCESS) { + if (!g_save_system_running) { + break; // 系统关闭 + } + + // 执行实际的同步保存操作 + int ret = save_persistent_state_sync(); + if (ret != HF_SUCCESS) { + e_printf("异步保存失败: %d\r\n", ret); + } else { + e_printf("异步保存成功\r\n"); } } - if (!valid) { - e_printf("[STORAGE] 设备状态未变化,跳过保存\r\n"); - return HF_SUCCESS; - } - } - // 准备数据 - memcpy(&state, &g_device_state, sizeof(device_state_t)); - // 保存到主存储区 - ret = write_device_data_to_addr(DEVICE_DATA_FLASH_ADDR, (uint8_t*)&state, sizeof(state)); - if (ret != HF_SUCCESS) { - e_printf("[STORAGE] 写入主存储区失败: %d\r\n", ret); - return ret; } - // 保存到备份区 - ret = write_device_data_to_addr(DEVICE_DATA_BACKUP_ADDR, (uint8_t*)&state, sizeof(state)); - if (ret != HF_SUCCESS) { - e_printf("[STORAGE] 写入备份区失败: %d\r\n", ret); + e_printf("异步保存任务退出\r\n"); + return 0; +} + +// 初始化异步保存系统 +int save_system_init(void) { + int ret = HF_SUCCESS; + + // 初始化互斥锁 + if (osal_mutex_init(&g_save_mutex) != OSAL_SUCCESS) { + e_printf("初始化保存互斥锁失败\r\n"); + return HF_FAIL; } - e_printf("[STORAGE] 设备状态保存完成\r\n"); + g_save_mutex_initialized = true; + + // 初始化信号量(初始值为0) + if (osal_sem_init(&g_save_semaphore, 0) != OSAL_SUCCESS) { + e_printf("初始化保存信号量失败\r\n"); + osal_mutex_destroy(&g_save_mutex); + g_save_mutex_initialized = false; + return HF_FAIL; + } + g_save_sem_initialized = true; + + // 启动异步保存任务 + g_save_system_running = true; + g_save_task_handle = osal_kthread_create((osal_kthread_handler)save_data_task, + NULL, + "save_data_task", + TASK_STACK_SIZE); + if (g_save_task_handle == NULL) { + e_printf("创建异步保存任务失败\r\n"); + g_save_system_running = false; + if (g_save_sem_initialized) { + osal_sem_destroy(&g_save_semaphore); + g_save_sem_initialized = false; + } + if (g_save_mutex_initialized) { + osal_mutex_destroy(&g_save_mutex); + g_save_mutex_initialized = false; + } + return HF_FAIL; + } + + // 设置任务优先级为低优先级,避免影响实时性 + osal_kthread_set_priority(g_save_task_handle, TASK_PRIORITY_LOW); + + e_printf("异步保存系统初始化成功\r\n"); return HF_SUCCESS; } -// 重置设备状态为默认值 -void reset_device_state(void) { - // memset(&g_device_state, 0, sizeof(device_data_t)); +// 清理异步保存系统 +void save_system_deinit(void) { + e_printf("清理异步保存系统\r\n"); - // 设置默认状态 - g_device_state.master_switch = false; // 总开关关闭 - g_device_state.panel_led = true; // 面板背光开启 - g_device_state.is_bound = false; // 设备未绑定 - g_device_state.is_first_boot = false; // 标记为非首次启动 - g_device_state.mode = MODE_NORMAL; // 正常模式 + // 停止保存系统 + g_save_system_running = false; - // 所有开关默认关闭,所有LED默认黄灯 - for (int i = 0; i < SWITCH_COUNT; i++) { - g_device_state.switches[i].switch_on = false; // 开关关闭 - g_device_state.switches[i].led_state = false; // LED黄灯 - g_device_state.switches[i].physical_key = true; // 按键松开 + // 唤醒保存任务使其退出 + if (g_save_sem_initialized) { + osal_sem_up(&g_save_semaphore); } - e_printf("[STATE] 设备状态已重置为默认值\r\n"); + // 等待任务退出 + if (g_save_task_handle) { + osal_kthread_destroy(g_save_task_handle, 1); + g_save_task_handle = NULL; + } + + // 清理同步原语 + if (g_save_sem_initialized) { + osal_sem_destroy(&g_save_semaphore); + g_save_sem_initialized = false; + } + + if (g_save_mutex_initialized) { + osal_mutex_destroy(&g_save_mutex); + g_save_mutex_initialized = false; + } + + e_printf("异步保存系统清理完成\r\n"); +} + +// 异步保存持久化状态(触发保存任务) +int save_persistent_state(void) { + if (!g_save_system_running || !g_save_sem_initialized) { + e_printf("异步保存系统未初始化,使用同步保存\r\n"); + return save_persistent_state_sync(); + } + + // 发送保存信号 + osal_sem_up(&g_save_semaphore); + + e_printf("已触发异步保存\r\n"); + return HF_SUCCESS; +} + +// 同步保存持久化状态到 Flash(实际的Flash写入操作) +int save_persistent_state_sync(void) { + int ret = 0; + static device_persistent_state_t state = {}; + bool need_save = false; + + // 使用互斥锁保护状态访问 + if (g_save_mutex_initialized && osal_mutex_lock(&g_save_mutex) != OSAL_SUCCESS) { + e_printf("获取保存互斥锁失败\r\n"); + return HF_FAIL; + } + + // 检查是否有变化,避免不必要的Flash写入 + if (memcmp(&state, &g_persistent_state, sizeof(device_persistent_state_t)) != 0) { + need_save = true; + } + + if (!need_save) { + if (g_save_mutex_initialized) { + osal_mutex_unlock(&g_save_mutex); + } + e_printf("持久化状态未变化,跳过保存\r\n"); + return HF_SUCCESS; + } + + // 准备数据 + memcpy(&state, &g_persistent_state, sizeof(device_persistent_state_t)); + + // 释放互斥锁,避免在Flash写入过程中长时间锁定 + if (g_save_mutex_initialized) { + osal_mutex_unlock(&g_save_mutex); + } + + // 保存到主存储区 + bool ret_main = write_device_data_to_addr(DEVICE_DATA_FLASH_ADDR, (uint8_t*)&state, sizeof(state)); + if (!ret_main) { + e_printf("写入主存储区失败\r\n"); + return HF_FAIL; + } + + // 保存到备份区 + bool ret_backup = write_device_data_to_addr(DEVICE_DATA_BACKUP_ADDR, (uint8_t*)&state, sizeof(state)); + if (!ret_backup) { + e_printf("写入备份区失败\r\n"); + } + + g_runtime_state.last_save_time = hfsys_get_time(); + e_printf("持久化状态保存完成\r\n"); + return HF_SUCCESS; +} + +// 重置持久化状态为默认值 +void reset_persistent_state(void) { + // 设置默认状态 + g_persistent_state.master_switch = false; // 总开关关闭 + g_persistent_state.panel_led = true; // 面板背光开启 + g_persistent_state.is_bound = false; // 设备未绑定 + g_persistent_state.is_first_boot = true; // 标记为首次启动 + + // 所有开关默认关闭,所有LED默认黄灯,初始化默认名字 + for (int i = 0; i < SWITCH_COUNT; i++) { + g_persistent_state.switches[i].switch_on = false; // 开关关闭 + g_persistent_state.switches[i].led_state = false; // LED黄灯 + snprintf(g_persistent_state.switches[i].name, SWITCH_NAME_MAX_LEN, "开关%d", i + 1); // 默认名字 + } + + g_persistent_state.magic = DEVICE_DATA_MAGIC; + g_persistent_state.version = DEVICE_DATA_VERSION; + + e_printf("[STATE] 持久化状态已重置为默认值\r\n"); +} + +// 初始化运行时状态 +void init_runtime_state(void) { + g_runtime_state.mode = MODE_NORMAL; + g_runtime_state.ble_mode_enabled = false; + g_runtime_state.config_start_time = 0; + g_runtime_state.config_key_id = -1; + g_runtime_state.config_led_blink_state = false; + g_runtime_state.factory_test_running = false; + g_runtime_state.last_save_time = 0; + + // 初始化所有开关的运行时状态 + for (int i = 0; i < SWITCH_COUNT; i++) { + g_runtime_state.switches[i].physical_key = true; // 按键松开 + g_runtime_state.switches[i].press_time = 0; + g_runtime_state.switches[i].debounce_flag = false; + g_runtime_state.switches[i].long_press_handled = false; + } + + e_printf("[STATE] 运行时状态已初始化\r\n"); } //====================== GPIO配置数据结构 ====================== typedef struct { pin_t pin; + pin_mode_t mode; + pin_pull_t pull; + pin_drive_strength_t ds; gpio_direction_t direction; const char* name; } gpio_config_t; @@ -228,25 +627,25 @@ typedef struct { // GPIO初始化配置表 static const gpio_config_t gpio_configs[] = { // 开关控制 - 输出 - {SWITCH1_GPIO, GPIO_DIRECTION_OUTPUT, "SWITCH1"}, - {SWITCH2_GPIO, GPIO_DIRECTION_OUTPUT, "SWITCH2"}, - {SWITCH3_GPIO, GPIO_DIRECTION_OUTPUT, "SWITCH3"}, - {SWITCH4_GPIO, GPIO_DIRECTION_OUTPUT, "SWITCH4"}, + {SWITCH1_GPIO, PIN_MODE_1, PIN_PULL_TYPE_DOWN, PIN_DS_4, GPIO_DIRECTION_OUTPUT, "SWITCH1"}, + {SWITCH2_GPIO, PIN_MODE_1, PIN_PULL_TYPE_DOWN, PIN_DS_4, GPIO_DIRECTION_OUTPUT, "SWITCH2"}, + {SWITCH3_GPIO, PIN_MODE_1, PIN_PULL_TYPE_DOWN, PIN_DS_4, GPIO_DIRECTION_OUTPUT, "SWITCH3"}, + {SWITCH4_GPIO, PIN_MODE_1, PIN_PULL_TYPE_DOWN, PIN_DS_4, GPIO_DIRECTION_OUTPUT, "SWITCH4"}, // 物理按键 - 输入 - {KEY1_GPIO, GPIO_DIRECTION_INPUT, "KEY1"}, - {KEY2_GPIO, GPIO_DIRECTION_INPUT, "KEY2"}, - {KEY3_GPIO, GPIO_DIRECTION_INPUT, "KEY3"}, - {KEY4_GPIO, GPIO_DIRECTION_INPUT, "KEY4"}, + {KEY1_GPIO, PIN_MODE_0, PIN_PULL_TYPE_STRONG_UP, PIN_DS_3, GPIO_DIRECTION_INPUT, "KEY1"}, + {KEY2_GPIO, PIN_MODE_0, PIN_PULL_TYPE_STRONG_UP, PIN_DS_3, GPIO_DIRECTION_INPUT, "KEY2"}, + {KEY3_GPIO, PIN_MODE_0, PIN_PULL_TYPE_STRONG_UP, PIN_DS_3, GPIO_DIRECTION_INPUT, "KEY3"}, + {KEY4_GPIO, PIN_MODE_0, PIN_PULL_TYPE_STRONG_UP, PIN_DS_3, GPIO_DIRECTION_INPUT, "KEY4"}, // 面板背光 - 输出 - {PANEL_LED_GPIO, GPIO_DIRECTION_OUTPUT, "PANEL_LED"}, + {PANEL_LED_GPIO, PIN_MODE_1, PIN_PULL_TYPE_DISABLE, PIN_DS_7, GPIO_DIRECTION_OUTPUT, "PANEL_LED"}, // LED指示灯 - 输出 - {LED1_GPIO, GPIO_DIRECTION_OUTPUT, "LED1"}, - {LED2_GPIO, GPIO_DIRECTION_OUTPUT, "LED2"}, - {LED3_GPIO, GPIO_DIRECTION_OUTPUT, "LED3"}, - // {LED4_GPIO, GPIO_DIRECTION_OUTPUT, "LED4"}, + {LED1_GPIO, PIN_MODE_1, PIN_PULL_TYPE_DISABLE, PIN_DS_7, GPIO_DIRECTION_OUTPUT, "LED1"}, + {LED2_GPIO, PIN_MODE_1, PIN_PULL_TYPE_DISABLE, PIN_DS_7, GPIO_DIRECTION_OUTPUT, "LED2"}, + {LED3_GPIO, PIN_MODE_1, PIN_PULL_TYPE_DISABLE, PIN_DS_7, GPIO_DIRECTION_OUTPUT, "LED3"}, + {LED4_GPIO, PIN_MODE_1, PIN_PULL_TYPE_DISABLE, PIN_DS_7, GPIO_DIRECTION_OUTPUT, "LED4"}, }; @@ -260,24 +659,34 @@ int switch_panel_gpio_init(void) { // 初始化pinctrl和GPIO uapi_pin_init(); - // uapi_gpio_init(); + uapi_gpio_init(); // 使用循环配置所有GPIO for (uint32_t i = 0; i < GPIO_CONFIG_COUNT; i++) { const gpio_config_t* config = &gpio_configs[i]; // 设置为GPIO模式 - ret = uapi_pin_set_mode(config->pin, PIN_MODE_0); - if (ret != HF_SUCCESS) { + ret = uapi_pin_set_mode(config->pin, config->mode); + if (ret != 0) { e_printf("[GPIO] %s pinctrl初始化失败: %d\r\n", config->name, ret); - return ret; + return HF_FAIL; + } + ret = uapi_pin_set_pull(config->pin, config->pull); + if (ret != 0) { + e_printf("[GPIO] %s pinctrl初始化失败: %d\r\n", config->name, ret); + return HF_FAIL; + } + ret = uapi_pin_set_ds(config->pin, config->ds); + if (ret != 0) { + e_printf("[GPIO] %s pinctrl初始化失败: %d\r\n", config->name, ret); + return HF_FAIL; } // 设置GPIO方向 ret = uapi_gpio_set_dir(config->pin, config->direction); - if (ret != HF_SUCCESS) { + if (ret != 0) { e_printf("[GPIO] %s 设置方向失败: %d\r\n", config->name, ret); - return ret; + return HF_FAIL; } e_printf("[GPIO] %s 初始化完成 (方向: %s)\r\n", @@ -361,16 +770,16 @@ bool get_key_input(int key_id) { //====================== 设备状态同步函数 ====================== -// 同步硬件状态与软件状态 +// 同步硬件状态与持久化状态 void sync_hardware_state(void) { // 同步所有开关状态 for (int i = 0; i < SWITCH_COUNT; i++) { - set_switch_output(i, g_device_state.switches[i].switch_on); - set_led_output(i, g_device_state.switches[i].led_state ? LED_WHITE : LED_YELLOW); + set_switch_output(i, g_persistent_state.switches[i].switch_on); + set_led_output(i, g_persistent_state.switches[i].led_state ? LED_WHITE : LED_YELLOW); } // 同步面板背光状态 - set_panel_led(g_device_state.panel_led ? PANEL_LED_ON : PANEL_LED_OFF); + set_panel_led(g_persistent_state.panel_led ? PANEL_LED_ON : PANEL_LED_OFF); e_printf("[STATE] 硬件状态已同步\r\n"); } @@ -381,32 +790,50 @@ int switch_panel_main(void) { int ret = HF_SUCCESS; if (g_initialized) { - e_printf("[MAIN] 开关面板已初始化\r\n"); + e_printf("开关面板已初始化\r\n"); return HF_SUCCESS; } g_initialized = true; - e_printf("[MAIN] 开始初始化SORONTEK智能面板...\r\n"); + e_printf("开始初始化SORONTEK智能面板...\r\n"); - // 加载设备状态 - ret = load_device_state(); + // 初始化运行时状态 + init_runtime_state(); + + // 初始化异步保存系统 + ret = save_system_init(); if (ret != HF_SUCCESS) { - e_printf("[MAIN] 加载设备状态失败: %d\r\n", ret); + e_printf("异步保存系统初始化失败: %d\r\n", ret); return ret; } - // 检查是否首次启动,如果是则标记为非首次 - bool first_boot = g_device_state.is_first_boot; - if (g_device_state.is_first_boot) { - g_device_state.is_first_boot = false; // 标记为非首次启动 - e_printf("[MAIN] 检测到首次启动\r\n"); + + // 初始化异步上报系统 + ret = report_system_init(); + if (ret != HF_SUCCESS) { + e_printf("异步上报系统初始化失败: %d\r\n", ret); + // 上报系统失败不影响主流程,继续运行 } + + // 加载持久化状态 + ret = load_persistent_state(); + if (ret != HF_SUCCESS) { + e_printf("加载持久化状态失败: %d\r\n", ret); + return ret; + } + + // 检查是否首次启动 + bool first_boot = g_persistent_state.is_first_boot; + + e_printf("设备状态 - 首次启动: %s, 绑定状态: %s\r\n", + first_boot ? "是" : "否", + g_persistent_state.is_bound ? "已绑定" : "未绑定"); // 初始化GPIO ret = switch_panel_gpio_init(); if (ret != HF_SUCCESS) { - e_printf("[MAIN] GPIO初始化失败: %d\r\n", ret); + e_printf("GPIO初始化失败: %d\r\n", ret); return ret; } if(key_system_init() != HF_SUCCESS) { - e_printf("[MAIN] 按键系统初始化失败: %d\r\n", ret); + e_printf("按键系统初始化失败: %d\r\n", ret); return ret; } @@ -414,24 +841,27 @@ int switch_panel_main(void) { sync_hardware_state(); // 保存状态更新 - save_device_state(); + save_persistent_state(); // 配网逻辑: // 1. 如果设备未绑定 且 是第一次启动 -> 直接进入配网 // 2. 如果设备未绑定 且 不是第一次启动 -> 等待按键触发配网 - if (!g_device_state.is_bound) { + if (!g_persistent_state.is_bound) { if (first_boot) { - e_printf("[MAIN] 首次启动且未绑定,直接进入配网模式\r\n"); + e_printf("首次启动且未绑定,直接进入配网模式\r\n"); enter_config_mode(); } else { - e_printf("[MAIN] 设备未绑定,等待按键触发配网\r\n"); + e_printf("设备未绑定,等待按键触发配网\r\n"); // 配网逻辑将在按键处理函数中实现 } } else { - e_printf("[MAIN] 设备已绑定,正常运行\r\n"); + e_printf("设备已绑定,正常运行\r\n"); + // 设备已绑定时禁用蓝牙模式,使用云端控制 + extern void switch_panel_ble_disable(void); + switch_panel_ble_disable(); } - e_printf("[MAIN] SORONTEK智能面板初始化完成\r\n"); + e_printf("SORONTEK智能面板初始化完成\r\n"); print_device_state(); return HF_SUCCESS; @@ -441,17 +871,18 @@ int switch_panel_main(void) { void print_device_state(void) { e_printf("\r\n===== 设备状态信息 =====\r\n"); - e_printf("总开关: %s\r\n", g_device_state.master_switch ? "开" : "关"); - e_printf("面板背光: %s\r\n", g_device_state.panel_led ? "开" : "关"); - e_printf("绑定状态: %s\r\n", g_device_state.is_bound ? "已绑定" : "未绑定"); - e_printf("首次启动: %s\r\n", g_device_state.is_first_boot ? "是" : "否"); - e_printf("工作模式: %s\r\n", get_mode_string(g_device_state.mode)); + e_printf("总开关: %s\r\n", g_persistent_state.master_switch ? "开" : "关"); + e_printf("面板背光: %s\r\n", g_persistent_state.panel_led ? "开" : "关"); + e_printf("绑定状态: %s\r\n", g_persistent_state.is_bound ? "已绑定" : "未绑定"); + e_printf("首次启动: %s\r\n", g_persistent_state.is_first_boot ? "是" : "否"); + e_printf("工作模式: %s\r\n", get_mode_string(g_runtime_state.mode)); for (int i = 0; i < SWITCH_COUNT; i++) { - e_printf("开关%d: %s, LED: %s\r\n", + e_printf("开关%d(%s): %s, LED: %s\r\n", i + 1, - g_device_state.switches[i].switch_on ? "开" : "关", - g_device_state.switches[i].led_state ? "白灯" : "黄灯"); + g_persistent_state.switches[i].name, + g_persistent_state.switches[i].switch_on ? "开" : "关", + g_persistent_state.switches[i].led_state ? "白灯" : "黄灯"); } e_printf("=======================\r\n\r\n"); } @@ -464,4 +895,115 @@ const char* get_mode_string(system_mode_t mode) { case MODE_UNBIND: return "解绑模式"; default: return "未知模式"; } +} + +//====================== 开关名字操作函数 ====================== + +// 获取开关名字 +const char* get_switch_name(int switch_id) { + if (switch_id < 0 || switch_id >= SWITCH_COUNT) { + return ""; + } + return g_persistent_state.switches[switch_id].name; +} + +// 设置开关名字 +int set_switch_name(int switch_id, const char* name) { + if (switch_id < 0 || switch_id >= SWITCH_COUNT || !name) { + e_printf("无效的开关ID或名字参数: switch_id=%d\r\n", switch_id); + return HF_FAIL; + } + + // 使用互斥锁保护状态访问 + if (g_save_mutex_initialized && osal_mutex_lock(&g_save_mutex) != OSAL_SUCCESS) { + e_printf("获取互斥锁失败\r\n"); + return HF_FAIL; + } + + // 检查名字是否相同,相同则跳过保存 + if (strcmp(g_persistent_state.switches[switch_id].name, name) == 0) { + if (g_save_mutex_initialized) { + osal_mutex_unlock(&g_save_mutex); + } + e_printf("开关%d名字未变化,跳过保存: %s\r\n", switch_id + 1, name); + return HF_SUCCESS; + } + + // 复制名字,确保不会溢出 + int ret = strncpy_s(g_persistent_state.switches[switch_id].name, + SWITCH_NAME_MAX_LEN, + name, + SWITCH_NAME_MAX_LEN - 1); + + if (ret != 0) { + e_printf("开关%d名字复制失败: %d\r\n", switch_id + 1, ret); + if (g_save_mutex_initialized) { + osal_mutex_unlock(&g_save_mutex); + } + return HF_FAIL; + } + + // 确保字符串以NULL结尾 + g_persistent_state.switches[switch_id].name[SWITCH_NAME_MAX_LEN - 1] = '\0'; + + if (g_save_mutex_initialized) { + osal_mutex_unlock(&g_save_mutex); + } + + e_printf("开关%d名字已更新为: %s\r\n", + switch_id + 1, g_persistent_state.switches[switch_id].name); + + // 异步保存状态 + save_persistent_state(); + + return HF_SUCCESS; +} + +// 初始化默认开关名字 +void init_default_switch_names(void) { + for (int i = 0; i < SWITCH_COUNT; i++) { + snprintf(g_persistent_state.switches[i].name, SWITCH_NAME_MAX_LEN, "开关%d", i + 1); + } + e_printf("默认开关名字已初始化\r\n"); +} + +//====================== 状态访问便利函数 ====================== + +// 获取开关状态 +bool get_switch_state(int switch_id) { + if (switch_id < 0 || switch_id >= SWITCH_COUNT) { + return false; + } + return g_persistent_state.switches[switch_id].switch_on; +} + +// 获取总开关状态 +bool get_master_switch_state(void) { + return g_persistent_state.master_switch; +} + +// 获取设备绑定状态 +bool is_device_bound(void) { + return g_persistent_state.is_bound; +} + +// 获取是否首次启动 +bool is_first_boot(void) { + return g_persistent_state.is_first_boot; +} + +// 获取当前工作模式 +system_mode_t get_device_mode(void) { + return g_runtime_state.mode; +} + +// 设置设备工作模式 +void set_device_mode(system_mode_t mode) { + if (g_runtime_state.mode != mode) { + system_mode_t old_mode = g_runtime_state.mode; + g_runtime_state.mode = mode; + + e_printf("[MODE] 设备模式切换: %s -> %s\r\n", + get_mode_string(old_mode), get_mode_string(mode)); + } } \ No newline at end of file diff --git a/build/config/target_config/ws63/config.py b/build/config/target_config/ws63/config.py index 60a7b59..fda05e1 100755 --- a/build/config/target_config/ws63/config.py +++ b/build/config/target_config/ws63/config.py @@ -287,7 +287,7 @@ target = { }, 'ws63-liteos-app-iot': { 'base_target_name': 'target_ws63_app_rom_template', - # 'liteos_kconfig': 'ws63_iot', + 'liteos_kconfig': 'ws63_iot', # EKKO add for remove indie upgrade 'os': 'liteos', 'defines': [ "USE_CMSIS_OS", @@ -330,7 +330,7 @@ target = { "SUPPORT_SOFTAP_NETCFG", # softAP配网 "SUPPORT_BLE_ANCILLAY_NETCFG", # ble辅助配网 # "SUPPORT_QUICK_NETCFG", # 快速配网 - "CONFIG_SUPPORT_HILINK_INDIE_UPGRADE", + # "CONFIG_SUPPORT_HILINK_INDIE_UPGRADE", # EKKO remove indie upgrade "CONFIG_DHCPS_GW", "_HSF_", # "ENABLE_BLE_SCAN" #open ble scan @@ -375,10 +375,10 @@ target = { 'cjson', 'xo_trim_port', 'hilink', - 'app_addr_map', - # 'hilinkdevicesdk', - # 'hilinkota', - # 'hilinkbtsdk', + # 'app_addr_map', # ekko remove for remove indie upgrade + 'hilinkdevicesdk', # ekko add for remove indie upgrade + 'hilinkota', # ekko add for remove indie upgrade + 'hilinkbtsdk', # ekko add for remove indie upgrade 'huks_sdk', 'deviceauth', 'little_fs', 'littlefs_adapt_ws63', diff --git a/indie_build.py b/indie_build.py index 1e065f6..5a44cc3 100755 --- a/indie_build.py +++ b/indie_build.py @@ -125,6 +125,10 @@ def all_build(): build_update_package(os.path.join(info.upg_output, "update.fwpkg"), temp_dir, info.upg_output) + print("gen package.zip") + file_dir = os.path.dirname(os.path.abspath(__file__)) + os.system("cd " + file_dir + " && python3 package.py") + fwpkg_src_file = os.path.join(info.output, "fwpkg", "ws63-liteos-app-iot", "ws63-liteos-app-iot_all.fwpkg") copy_files = { info.hilink_bin: temp_dir, diff --git a/readme.md b/readme.md index e5ea931..93a5ed9 100644 --- a/readme.md +++ b/readme.md @@ -34,6 +34,7 @@ application/samples/wifi/ohos_connect/hilink_adapt/product/ 1. 其物理模型2Q4G.json,但是里面的netInfo timer update这几个是hilink内部已经实现无需我们关心,我们只需要关心switch3、switch2、switch1、switch4 switch这几个物理模型。 1. switch 是整个面板的总开关,其是属于一个软件上的控制,只有当其属于打开状态时其他的开关才允许控制 2. switch1->switch4 是对应四个物理开关,每个switch可以控制对应的一个开关。 + 3. 设备的状态是开是数值1,关闭是数值0. ## 物理连接相关定义及其中一些信息进行补充: GPOP00 <--> LED1 @@ -56,46 +57,62 @@ LED 是整个控制面板的背光灯,高电平亮灯(黄色),低电平灭 ## 需求分析 -### 设备的一些信息需要存储在flash中 +### 基本功能需求: +1. 设备的一些信息需要存储在flash中 1. 设备的每个按键的开关状态 2. 设备的背光灯状态 3. 设备的总开关状态 4. 设备的配网状态 -### 物理按键和APP按键控制要统一 -设备可以通过APP进行控制,也可以通过物理按键进行控制,通过物理按键进行控制时也需要存储按键的开关状态APP控制时也需要存储按键的开关状态。这样二者的信息能够统一同步 +2. 物理按键和APP按键控制要统一 + 1. 设备可以通过APP进行控制,也可以通过物理按键进行控制,通过物理按键进行控制时也需要存储按键的开关状态APP控制时也需要存储按键的开关状态。这样二者的信息能够统一同步 -### 每个按键的的指示灯和开关状态需要同步,当按键按下时,指示灯亮,当按键松开时,指示灯需要保持长亮。 -### 你需要在HILINK_NotifyDevStatus对应的 HILINK_M2M_CLOUD_ONLINE 和 HILINK_M2M_CLOUD_OFFLINE 中添加对应的逻辑,当设备上线时,需要将按键的开关状态和指示灯状态进行同步,当设备下线时,需要将按键的开关状态和指示灯状态进行同步。 +3. 每个按键的的指示灯和开关状态需要同步,当按键按下时,指示灯亮,当按键松开时,指示灯需要保持长亮。 +4. 你需要在HILINK_NotifyDevStatus对应的 HILINK_M2M_CLOUD_ONLINE 和 HILINK_M2M_CLOUD_OFFLINE 中添加对应的逻辑,当设备上线时,需要将按键的开关状态和指示灯状态进行同步,当设备下线时,需要将按键的开关状态和指示灯状态进行同步。 -### 你需要在 HILINK_DEVICE_UNREGISTER 中添加对应的逻辑,当设备被删除时,需要将按键的开关状态和指示灯状态进行同步。 -### 设备的按键状态的修改请立即进行保存,避免突然断电导致状态丢失。 -### 设备上线后才能立即进行同步所有物模型信息,避免APP界面异常 +5. 你需要在 HILINK_DEVICE_UNREGISTER 中添加对应的逻辑,当设备被删除时,需要将按键的开关状态和指示灯状态进行同步。 +6. 设备的按键状态的修改请立即进行保存,避免突然断电导致状态丢失。 +7. 设备上线后才能立即进行同步所有物模型信息,避免同步失败导致APP界面异常 +8. 需要支持离线后的本地蓝牙控制。 + +### 产测需求: +1. 只有处于出厂状态,从未被绑定才会触发产测 +2. 主动扫描识别固定的热点名 ShuorongSelfTest 作为进入产测的信号,5秒未搜索扫指定名字退出产测 + => 搜索识别指定wifi热点名称是后台的行为,其他诸如配网逻辑保持不变。所以这里建议单开一个线程实现 + 产测获取wifi信息相关代码可以参考 /home/ekko.bao/work/hilink-hf_lpt26x/SR_Light_Hilink_14.2.1.312_20250704/application/ws63/user_main/spotlight/factory_test.c wifi_scan_and_check函数的实现。 +3. 测试流程:开1-开2-开3-开4-全关-全开-全关,每1.5秒执行一个动作 +4. 需要校验WIFI 信号强度 -70 + + +### SORONTEK智能面板配网: +配网有两种触发条件: +1. **自动配网**: 设备处于出厂状态(`is_first_boot = true`)时,上电自动进入配网 +2. **手动配网**: 设备处于未绑定状态时,长按左上角第一个按键10秒进入配网 +3. 配网过程中,面板背光灯快闪1秒,表示开关已进入配网状态 +4. 配网中途只有被按下的按键的指示灯进行闪烁,固定1HZ闪烁,其他按键指示灯保持长亮 +5. 配网超时10分钟,超时未配网按键指示灯恢复长亮 + =》前三分钟指示灯保持闪烁,后七分钟指示灯保持长亮。 ## 软件编写规范 1. 实现需求需要在application/ws63/user_main另外新建文件夹(命名你根据产品需求定义)实现相关逻辑 -2. flush操作的api可见 application/ws63/hsf/hfflash.h 里面定义的 HSF_API 修饰的api -3. gpio其相关api可见 include/driver/pinctrl.h 和 include/driver/gpio.h。 - 其中 PIN_MODE_0 为输入模式,PIN_MODE_1输出模式 - GPIO口枚举可见drivers/chips/ws63/include/platform_core_rom.h -5. 延时使用msleep -6. 定时器使用 uapi_timer_xx 头文件定义在-> include/driver/timer.h -7. 线程相关使用 osal_kthread_xx 头文件定义在-> kernel/osal/include/schedule/osal_task.h +2. hf的接口提供了多套api架构, + 1. include/driver 里面存放的是更底层的api 是hf开头的api。 + 2. application/ws63/hsf 里面存放的高一层级的api。是uapi开头的api + 二者均可提供服务但是请优先使用 application/ws63/hsf 里面的接口 + 如: + 1. flush操作的api可见 application/ws63/hsf/hfflash.h 里面定义的 HSF_API 修饰的api + 但是有一些例外: + 1. gpio 其相关api可见 include/driver/pinctrl.h 和 include/driver/gpio.h。 + 其中 PIN_MODE_0 为输入模式,PIN_MODE_1输出模式 + GPIO口枚举可见drivers/chips/ws63/include/platform_core_rom.h + 2. 定时器使用 uapi_timer_xx 头文件定义在-> include/driver/timer.h +3. 离线本地蓝牙上报和接收接口可见: + 1. 处理接受的数据 application/samples/wifi/ohos_connect/hilink_adapt/entry/hilink_ble_main.c => BleHandleCustomData + 2. 上报数据 application/samples/wifi/ohos_connect/hilink_adapt/include/ble_cfg_net_api.h => BLE_SendCustomData + 使用示例可见application/samples/wifi/ohos_connect/hilink_adapt/entry/hilink_ble_main.c => ReporSwitchStatus + 实现考虑和云端的合二为一,复用上报的代码:在 fast_report 函数里面根据当前处于的模式(离线蓝牙控制模式还是云端网络控制)选择走本地上报还是云端上报。 + -### SORONTEK智能面板产测: -1. 由信标发送产测信号 - =》识别固定的热点名 ShuorongSelfTest 作为进入产测的信号 -2. 开关循环:开1-开2-开3-开4-全关-全开-全关 -3. 每1.5秒执行一个动作 -4. 需要校验WIFI 信号强度 -70 - -### SORONTEK智能面板配网: -1. 只有设备处于未绑定状态才能进行配网(出厂或者被APP删除) - =》 设备处于出厂状态需要立刻进入配网。 -2. 长按开关左上角第一个按键10秒, 进入配网,面板背光灯快闪1秒,表示开关已进入配网状态 -3. 配网中途只有被按下的按键的指示灯进行闪烁,固定1HZ闪烁,其他按键指示灯保持长亮 -4. 配网超时10分钟,超时未配网按键指示灯恢复长亮 - =》前三分钟指示灯保持闪烁,后七分钟指示灯保持长亮。 ## 其他补充信息 ### 出厂/复位 SORONTEK智能面板默认状态: @@ -108,5 +125,104 @@ LED 是整个控制面板的背光灯,高电平亮灯(黄色),低电平灭 上报使用 fast_report函数,传入对应的svc_id子串即可 +## 编译和打包命令 + +### 基本编译命令 +```bash +# 清除构建并编译主程序 +python3 build.py -c ws63-liteos-hilink + +# 并行编译(推荐) +python3 build.py -j8 ws63-liteos-hilink + +# 独立升级编译(包含两个目标) +python3 indie_build.py sdk # 仅编译SDK +python3 indie_build.py all # 编译所有组件 + +# 生成升级包 +python3 package.py +``` + +### 编译输出目录 +- `output/LPT262_hilink.fwpkg` - 主固件包 +- `output/LPT262_hilink_UPGRADE.bin` - 升级固件 +- `output/package.zip` - OTA升级包 + +### 调试编译选项 +```bash +# 添加编译宏 +python3 build.py -def=DEBUG_SWITCH_PANEL,LOG_LEVEL=3 ws63-liteos-hilink + +# 仅编译指定组件 +python3 build.py -component=user_main ws63-liteos-hilink + +# 使用ninja生成器(更快) +python3 build.py -ninja ws63-liteos-hilink +``` + +## 关键API参考 + +### GPIO控制 +```c +// 引用头文件 +#include "driver/gpio.h" +#include "driver/pinctrl.h" + +// GPIO初始化 +uapi_pin_set_mode(GPIO_00, PIN_MODE_1); // 输出模式 +uapi_gpio_set_dir(GPIO_00, GPIO_DIRECTION_OUTPUT); + +// GPIO操作 +uapi_gpio_set_val(GPIO_00, GPIO_LEVEL_HIGH); +uapi_gpio_get_val(GPIO_00, &level); +``` + +### Flash存储 +```c +// 引用头文件 +#include "application/ws63/hsf/hfflash.h" + +// Flash操作 +HSF_WriteFlash(offset, data, len); +HSF_ReadFlash(offset, data, len); +``` + +### 定时器 +```c +// 引用头文件 +#include "driver/timer.h" + +// 定时器操作 +uapi_timer_create(&timer_id); +uapi_timer_start(timer_id, timeout, callback); +``` + +### 延时操作 +```c +msleep(1000); // 延时1秒 +``` + +## 设备状态管理要点 + +### 设备绑定状态管理 +- **`is_first_boot` 标志**: 表示设备从未被绑定的状态(可理解为 `not_first_bind`) + - 出厂状态:`is_first_boot = true` (从未被绑定) + - 首次绑定后:`is_first_boot = false` (永久设置,不可逆) + - 设备删除后:`is_first_boot` 保持 `false` (不影响该标志) +- **未绑定状态**: 设备当前没有绑定到任何用户账户(包括出厂状态和被删除后的状态) +- **产测触发条件**: 只有 `is_first_boot = true` 时才会触发产测流程 +- **配网触发条件**: + - 出厂状态(`is_first_boot = true`):上电自动进入配网 + - 未绑定状态:长按左上角第一个按键10秒可进入配网 + +### 设备状态同步要点 +1. **云端上报入口**: `fast_report(svc_id)` - 自动处理蓝牙/云端模式 +2. **云端控制入口**: `application/samples/wifi/ohos_connect/hilink_adapt/product/hilink_device.c` 中的 `handle_put_xxx` 函数 +3. **状态存储**: 所有设备状态变化必须立即调用Flash写入操作 +4. **设备事件处理**: 在 `HILINK_NotifyDevStatus` 中处理设备上线/下线/删除事件 + - `HILINK_M2M_CLOUD_ONLINE`: 设备上线时同步所有状态 + - `HILINK_M2M_CLOUD_OFFLINE`: 设备下线时保持本地状态 + - `HILINK_DEVICE_UNREGISTER`: 设备删除时不影响 `is_first_boot` 标志 + 现在需要请你帮我实现一个四开关面板程序功能 请先分析需求,拆解步骤,然后逐步进行编码。 \ No newline at end of file