From f915eb634a22600ff757fbffaef7288956b4cd67 Mon Sep 17 00:00:00 2001 From: "ekko.bao" Date: Sat, 24 May 2025 15:32:33 +0800 Subject: [PATCH] =?UTF-8?q?=E5=9F=BA=E6=9C=AC=E5=AE=8C=E6=88=90=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=EF=BC=8C=E5=8E=BB=E9=99=A4=E4=BA=86=E7=8B=AC=E7=AB=8B?= =?UTF-8?q?=E5=8D=87=E7=BA=A7=E5=8A=9F=E8=83=BD=E7=9A=84=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../samples/wifi/ohos_connect/CMakeLists.txt | 3 + .../hilink_adapt/entry/hilink_ble_main.c | 58 +- .../hilink_adapt/entry/hilink_wifi_main.c | 9 +- .../hilink_adapt/product/device_profile.h | 44 +- .../hilink_adapt/product/hilink_device.c | 70 +- application/ws63/hsf/hfuart.h | 13 +- application/ws63/user_main/CMakeLists.txt | 7 + application/ws63/user_main/app_main.c | 25 +- .../ws63/user_main/spotlight/device_module.c | 324 +++- .../ws63/user_main/spotlight/spotlight.h | 160 ++ .../ws63/user_main/spotlight/spotlight_main.c | 1314 +++++++++++++++-- .../ws63/user_main/spotlight/spotlight_ut.c | 116 ++ build/config/target_config/ws63/config.py | 15 +- .../acore/ws63_liteos_app_iot.config | 1 + compile_commands.json | 1 + indie_build.py | 3 + package.py | 61 + 17 files changed, 2031 insertions(+), 193 deletions(-) create mode 100644 application/ws63/user_main/spotlight/spotlight_ut.c mode change 100755 => 100644 build/config/target_config/ws63/menuconfig/acore/ws63_liteos_app_iot.config create mode 120000 compile_commands.json create mode 100755 package.py diff --git a/application/samples/wifi/ohos_connect/CMakeLists.txt b/application/samples/wifi/ohos_connect/CMakeLists.txt index 2842656..7d42850 100755 --- a/application/samples/wifi/ohos_connect/CMakeLists.txt +++ b/application/samples/wifi/ohos_connect/CMakeLists.txt @@ -66,6 +66,8 @@ set(SOURCES ${CMAKE_HILINK_SOURCE_DIR}/hilink_adapt/adapter/hilink_thread_adapter.c ${CMAKE_HILINK_SOURCE_DIR}/hilink_adapt/adapter/hilink_ble_adapter.c ${CMAKE_HILINK_SOURCE_DIR}/hilink_adapt/entry/hilink_entry.c + # ${CMAKE_HILINK_SOURCE_DIR}/hilink_adapt/product/spotlight/spotlight_main.c + # ${CMAKE_HILINK_SOURCE_DIR}/hilink_adapt/product/spotlight/device_module.c ) if (DEFINES MATCHES "SUPPORT_MINIMALIST_NETCFG" OR DEFINES MATCHES "SUPPORT_BLE_ANCILLAY_NETCFG") @@ -85,6 +87,7 @@ set(PUBLIC_HEADER ${CMAKE_HILINK_SOURCE_DIR}/hilink_adapt/include/ ${CMAKE_HILINK_SOURCE_DIR}/hilink_adapt/product/ ${CMAKE_HILINK_SOURCE_DIR}/hilink_adapt/adapter/include/ + # ${CMAKE_HILINK_SOURCE_DIR}/hilink_adapt/product/spotlight ) set(PRIVATE_HEADER 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 19e678d..b7f981d 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,7 +42,7 @@ //static bool g_switch = 0; int sid_switch; static bool g_autoUpdate = 0; - +static int ble_adv_time = BLE_ADV_TIME; static HILINK_BT_DevInfo g_btDevInfo = {0}; extern int set_get_ble_mac(void); extern int HILINK_Printf(const char *format, ...); @@ -275,7 +275,7 @@ static void HILINK_BT_StateChangeHandler(HILINK_BT_SdkStatus event, const void * BLE_SetAdvType(BLE_ADV_LOCAL_NAME); /* BLE配网广播控制:参数代表广播时间,0:停止;0xFFFFFFFF:一直广播,其他:广播指定时间后停止,单位秒 */ - (void)BLE_CfgNetAdvCtrl(BLE_ADV_TIME); + (void)BLE_CfgNetAdvCtrl(ble_adv_time); } } @@ -431,11 +431,12 @@ unsigned int GetWifiRecoveryType(void) return (0x01 | 0x02); } -int hilink_ble_main(void) +int start_hilink_ble_net_config(int32_t net_cfg_time_s) { int ret = 0; - int hilink_entry_mode=hfget_hilink_mode(); - printf("hilink_entry_mode:%d\r\n",hilink_entry_mode); + int hilink_entry_mode=hfget_hilink_mode(); + ble_adv_time = net_cfg_time_s; + printf("hilink_entry_mode:%d, ble_adv_time:%d\r\n",hilink_entry_mode, ble_adv_time); if(hilink_entry_mode != SMTLK_SOFTAP) { if(hfget_hilink_mode() == SMTLK_BLE_NARMAL) @@ -452,6 +453,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); @@ -480,6 +482,7 @@ int hilink_ble_main(void) 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) @@ -487,19 +490,7 @@ int hilink_ble_main(void) HILINK_SetProtType(1); ret = HILINK_SetNetConfigMode(HILINK_NETCONFIG_WIFI); } - - - /* 修改任务属性 */ - HILINK_SdkAttr *sdkAttr = HILINK_GetSdkAttr(); - if (sdkAttr == NULL) { - HILINK_SAL_NOTICE("sdkAttr is null"); - return -1; - } - sdkAttr->monitorTaskStackSize = 0x600; /* 示例代码 推荐栈大小为0x400 */ - sdkAttr->rebootHardware = HardReboot; - sdkAttr->rebootSoftware = HardReboot; - HILINK_SetSdkAttr(*sdkAttr); - + /* 注册极简配网WIFI相关函数 */ WiFiRecoveryApi wifiCb; wifiCb.getWifiRecoveryType = GetWifiRecoveryType; @@ -514,6 +505,23 @@ int hilink_ble_main(void) } HILINK_SAL_NOTICE("RegWiFiRecoveryCallback finish!\n"); + return ret; +} +int hilink_ble_main(void) +{ + hfdbg_set_level(1); + hfset_hilink_mode(SMTLK_BLE_FAST_CONNECT); + /* 修改任务属性 */ + HILINK_SdkAttr *sdkAttr = HILINK_GetSdkAttr(); + if (sdkAttr == NULL) { + HILINK_SAL_NOTICE("sdkAttr is null"); + return -1; + } + sdkAttr->monitorTaskStackSize = 0x600; /* 示例代码 推荐栈大小为0x400 */ + sdkAttr->rebootHardware = HardReboot; + sdkAttr->rebootSoftware = HardReboot; + HILINK_SetSdkAttr(*sdkAttr); + #ifdef CONFIG_SUPPORT_HILINK_INDIE_UPGRADE if (HILINK_RegisterErrnoCallback(get_os_errno) != 0) { HILINK_SAL_NOTICE("reg errno cb err\r\n"); @@ -526,6 +534,18 @@ 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(); + extern int spotlight_main(void); + spotlight_main(); 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/entry/hilink_wifi_main.c b/application/samples/wifi/ohos_connect/hilink_adapt/entry/hilink_wifi_main.c index afb207f..420d95a 100755 --- a/application/samples/wifi/ohos_connect/hilink_adapt/entry/hilink_wifi_main.c +++ b/application/samples/wifi/ohos_connect/hilink_adapt/entry/hilink_wifi_main.c @@ -12,16 +12,19 @@ #include "hal_reboot.h" #include "hilink_device.h" #include "hilink_network_adapter.h" +#include "hsf.h" #ifdef SUPPORT_QUICK_NETCFG #include "hilink_quick_netcfg_demo.h" #include "hilink_quick_netcfg_api.h" #endif - +extern unsigned char *HILINK_GetAutoAc(void); static int GetAcV2Func(unsigned char *acKey, unsigned int acLen) { /* key文件在开发者平台获取 */ - return -1; + e_printf("GetAcV2Func\r\n"); + memcpy(acKey, HILINK_GetAutoAc(), acLen); + return 0; } static unsigned int GetWifiRecType(void) @@ -84,7 +87,7 @@ int hilink_wifi_main(void) HILINK_SAL_NOTICE("HILINK_Main start error"); return -1; } - + e_printf("HILINK_Main start success\r\n"); #ifdef SUPPORT_QUICK_NETCFG StartQuickNetCfg(); #endif 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 497c47a..661ca11 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 @@ -16,7 +16,39 @@ extern "C" { * 与hilink sdk相同定义,双模组模式只需一份,已提供给第三方厂家,暂不按编程规范整改 */ - +#define SORONTEK_PRODUCT 1 +#define HIFLYING_PRODUCT 0 +#if SORONTEK_PRODUCT // EKKO add for device info, Reference from product json +#define ProductId "2Q4H" +#define deviceTypeId "21D" +#define manufacturerID "gub" +#define deviceModel "SR-TH-020-10W" +#define configName "SR_" +#define configType "light" +#define enterpriseEnglishName "SORONTEK" // EKKO: TODO: need check +#define brandEn "SORONTEK" +#define deviceName "LED嵌入式天花射灯" +#define productSeries "" +#define MANUAFACTURER_ACKEY {\ + 0x66, 0x22, 0x33, 0x5F, 0x75, 0x31, 0x29, 0x6A, 0x27, 0x34, 0x57, 0x44, 0x32, 0x42, 0x27, 0x59, \ + 0xE1, 0xDE, 0x9C, 0xA6, 0xA6, 0x77, 0x54, 0x8C, 0xA9, 0xB0, 0x0D, 0xC7, 0xCB, 0x1B, 0x32, 0x2B, \ + 0xB9, 0x4E, 0xEE, 0x97, 0xDC, 0x43, 0xFB, 0xF1, 0x86, 0xCC, 0xFD, 0x6E, 0x78, 0xA8, 0x36, 0x22} +#elif HIFLYING_PRODUCT +#define ProductId "2PAD" +#define deviceTypeId "01D" +#define manufacturerID "01C" +#define deviceModel "hf-262-jjpw" +#define configName "262" +#define configType "HF" +#define enterpriseEnglishName "HighFlying" +#define brandEn "hiflying" +#define deviceName "专用262认证测试设备" +#define productSeries "262" +#define MANUAFACTURER_ACKEY { \ + 0x73,0x4A,0x68,0x4E,0x3B,0x77,0x2A,0x42,0x33,0x74,0x6D,0x50,0x3C,0x6F,0x2E,0x60,0x40,0xCA, \ + 0x74,0x27,0x25,0xB7,0x1A,0x72,0x41,0xCD,0xAB,0xD4,0x04,0x55,0x7D,0xC4,0x2B,0x32,0x7E,0xB2, \ + 0x39,0xB1,0xDC,0x28,0x46,0xBB,0x6A,0x77,0x86,0x3D,0xD8,0xE3 } +#else #define ProductId "2PQP" #define deviceTypeId "112" #define manufacturerID "i0s" @@ -27,10 +59,14 @@ extern "C" { #define brandEn "YX" #define deviceName "SamSLETest" #define productSeries "" - +#define MANUAFACTURER_ACKEY { \ + 0x66, 0x3F, 0x64, 0x78, 0x4B, 0x78, 0x3B, 0x26, 0x76, 0x78, 0x24, 0x56, 0x25, 0x2C, 0x57, 0x47, 0x6F, \ + 0xC8, 0xE7, 0xD6, 0xC6, 0xF2, 0x7F, 0x03, 0x3A, 0x1C, 0x78, 0x05, 0x66, 0x24, 0x5F, 0x09, 0xCD, 0xAD, \ + 0x30, 0x55, 0x2C, 0x19, 0x28, 0x34, 0x1A, 0xC3, 0x31, 0x49, 0x16, 0xC7, 0x45, 0x1B}; +#endif #define DEVICE_HIVERSION "1.0.0" /* 设备固件版本号 */ -#define FIRMWARE_VER "1.0.0" +#define FIRMWARE_VER "1.1.1" /* 设备硬件版本号 */ #define HARDWARE_VER "1.0.0" /* 设备软件版本号 */ @@ -51,4 +87,4 @@ extern "C" { #endif #endif -#endif /* DEVICE_PROFILE_H */ \ No newline at end of file +#endif /* DEVICE_PROFILE_H */ 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 350fa32..3a7e7ec 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 @@ -15,6 +15,7 @@ #include "hsf.h" #include "hilink_entry.h" +// #undef CONFIG_SUPPORT_HILINK_INDIE_UPGRADE /* * * 服务信息定义 @@ -126,21 +127,18 @@ int HILINK_GetSvcInfo(HILINK_SvcInfo *svcInfo[], unsigned int size) } /* AC参数 */ -unsigned char A_C[48] = { - 0x66, 0x3F, 0x64, 0x78, 0x4B, 0x78, 0x3B, 0x26, 0x76, 0x78, 0x24, 0x56, 0x25, 0x2C, 0x57, 0x47, 0x6F, \ - 0xC8, 0xE7, 0xD6, 0xC6, 0xF2, 0x7F, 0x03, 0x3A, 0x1C, 0x78, 0x05, 0x66, 0x24, 0x5F, 0x09, 0xCD, 0xAD, \ - 0x30, 0x55, 0x2C, 0x19, 0x28, 0x34, 0x1A, 0xC3, 0x31, 0x49, 0x16, 0xC7, 0x45, 0x1B}; - +unsigned char A_C[48] = MANUAFACTURER_ACKEY; /* 获取加密 AC 参数 */ unsigned char *HILINK_GetAutoAc(void) { + // e_printf("HILINK_GetAutoAc\r\n"); return A_C; } extern int HILINK_HILINK_Printf(const char *format, ...); - +#if 0 //EKKO REMOVE // 设备状态定义 typedef struct{ int switch_on; @@ -243,20 +241,34 @@ int handle_get_cmd(const char* svc_id, const char* in, unsigned int in_len, char } return function(svc_id, in, in_len, out,out_len); } +#endif +// 声明外部函数 +extern int device_module_control(const char* svc_id, const char* payload, unsigned int len); +extern int device_module_get_state(const char* svc_id, const char* in, unsigned int in_len, char** out, unsigned int* out_len); +extern void handle_device_online(void); +extern void handle_device_unbind(void); +extern void handle_device_offline(void); +//替换实现 +#define handle_get_cmd device_module_get_state +#define handle_put_cmd device_module_control // 快速上报函数,用于上报服务状态信息 int fast_report(const char* 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; - } - err = HILINK_ReportCharState(svc_id, payload, len); - HILINK_Printf("report %s result is %d \r\n", svc_id, err); - return err; -} + // 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) { + e_printf("[fast_report] 获取服务状态失败: %d\r\n", err); + return err; + } + + err = HILINK_ReportCharState(svc_id, payload, len); + e_printf("[fast_report] 服务[%s] 状态上报结果: %d, payload: %s\r\n", svc_id, err, payload); + return err; +} + /* * 修改服务当前字段值 * svcId为服务的ID,payload为接收到需要修改的Json格式的字段与其值,len为payload的长度 @@ -266,16 +278,23 @@ 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); + if ((svcId == NULL) || (payload == NULL)) { + e_printf("[HILINK_PutCharState] 参数无效\r\n"); return -1; } cJSON *json = cJSON_Parse(payload); if (json == NULL) { - return -1; + e_printf("[HILINK_PutCharState] JSON解析失败\r\n"); + return -101; } - int err = handle_put_cmd(svcId, payload, len); - return err; + cJSON_Delete(json); + + int err = handle_put_cmd(svcId, payload, len); + e_printf("[HILINK_PutCharState] 控制指令处理完成,返回值: %d\r\n", err); + return err; } #ifdef CONFIG_SUPPORT_HILINK_INDIE_UPGRADE static int GetFwvCheckSum(char **out, unsigned int *outLen) @@ -375,14 +394,16 @@ void HILINK_NotifyDevStatus(int status) BLE_CfgNetAdvCtrl(0); (void)BLE_CfgNetAdvCtrl(0xFFFFFFFF); #endif + handle_device_offline(); break; case HILINK_M2M_CLOUD_ONLINE: - /* 设备连接云端成功,请在此处添加实现 */ #if defined(SUPPORT_MINIMALIST_NETCFG) || defined(SUPPORT_BLE_ANCILLAY_NETCFG) BLE_CfgNetAdvUpdate(NULL); BLE_CfgNetAdvCtrl(0); (void)BLE_CfgNetAdvCtrl(0xFFFFFFFF); #endif + /* 设备连接云端成功,请在此处添加实现 */ + handle_device_online(); // 处理设备上线 break; case HILINK_M2M_LONG_OFFLINE: /* 设备与云端连接长时间断开,请在此处添加实现 */ @@ -418,10 +439,12 @@ void HILINK_NotifyDevStatus(int status) /* 设备被注册,请在此处添加实现 */ break; case HILINK_DEVICE_UNREGISTER: - /* 设备被解绑,请在此处添加实现 */ + e_printf("[HILINK_NotifyDevStatus] 设备被解绑\r\n"); + handle_device_unbind(); break; case HILINK_REVOKE_FLAG_SET: /* 设备被复位标记置位,请在此处添加实现 */ + handle_device_unbind(); // 处理设备解绑 break; case HILINK_NEGO_REG_INFO_FAIL: /* 设备协商配网信息失败 */ @@ -466,7 +489,8 @@ int HILINK_ProcessBeforeRestart(int flag) /* APP删除设备触发模组重启 */ if (flag == 1) { /* 实现模组重启前的操作(如:保存系统状态等) */ - return 1; + handle_device_unbind(); // 处理设备解绑 + return 0; } /* 设备长时间离线触发模组重启,尝试恢复网络 */ diff --git a/application/ws63/hsf/hfuart.h b/application/ws63/hsf/hfuart.h index 1f19010..2031de3 100755 --- a/application/ws63/hsf/hfuart.h +++ b/application/ws63/hsf/hfuart.h @@ -57,14 +57,23 @@ typedef struct _HFUART }HFUART,*PHFUART; -void HSF_API HF_Debug(int debug_level, const char *format , ... ); +#if defined(DEBUG_LOG_ENABLE) +#define HF_Debug( debug_level, format, ...) printf(format, ##__VA_ARGS__) +#else +void HSF_API HF_Debug(int debug_level, const char *format, ...); +#endif #define hfdbg_error(...) HF_Debug(DEBUG_ERROR,"[ %d error %s %d]",hfsys_get_time(),__FUNCTION__,__LINE__); \ HF_Debug(DEBUG_ERROR,__VA_ARGS__) #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 feab344..aed2f5a 100755 --- a/application/ws63/user_main/CMakeLists.txt +++ b/application/ws63/user_main/CMakeLists.txt @@ -6,12 +6,19 @@ set(COMPONENT_NAME "user_main") set(SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/app_main.c + ${CMAKE_CURRENT_SOURCE_DIR}/spotlight/spotlight_main.c + ${CMAKE_CURRENT_SOURCE_DIR}/spotlight/device_module.c ) +if (DEFINES MATCHES "CONFIG_SPOTLIGHT_UT") + list(APPEND SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/spotlight/spotlight_ut.c) +endif() + if (DEFINES MATCHES "HF_MCU_OTA") list(APPEND SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/mcu_update.c) endif() set(PUBLIC_HEADER ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/spotlight/ ) set(PRIVATE_HEADER diff --git a/application/ws63/user_main/app_main.c b/application/ws63/user_main/app_main.c index 17a90bb..4d4e38d 100755 --- a/application/ws63/user_main/app_main.c +++ b/application/ws63/user_main/app_main.c @@ -11,6 +11,7 @@ #ifdef HF_MCU_OTA #include "mcu_update.h" #endif + int g_module_id = HFM_TYPE_LPT262; const int hf_lpt_262_gpio_fid_to_pid_map_table[HFM_MAX_FUNC_CODE]= { @@ -197,15 +198,34 @@ int hf_atcmd_appver(pat_session_t s,int argc,char *argv[],char *rsp,int len) } return -3; } +int hf_atcmd_reset(pat_session_t s,int argc,char *argv[],char *rsp,int len) +{ + extern int HILINK_RestoreFactorySettings(void); + HILINK_RestoreFactorySettings(); + return 0; +} +#ifdef CONFIG_SPOTLIGHT_UT + extern int hf_atcmd_pwm(pat_session_t s, int argc, char *argv[], char *rsp, int len); + extern int hf_atcmd_pwm_query(pat_session_t s, int argc, char *argv[], char *rsp, int len); + extern int hf_atcmd_light(pat_session_t s, int argc, char *argv[], char *rsp, int len); + extern int hf_atcmd_light_query(pat_session_t s, int argc, char *argv[], char *rsp, int len); +#endif const hfat_cmd_t user_define_at_cmds_table[]= { {"APPVER", hf_atcmd_appver, "AT+APPVER: get version. \r\n",NULL}, + {"RESET", hf_atcmd_reset, "AT+RESET: reset. \r\n",NULL}, #ifdef HF_MCU_OTA {"MCUOTA", hf_atcmd_mcu, "AT+MCUOTA: start mcu ota. \r\n",NULL}, {"MCUVER", hf_atcmd_mcuver, "AT+MCUVER: get mcu version. \r\n",NULL}, {"OTAWAITTIME", hf_atcmd_ota_wait_time, "AT+OTAWAITTIME: set/get uart send mcu ota time\r\n", NULL}, #endif - {NULL, NULL, NULL, NULL} //the last item must be null +#ifdef CONFIG_SPOTLIGHT_UT + {"UTPWM", hf_atcmd_pwm, "AT+PWM=,: Set PWM duty cycle (0-1000)\r\n", NULL}, + {"UTLIGHT", hf_atcmd_light, "AT+LIGHT=,: Set brightness(0-1000) and CCT(2700-6000)\r\n", NULL}, + {"UTPWMQ", NULL, "AT+PWMQ=,: Set PWM duty cycle (0-1000)\r\n", hf_atcmd_pwm_query}, + {"UTLIGHTQ", NULL, "AT+LIGHTQ=,: Set brightness(0-1000) and CCT(2700-6000)\r\n", hf_atcmd_light_query}, +#endif + {NULL, NULL, NULL, NULL} //the last item must be null }; int USER_FUNC user_app_main(void) @@ -220,7 +240,7 @@ int USER_FUNC user_app_main(void) HF_Debug(DEBUG_WARN,"init mcu ota fail!\r\n"); #endif //See Wi-Fi Config tools APP for detailed usage of this thread, only debug mode open - if(hfdbg_get_level()) + if(0 && hfdbg_get_level()) { while(!hfnet_wifi_is_active()) { @@ -233,7 +253,6 @@ int USER_FUNC user_app_main(void) } e_printf("start assis success!\r\n"); // hfgpio_pwm_enable(HFGPIO_F_SPI_CLK, 1000, 50); - spotlight_main(); return 1; } diff --git a/application/ws63/user_main/spotlight/device_module.c b/application/ws63/user_main/spotlight/device_module.c index 26cfe0d..1527b8c 100644 --- a/application/ws63/user_main/spotlight/device_module.c +++ b/application/ws63/user_main/spotlight/device_module.c @@ -1,70 +1,296 @@ #include "spotlight.h" #include "cJSON.h" - +// 服务处理函数定义 +typedef int (*handle_put_func)(const char* svc_id, const char* payload, unsigned int len); +typedef int (*handle_get_func)(const char* svc_id, const char* in, unsigned int in_len, char** out, unsigned int* out_len); +// 服务注册信息定义 +typedef struct{ + // service id + char* sid; + // HILINK_PutCharState cmd function + handle_put_func putFunc; + // handle HILINK_GetCharState cmd function + handle_get_func getFunc; +} HANDLE_SVC_INFO; #define CHECK_JSON_ITEM(condition) if(condition) { \ goto lab_end; \ } -#define MAX_BRIGHTNESS 100 -#define MIN_BRIGHTNESS 0 -#define MAX_CCT 6500 -#define MIN_CCT 2700 +extern device_control_t g_device_control; -typedef struct device_control_s { - bool on; - int32_t colorTemperature; - int32_t brightness; -} device_control_t; +// 场景模式预设值定义 +typedef struct { + int brightness; + int cct; +} scene_preset_t; -device_control_t g_device_control = { - .on = false, - .colorTemperature = MAX_CCT, - .brightness = MAX_BRIGHTNESS / 2, +static const scene_preset_t scene_presets[] = { + {(INIT_STA__BRIGHTNESS), (INIT_STA__CCT)}, // 模式0:非情景模式 配网成功后的默认 + {(50), (4000)}, // 模式1:休闲模式 - 较低亮度,暖色调 + {(10), (3000)}, // 模式2:观影模式 - 低亮度,暖色调 + {(100), (4000)}, // 模式3:用餐模式 - 中等亮度,自然光 + {(80), (3500)}, // 模式4:回家模式 - 较高亮度,自然光 + {(100), (CCT_MIN)}, // 模式5:冬天模式 - 中等亮度,暖色调 + {(100), (CCT_MAX)}, // 模式6:夏天模式 - 较高亮度,冷色调 }; -int device_module_control(const char *svcId, const char *payload, unsigned int len) +#define SCENE_MODE_COUNT (sizeof(scene_presets) / sizeof(scene_presets[0])) + +// 处理亮度控制 +static int handle_put_brightness(const char* svc_id, const char* payload, unsigned int len) { - if ((svcId == NULL) || (payload == NULL)) { + int ret = -1; + cJSON *json = cJSON_Parse(payload); + if (json == NULL) { return -1; } - cJSON *item = NULL; - cJSON *json = cJSON_Parse(payload); - CHECK_JSON_ITEM(json == NULL); - e_printf("put char sid:%s,payload:%s\r\n",svcId,payload); - if(strcmp(svcId, "brightness") == 0) - { - item = cJSON_GetObjectItem(json,"brightness"); - CHECK_JSON_ITEM(item == NULL); - CHECK_JSON_ITEM(!cJSON_IsNumber(item)); - } - else if(strcmp(svcId, "cct") == 0) - { - item = cJSON_GetObjectItem(json,"colorTemperature"); - CHECK_JSON_ITEM(item == NULL); - CHECK_JSON_ITEM(!cJSON_IsNumber(item)); - } else if(strcmp(svcId, "lightMode") == 0) // scense mode - { - item = cJSON_GetObjectItem(json,"mode"); - CHECK_JSON_ITEM(item == NULL); - CHECK_JSON_ITEM(!cJSON_IsNumber(item)); - - } - else if(strcmp(svcId, "switch") == 0) // device open/close - { - item = cJSON_GetObjectItem(json,"on"); - CHECK_JSON_ITEM(item == NULL); - CHECK_JSON_ITEM(!cJSON_IsNumber(item)); - } - -lab_end: - if(json != NULL) - { + cJSON *item = cJSON_GetObjectItem(json, JSON_FIELD_BRIGHTNESS); + if (item == NULL || !cJSON_IsNumber(item)) { cJSON_Delete(json); - json = NULL; + return -1; } + + int brightness = item->valueint; + if (brightness < BRIGHTNESS_MIN) brightness = BRIGHTNESS_MIN; + if (brightness > BRIGHTNESS_MAX) brightness = BRIGHTNESS_MAX; + + // g_device_control.brightness_local = brightness; + g_device_control.elightMode = LIGHT_MODE_CUSTOMER; + ret = set_light(BRIGHTNESS_REMOTE2LOCAL(brightness), -1); // 只更新亮度,色温保持不变 + + cJSON_Delete(json); + return ret; //异步上报 +} + +// 处理色温控制 +static int handle_put_cct(const char* svc_id, const char* payload, unsigned int len) +{ + int ret = -1; + cJSON *json = cJSON_Parse(payload); + if (json == NULL) { + return -1; + } + + cJSON *item = cJSON_GetObjectItem(json, JSON_FIELD_CCT); + if (item == NULL || !cJSON_IsNumber(item)) { + cJSON_Delete(json); + return -1; + } + + int cct = item->valueint; + if (cct < CCT_MIN) cct = CCT_MIN; + if (cct > CCT_MAX) cct = CCT_MAX; + + // g_device_control.cct_local = cct; + g_device_control.elightMode = LIGHT_MODE_CUSTOMER; + ret = set_light(-1, CCT_REMOTE2LOCAL(cct)); // 只更新色温,亮度保持不变 + + cJSON_Delete(json); + return ret ;//异步上报 +} + +// 处理场景模式控制 +static int handle_put_lightMode(const char* svc_id, const char* payload, unsigned int len) +{ + int ret = -1; + cJSON *json = cJSON_Parse(payload); + if (json == NULL) { + return -1; + } + + cJSON *item = cJSON_GetObjectItem(json, JSON_FIELD_MODE); + if (item == NULL || !cJSON_IsNumber(item)) { + cJSON_Delete(json); + return -1; + } + + uint16_t mode = item->valueint; + if (mode >= 0 && mode < SCENE_MODE_COUNT) { + g_device_control.elightMode = mode; + + // 同时更新亮度和色温 + ret = set_light(BRIGHTNESS_REMOTE2LOCAL(scene_presets[mode].brightness), CCT_REMOTE2LOCAL(scene_presets[mode].cct)); + } + + cJSON_Delete(json); + return ret; +} + +// 处理开关控制 +static int handle_put_switch(const char* svc_id, const char* payload, unsigned int len) +{ + int ret = -1; + cJSON *json = cJSON_Parse(payload); + if (json == NULL) { + return -1; + } + + cJSON *item = cJSON_GetObjectItem(json, JSON_FIELD_ON); + if (item == NULL || !cJSON_IsNumber(item)) { + cJSON_Delete(json); + return -1; + } + + bool on = (item->valueint != 0); + g_device_control.on = on; + ret = set_switch(on); + + cJSON_Delete(json); + return ret; +} + +// 获取亮度状态 +static int handle_get_brightness(const char* svc_id, const char* in, unsigned int in_len, char** out, unsigned int* out_len) +{ + *out_len = 32; + *out = (char*)malloc(*out_len); + if (NULL == *out) { + return -1; + } + *out_len = sprintf_s(*out, *out_len, "{\"%s\":%d}", JSON_FIELD_BRIGHTNESS, BRIGHTNESS_LOCAL2REMOTE(g_device_control.brightness_local)); return 0; } +// 获取色温状态 +static int handle_get_cct(const char* svc_id, const char* in, unsigned int in_len, char** out, unsigned int* out_len) +{ + *out_len = 32; + *out = (char*)malloc(*out_len); + if (NULL == *out) { + return -1; + } + *out_len = sprintf_s(*out, *out_len, "{\"%s\":%d}", JSON_FIELD_CCT, CCT_LOCAL2REMOTE(g_device_control.cct_local)); + return 0; +} + +// 获取场景模式状态 +static int handle_get_lightMode(const char* svc_id, const char* in, unsigned int in_len, char** out, unsigned int* out_len) +{ + *out_len = 32; + *out = (char*)malloc(*out_len); + if (NULL == *out) { + return -1; + } + *out_len = sprintf_s(*out, *out_len, "{\"%s\":%d}", JSON_FIELD_MODE, g_device_control.elightMode); + return 0; +} + +// 获取开关状态 +static int handle_get_switch(const char* svc_id, const char* in, unsigned int in_len, char** out, unsigned int* out_len) +{ + *out_len = 32; + *out = (char*)malloc(*out_len); + if (NULL == *out) { + return -1; + } + *out_len = sprintf_s(*out, *out_len, "{\"%s\":%d}", JSON_FIELD_ON, g_device_control.on); + return 0; +} + +// 处理渐变时长控制 +static int handle_put_smooth_time(const char* svc_id, const char* payload, unsigned int len) +{ + int ret = -1; + e_printf("[handle_put_smooth_time] Received smooth time setting request: %s\n", payload); + + cJSON *json = cJSON_Parse(payload); + if (json == NULL) { + e_printf("[handle_put_smooth_time] JSON parsing failed\n"); + return -1; + } + + cJSON *item = cJSON_GetObjectItem(json, JSON_FIELD_FADE_TIME); + if (item == NULL || !cJSON_IsNumber(item)) { + e_printf("[handle_put_smooth_time] Invalid smooth time parameter\n"); + cJSON_Delete(json); + return -1; + } + + int smooth_time = item->valueint; + e_printf("[handle_put_smooth_time] Setting smooth time: %d s\n", smooth_time); + ret = set_smooth_time(smooth_time); + + cJSON_Delete(json); + // e_printf("[handle_put_smooth_time] Smooth time setting completed\n"); + return ret; +} + +// 获取渐变时长状态 +static int handle_get_smooth_time(const char* svc_id, const char* in, unsigned int in_len, char** out, unsigned int* out_len) +{ + *out_len = 32; + *out = (char*)malloc(*out_len); + if (NULL == *out) { + e_printf("[handle_get_smooth_time] Memory allocation failed\n"); + return -1; + } + + *out_len = sprintf_s(*out, *out_len, "{\"%s\":%d}", JSON_FIELD_FADE_TIME, g_device_control.fade_time); + return 0; +} + +// 服务处理信息注册 +HANDLE_SVC_INFO g_device_profile[] = { + {SVC_ID_SWITCH, handle_put_switch, handle_get_switch}, + {SVC_ID_BRIGHTNESS, handle_put_brightness, handle_get_brightness}, + {SVC_ID_CCT, handle_put_cct, handle_get_cct}, + {SVC_ID_LIGHT_MODE, handle_put_lightMode, handle_get_lightMode}, + {SVC_ID_FADE_TIME, handle_put_smooth_time, handle_get_smooth_time}, +}; + +// 服务总数量 +int g_device_profile_count = sizeof(g_device_profile) / sizeof(HANDLE_SVC_INFO); + +// 辅助函数,用于查找服务注册信息 +static HANDLE_SVC_INFO* find_handle(const char* svc_id) +{ + for(int i = 0; i < g_device_profile_count; i++) { + HANDLE_SVC_INFO handle = g_device_profile[i]; + if(strcmp(handle.sid, svc_id) == 0) { + return &g_device_profile[i]; + } + } + return NULL; +} + +// 分发设备控制指令 +int device_module_control(const char* svc_id, const char* payload, unsigned int len) +{ + e_printf("[device_module_control] Received control command: svc_id=%s, payload=%s\n", svc_id, payload); + + HANDLE_SVC_INFO* handle = find_handle(svc_id); + if(handle == NULL) { + e_printf("[device_module_control] No service handler found\n"); + return -1; + } + + handle_put_func function = handle->putFunc; + if(function == NULL) { + e_printf("[device_module_control] Service handler function is NULL\n"); + return -1; + } + + int ret = function(svc_id, payload, len); + e_printf("[device_module_control] Control command processing completed, return value: %d\n", ret); + return ret; +} + +// 分发获取状态指令 +int device_module_get_state(const char* svc_id, const char* in, unsigned int in_len, char** out, unsigned int* out_len) +{ + HANDLE_SVC_INFO* handle = find_handle(svc_id); + if(handle == NULL) { + return -1; + } + + handle_get_func function = handle->getFunc; + if(function == NULL) { + return -1; + } + + return function(svc_id, in, in_len, out, out_len); +} + diff --git a/application/ws63/user_main/spotlight/spotlight.h b/application/ws63/user_main/spotlight/spotlight.h index 8520ee5..6596d85 100644 --- a/application/ws63/user_main/spotlight/spotlight.h +++ b/application/ws63/user_main/spotlight/spotlight.h @@ -2,6 +2,166 @@ #define __SPOTLIGHT_H__ #include + +typedef enum elightMode { + LIGHT_MODE_CUSTOMER = 0, // 非情景模式 + LIGHT_MODE_RELAX, // 休闲模式 + LIGHT_MODE_MOVIE, // 观影模式 + LIGHT_MODE_DINING, // 用餐模式 + LIGHT_MODE_HOME, // 回家模式 + LIGHT_MODE_WINTER, // 冬天模式 + LIGHT_MODE_SUMMER, // 夏天模式 + LIGHT_MODE_MAX // 模式数量 +} lightMode_e; + +typedef enum { + INIT_POWER_ON, + NET_CONFIGING, + BIND_OK, +}eDeviceBindStatus; + + +// 服务ID定义 +#define SVC_ID_SWITCH "switch" // 开关控制 +#define SVC_ID_BRIGHTNESS "brightness" // 亮度控制 +#define SVC_ID_CCT "cct" // 色温控制 +#define SVC_ID_LIGHT_MODE "lightMode" // 场景模式控制 +#define SVC_ID_FADE_TIME "progressSwitch" // 渐变时长的控制 + +// JSON字段名定义 +#define JSON_FIELD_ON "on" // 开关状态字段 +#define JSON_FIELD_BRIGHTNESS "brightness" // 亮度字段 +#define JSON_FIELD_CCT "colorTemperature" // 色温字段 +#define JSON_FIELD_MODE "mode" // 场景模式字段 +#define JSON_FIELD_FADE_TIME "fadeTime" // 渐变时长字段 + + +// 当前亮度和色温状态 +typedef struct { + // 物模型同步需要 持久化维持 + bool on; // 开关状态 + lightMode_e elightMode; + uint16_t brightness_local; // 当前亮度 (0-1000) + uint16_t fade_time; // s + uint16_t cct_local; // 当前色温 (2700-6500) + // 持久化维持 + int32_t power_on_cnt; // 上电次数计数 + bool is_networked; // 是否已配网 + //运行时的数据 + bool read_done; // 读取数据done + uint16_t duty_cw; // 冷白LED占空比 + uint16_t duty_ww; // 暖白LED占空比 +} device_control_t; + +// 色温范围定义 +#define CCT_MIN 2700 // 最小色温 2700K (暖白) +#define CCT_MAX 6000 // 最大色温 6000K (冷白) +#define CCT_RANGE (CCT_MAX - CCT_MIN) +#define CCT_LOCAL_MIN 0 // 最小色温 0K (暖白) +#define CCT_LOCAL_MAX (CCT_LOCAL_MIN + CCT_RANGE) // 最大色温 6000K (冷白) +#define CCT_LOCAL2REMOTE(x) (x + (CCT_MIN - CCT_LOCAL_MIN)) +#define CCT_REMOTE2LOCAL(x) (x - (CCT_MIN - CCT_LOCAL_MIN)) +#define CCT_LITME_RANGE(x) do { \ + if (x > CCT_LOCAL_MAX) x = CCT_LOCAL_MAX; \ + if (x < CCT_LOCAL_MIN) x = CCT_LOCAL_MIN; \ +} while (0) + +// 亮度范围定义 +#define BRIGHTNESS_MIN 0 // 最小亮度 +#define BRIGHTNESS_MAX 100 // 最大亮度 +#define BRIGHTNESS_LOCAL_MIN 0 // 最小亮度 +#define BRIGHTNESS_LOCAL_MAX 1000 // 最大亮度 +#define BRIGHTNESS_REMOTE2LOCAL(x) (x * 10) //变化范围0 -1000 +#define BRIGHTNESS_LOCAL2REMOTE(x) (x / 10) + +#define BRIGHTNESS_LITME_RANGE(x) do { \ + if (x > BRIGHTNESS_LOCAL_MAX) x = BRIGHTNESS_LOCAL_MAX; \ + if (x < BRIGHTNESS_LOCAL_MIN) x = BRIGHTNESS_LOCAL_MIN; \ +} while (0) + +// 设备上电并未进入配网的状态 +#define INIT_STA__LIGHT_MODE LIGHT_MODE_CUSTOMER +#define INIT_STA__BRIGHTNESS 50 +#define INIT_STA__CCT 4000 + +#define INIT_NET_CFG_PWOER_ON_KEEP_TIME (5 * 1000) // 统计进入配网每次打开状态的保持时间 + +#define NET_CFG_ENTRY_CNT 6 // 配网进入的上电次数 +#define NET_CFG_BREATH_DURATION 1*60*1000 // 配网呼吸灯持续时间(ms) +#define NET_CFG_TOTAL_TIMEOUT 10*60*1000 // 配网总超时时间(ms) + +// 配网成功后的默认值 +#define NET_CFG_DEFAULT_BRIGHTNESS 80 // 默认亮度 +#define NET_CFG_DEFAULT_CCT 6000 // 默认色温 +#define NET_CFG_DEFAULT_FADE_TIME 1 // 默认渐变时长(s) +#define NET_CFG_DEFAULT_LIGHTMODE LIGHT_MODE_CUSTOMER // 默认模式 + +// 配网超时后的默认值 +#define NET_CFG_TIMEOUT_BRIGHTNESS 50 // 呼吸灯超时后的亮度 +#define NET_CFG_TIMEOUT_CCT 4000 // 呼吸灯超时后的色温 + +#define FADE_INTERVAL_MIN (1*1000) //us + +#define PWM_DUTY_RATIO_MAX 1000 + +//呼吸灯定义 +#define BREARTH_PERIOD (3*1000*1000) //呼吸灯周期(Us) +#define BREARTH_INTERVAL (3*1000) //更新pwm的间隔 us +#define BREARTH_STEP ((PWM_DUTY_RATIO_MAX) * 2) / (BREARTH_PERIOD / BREARTH_INTERVAL) //每次变化的幅度 + + +//渐变范围 +#define SMOOTH_TIME_MAX 30 +#define SMOOTH_TIME_MIN 0 + +#define SUPPORT_SAVE_TO_FLASH 1 + +// 定义Flash存储地址和大小 +#define DEVICE_DATA_FLASH_ADDR 0x000000 // 主数据区地址 +#define DEVICE_DATA_BACKUP_ADDR 0x001000 // 备份数据区地址 +#define DEVICE_DATA_FLASH_SIZE 0x001000 // 使用一个页的大小 + +// 设备数据结构 +typedef struct { + uint32_t checksum; // 校验和 + uint32_t magic; // 魔数,用于验证数据有效性 + uint32_t version; // 数据版本号 + device_control_t control; // 设备控制状态 +} device_data_t; + +#define DEVICE_DATA_MAGIC 0x4C505426 // "LPT&"的ASCII码 +#define DEVICE_DATA_VERSION 1 // 数据版本号 + int spotlight_main(void); +int set_light(int32_t brightness, int32_t cct); // 新的统一控制函数 +int set_switch(bool open); +int set_smooth_time(uint32_t smooth_time); // 设置渐变时长 + +extern int fast_report(const char* svc_id); + +// 保存任务相关定义 +#define SAVE_TASK_PRIO 25 +#define SAVE_TASK_STACK_SIZE 0x1000 +#define SAVE_TASK_SLEEP_TIME 100 // 100ms + +// 保存任务状态 +typedef enum { + SAVE_STATE_IDLE = 0, // 空闲状态 + SAVE_STATE_SAVING, // 正在保存 + SAVE_STATE_WAITING // 等待保存 +} save_state_e; + +#define DEFAULT_DEVICE_DATA { \ + .read_done = false, \ + .on = true, \ + .elightMode = INIT_STA__LIGHT_MODE, \ + .cct_local = CCT_REMOTE2LOCAL(INIT_STA__CCT), \ + .brightness_local = BRIGHTNESS_REMOTE2LOCAL(INIT_STA__BRIGHTNESS), \ + .fade_time = NET_CFG_DEFAULT_FADE_TIME, \ + .is_networked = false, \ + .duty_cw = 0, \ + .duty_ww = 0, \ +}; + #endif diff --git a/application/ws63/user_main/spotlight/spotlight_main.c b/application/ws63/user_main/spotlight/spotlight_main.c index aef155a..19f1470 100644 --- a/application/ws63/user_main/spotlight/spotlight_main.c +++ b/application/ws63/user_main/spotlight/spotlight_main.c @@ -1,120 +1,1266 @@ #include "hsf.h" +#include "hfconfig.h" #if defined(CONFIG_PWM_SUPPORT_LPM) #include "pm_veto.h" #endif #include "pinctrl.h" #include "gpio.h" +#include "pinctrl.h" #include "pwm.h" +#include "timer.h" +#include "soc_osal.h" +#include "systick.h" + // #include "tcxo.h" #include "spotlight.h" -#define BRIGHTNESS_PIN GPIO_00 -#define CCT_PIN GPIO_04 +// 函数前向声明 +static void init_timer_system(void); +static void init_fade_ctx(void); +static void init_breath_ctx(void); +static void stop_fade_task(void); +static void stop_breath_task(void); +static void stop_breath_timer(void); +static void stop_spotlight_main_task(void); +static void check_net_cfg_timeout(void); +static void check_net_cfg_power_on_keep_time(void); +static void set_light2breathtimeout(void); +static void set_light2net_cfg_done(void); +static void start_breath_timer(void); +void calculate_pwm_duty(void); +void update_pwm_output(void); + +// 定义上报函数类型 +typedef void (*report_func_t)(void); +static void start_report_task(report_func_t report_func); +static void report_fade_complete_status(void); +static uint32_t calculate_checksum(const device_data_t* data); +static bool write_device_data_to_addr(uint32_t addr, const device_data_t* data);; +static void report_device_online_status(void); + + +#define BRIGHTNESS_PIN GPIO_04 // 冷白LED (CW) +#define CCT_PIN GPIO_00 // 暖白LED (WW) #define SWITCH_PIN GPIO_13 -#define BRIGHTNESS_PWM_CHANNEL 0 -#define CCT_PWM_CHANNEL 4 +#define CONFIG_PWM_GROUP_ID 2 -#define CONFIG_PWM_GROUP_ID 0 +#define OPEN_LIGHT GPIO_LEVEL_HIGH +#define CLOSE_LIGHT GPIO_LEVEL_LOW -static errcode_t pwm_sample_callback(uint8_t channel) +// 状态上报任务相关变量 +#define REPORT_TASK_PRIO 80 +#define REPORT_TASK_STACK_SIZE 0x1000 +static osal_task *report_task_handle = NULL; +static bool report_task_running = false; // 添加任务运行状态标志 + +// 灯渐变任务相关定义 +#define FADE_TASK_PRIO 80 +#define FADE_TASK_STACK_SIZE 0x1000 + +// 呼吸灯任务相关定义 +#define BREATH_TASK_PRIO 80 +#define BREATH_TASK_STACK_SIZE 0x1000 + +// PWM频率和周期定义 +#define PWM_FREQUENCY 2000 // PWM频率 2KHz + +static uint32_t pwm_period_cnt = 0; // PWM周期 40us +static uint8_t channel_id_cw = 0; // 冷白LED通道ID +static uint8_t channel_id_ww = 0; // 暖白LED通道ID + +void save_device_data(void); +void read_device_data(void); +void req_save_device_data(void); +static void stop_breath_timer(void); +device_control_t g_device_control = DEFAULT_DEVICE_DATA; + +// 配网状态定义 +typedef enum { + NET_CFG_IDLE = 0, // 空闲状态 + NET_CFG_BREATHING, // 呼吸灯状态,配网中 + NET_CFG_TIMEOUT, // 超时状态 +} net_cfg_state_e; + +// 配网状态相关变量 +static net_cfg_state_e g_net_breath_state = NET_CFG_IDLE; // 配网状态 +static uint64_t g_net_cfg_start_time = 0; // 配网开始时间 + +// 保存任务相关变量 +static osal_task *save_task_handle = NULL; +static bool save_task_running = false; +static osal_semaphore save_data_sem; + +// 打印频率限制相关定义 +#define PRINT_INTERVAL_MS 1000 // 最小打印间隔(ms) + +typedef struct { + uint64_t last_print_time; // 上次打印时间 + const char *prefix; // 打印前缀 +} print_limiter_t; + +// 渐变控制相关变量 +static struct { + // 渐变控制参数 + int32_t target_brightness; + int32_t target_cct; + int32_t current_brightness; + int32_t current_cct; + int32_t step_brightness; + int32_t step_cct; + bool is_fading; + bool fade_completed; + uint32_t smooth_time_us; // 渐变总时长(us) + uint32_t update_interval; // 更新间隔(us) + + // 任务相关 + osal_task *task_handle; + bool task_running; + osal_semaphore sem; + timer_handle_t timer_handle; + + // 打印限制器 + print_limiter_t print_limiter; +} fade_ctx = {0}; + +// 呼吸灯控制相关变量 +static struct { + // 呼吸灯控制参数 + int32_t duty; + int32_t step; + uint32_t update_interval; // 更新间隔(us) + bool is_initial_breath; // 是否是初始3秒呼吸 + + // 任务相关 + osal_task *task_handle; + bool task_running; + osal_semaphore sem; + timer_handle_t timer_handle; + + uint32_t start_time_ms; + uint32_t breath_cnt; + + // 打印限制器 + print_limiter_t print_limiter; +} breath_ctx = {0}; + +print_limiter_t pwm_limiter = { + .last_print_time = 0, + .prefix = "[pwm]", +}; + +// 定时器初始化标志 +static bool timer_initialized = false; + +// 初始化定时器系统 +static void init_timer_system(void) { - e_printf("PWM %d, cycle done. \r\n", channel); - return ERRCODE_SUCC; + if (!timer_initialized) { + uapi_timer_init(); + int ret = uapi_timer_adapter(TIMER_INDEX_1, TIMER_1_IRQN, 1); + if (ret != 0) { + e_printf("定时器适配器初始化失败\r\n"); + return; + } + timer_initialized = true; + } +} + +// 初始化打印限制器 +static void init_print_limiter(print_limiter_t *limiter, const char *prefix) +{ + limiter->last_print_time = 0; + limiter->prefix = prefix; +} + +// 带频率限制的打印函数 +static bool should_print(print_limiter_t *limiter) +{ + uint64_t current_time = uapi_systick_get_ms(); + if (current_time - limiter->last_print_time >= PRINT_INTERVAL_MS) { + limiter->last_print_time = current_time; + return true; + } + return false; +} + +// 渐变灯任务函数 +static void *fade_task(const char *arg) +{ + unused(arg); + e_printf("[fade_task] Task started\r\n"); + + while (fade_ctx.task_running) { + // 等待渐变信号 + if (osal_sem_down_timeout(&fade_ctx.sem, 100*1000) != OSAL_SUCCESS) { + continue; + } + + if (!fade_ctx.is_fading) { + continue; + } + + // 使用打印限制器控制打印频率 + if (should_print(&fade_ctx.print_limiter)) { + e_printf("%s Brightness: curr:%d, target:%d, step:%d, cct:curr:%d, target:%d, step:%d\r\n", + fade_ctx.print_limiter.prefix, + fade_ctx.current_brightness, fade_ctx.target_brightness, fade_ctx.step_brightness, + fade_ctx.current_cct, fade_ctx.target_cct,fade_ctx.step_cct); + } + + // 检查是否达到目标值 + bool brightness_reached = (fade_ctx.step_brightness > 0) ? + (fade_ctx.current_brightness >= fade_ctx.target_brightness) : + (fade_ctx.current_brightness <= fade_ctx.target_brightness); + + bool cct_reached = (fade_ctx.step_cct > 0) ? + (fade_ctx.current_cct >= fade_ctx.target_cct) : + (fade_ctx.current_cct <= fade_ctx.target_cct); + // 更新当前值 + if (!brightness_reached) + { + fade_ctx.current_brightness += fade_ctx.step_brightness; + } + if (!cct_reached) + { + fade_ctx.current_cct += fade_ctx.step_cct; + } + // 如果达到目标值,停止渐变 + if (brightness_reached && cct_reached) { + e_printf("[fade_task] Fade completed, Final brightness: %d, CCT: %d\r\n", + fade_ctx.target_brightness, + fade_ctx.target_cct); + fade_ctx.is_fading = false; + fade_ctx.fade_completed = true; + fade_ctx.current_brightness = fade_ctx.target_brightness; + fade_ctx.current_cct = fade_ctx.target_cct; + uapi_timer_stop(fade_ctx.timer_handle); + } + + BRIGHTNESS_LITME_RANGE(fade_ctx.current_brightness); + CCT_LITME_RANGE(fade_ctx.current_cct); + // 更新PWM输出 + g_device_control.brightness_local = fade_ctx.current_brightness; + g_device_control.cct_local = fade_ctx.current_cct; + calculate_pwm_duty(); + update_pwm_output(); + + if (fade_ctx.fade_completed) { + fade_ctx.fade_completed = false; + e_printf("[fade_task] Status report: local brightness=%d, CCT=%d\r\n", + g_device_control.brightness_local, + g_device_control.cct_local); + // 启动状态上报任务,使用渐变完成状态上报函数 + start_report_task(report_device_online_status); + req_save_device_data(); + } + } + + e_printf("[fade_task] Task exited\r\n"); + return NULL; +} + +// 呼吸灯任务函数 +static void *breath_task(const char *arg) +{ + unused(arg); + e_printf("[breath_task] Task started\r\n"); + + while (breath_ctx.task_running) { + // 等待呼吸信号 + if (osal_sem_down_timeout(&breath_ctx.sem, 100*1000) != OSAL_SUCCESS) { + continue; + } + + if (g_net_breath_state != NET_CFG_BREATHING) { + continue; + } + + // 检查是否超过呼吸灯持续时间 + uint32_t current_time = uapi_systick_get_ms(); + uint32_t elapsed_time = current_time - g_net_cfg_start_time; + + if (elapsed_time >= NET_CFG_BREATH_DURATION) { + set_light2breathtimeout(); + stop_breath_timer(); + continue; + } + // 检查是否需要改变方向 + if (breath_ctx.duty >= BRIGHTNESS_LOCAL_MAX) { + breath_ctx.duty = BRIGHTNESS_LOCAL_MAX; + breath_ctx.step = -1 * breath_ctx.step; + e_printf("%s breathing %d, down %dms\n", breath_ctx.print_limiter.prefix, breath_ctx.breath_cnt, uapi_systick_get_ms() - breath_ctx.start_time_ms); + } else if (breath_ctx.duty <= 0) { + breath_ctx.duty = 0; + breath_ctx.step = -1 * breath_ctx.step; + e_printf("%s breathing %d, up %dms\n", breath_ctx.print_limiter.prefix, breath_ctx.breath_cnt, uapi_systick_get_ms() - breath_ctx.start_time_ms); + } + + // 更新亮度 + breath_ctx.duty += breath_ctx.step; + + int32_t brightness_pwm = breath_ctx.duty; + // 设置黄白灯交替呼吸 + g_device_control.duty_ww = brightness_pwm; + g_device_control.duty_cw = PWM_DUTY_RATIO_MAX - brightness_pwm; + + update_pwm_output(); + } + + e_printf("[breath_task] Task exited\r\n"); + return NULL; +} + +// 渐变定时器回调函数 +static void fade_timer_callback(uintptr_t data) +{ + osal_sem_up(&fade_ctx.sem); + uapi_timer_start(fade_ctx.timer_handle, fade_ctx.update_interval, fade_timer_callback, 0); +} + +// 呼吸灯定时器回调函数 +static void breath_timer_callback(uintptr_t data) +{ + osal_sem_up(&breath_ctx.sem); + uapi_timer_start(breath_ctx.timer_handle, breath_ctx.update_interval, breath_timer_callback, 0); +} + +// 初始化渐变控制 +static void init_fade_ctx(void) +{ + init_timer_system(); + int ret = uapi_timer_create(TIMER_INDEX_1, &fade_ctx.timer_handle); + if (ret != 0) { + e_printf("渐变定时器创建失败\r\n"); + } + + // 初始化信号量 + if (osal_sem_init(&fade_ctx.sem, 0) != OSAL_SUCCESS) { + e_printf("[init_fade_ctx] Failed to init semaphore\n"); + return; + } + + // 初始化打印限制器 + init_print_limiter(&fade_ctx.print_limiter, "[fade_task]"); + + // 创建渐变任务 + osal_kthread_lock(); + fade_ctx.task_handle = osal_kthread_create((osal_kthread_handler)fade_task, NULL, "FadeTask", + FADE_TASK_STACK_SIZE); + if (fade_ctx.task_handle != NULL) { + fade_ctx.task_running = true; + osal_kthread_set_priority(fade_ctx.task_handle, FADE_TASK_PRIO); + e_printf("[init_fade_ctx] Fade task created successfully\n"); + } else { + e_printf("[init_fade_ctx] Failed to create fade task\n"); + osal_sem_destroy(&fade_ctx.sem); + } + osal_kthread_unlock(); + + // 设置默认更新间隔 + fade_ctx.update_interval = FADE_INTERVAL_MIN; +} + +// 初始化呼吸灯控制 +static void init_breath_ctx(void) +{ + init_timer_system(); + int ret = uapi_timer_create(TIMER_INDEX_1, &breath_ctx.timer_handle); + if (ret != 0) { + e_printf("呼吸灯定时器创建失败\r\n"); + } + + // 初始化信号量 + if (osal_sem_init(&breath_ctx.sem, 0) != OSAL_SUCCESS) { + e_printf("[init_breath_ctx] Failed to init semaphore\n"); + return; + } + + // 初始化打印限制器 + init_print_limiter(&breath_ctx.print_limiter, "[breath_task]"); + breath_ctx.duty = g_device_control.brightness_local; + breath_ctx.step = BREARTH_STEP == 0 ? 1 : BREARTH_STEP; + breath_ctx.update_interval = BREARTH_INTERVAL; + breath_ctx.is_initial_breath = true; + breath_ctx.start_time_ms = uapi_systick_get_ms(); + e_printf("Breath task: brightness=%d, step=%d, update_interval=%d, is_initial_breath=%d\n", + breath_ctx.duty, breath_ctx.step, breath_ctx.update_interval, breath_ctx.is_initial_breath); + + // 创建呼吸灯任务 + osal_kthread_lock(); + breath_ctx.task_handle = osal_kthread_create((osal_kthread_handler)breath_task, NULL, "BreathTask", + BREATH_TASK_STACK_SIZE); + if (breath_ctx.task_handle != NULL) { + breath_ctx.task_running = true; + osal_kthread_set_priority(breath_ctx.task_handle, BREATH_TASK_PRIO); + e_printf("[init_breath_ctx] Breath task created successfully\n"); + } else { + e_printf("[init_breath_ctx] Failed to create breath task\n"); + osal_sem_destroy(&breath_ctx.sem); + } + osal_kthread_unlock(); +} + +// 停止渐变任务 +static void stop_fade_task(void) +{ + if (fade_ctx.task_handle == NULL) { + return; + } + + fade_ctx.task_running = false; + osal_sem_up(&fade_ctx.sem); // 唤醒任务以使其退出 + + osal_kthread_lock(); + osal_kthread_destroy(fade_ctx.task_handle, 0); + fade_ctx.task_handle = NULL; + osal_kthread_unlock(); + + osal_sem_destroy(&fade_ctx.sem); + + e_printf("[stop_fade_task] Fade task stopped\n"); +} + +// 停止呼吸灯任务 +static void stop_breath_task(void) +{ + if (breath_ctx.task_handle == NULL) { + return; + } + + breath_ctx.task_running = false; + osal_sem_up(&breath_ctx.sem); // 唤醒任务以使其退出 + + osal_kthread_lock(); + osal_kthread_destroy(breath_ctx.task_handle, 0); + breath_ctx.task_handle = NULL; + osal_kthread_unlock(); + + osal_sem_destroy(&breath_ctx.sem); + + e_printf("[stop_breath_task] Breath task stopped\n"); +} + +// 修改stop_breath_timer函数 +static void stop_breath_timer(void) +{ + uapi_timer_stop(breath_ctx.timer_handle); + uapi_timer_delete(breath_ctx.timer_handle); + stop_breath_task(); +} + +// 计算PWM占空比 +void calculate_pwm_duty(void) +{ // 如果开关关闭,则占空比为0 + uint32_t total_brightness_pwm = g_device_control.brightness_local; + // 计算色温比例 (0-10000) + uint16_t cct_ratio = ((g_device_control.cct_local - CCT_LOCAL_MIN) * 10000) / CCT_RANGE; + + // 根据色温比例计算CW和WW的占空比 + // 总亮度保持不变,只调整CW和WW的比例 + g_device_control.duty_cw = (total_brightness_pwm * cct_ratio) / 10000; + g_device_control.duty_ww = total_brightness_pwm - g_device_control.duty_cw; +} + +// 更新PWM输出 +void update_pwm_output(void) +{ + pwm_config_t cfg_repeat = {0}; + cfg_repeat.repeat = true; + if (g_device_control.duty_cw > PWM_DUTY_RATIO_MAX) { + g_device_control.duty_cw = PWM_DUTY_RATIO_MAX; + } + if (g_device_control.duty_ww > PWM_DUTY_RATIO_MAX) { + g_device_control.duty_ww = PWM_DUTY_RATIO_MAX; + } + uint32_t high_cnt_cw = (pwm_period_cnt * g_device_control.duty_cw) / PWM_DUTY_RATIO_MAX; + uint32_t low_cnt_cw = pwm_period_cnt - high_cnt_cw; + + uint32_t high_cnt_ww = (pwm_period_cnt * g_device_control.duty_ww) / PWM_DUTY_RATIO_MAX; + uint32_t low_cnt_ww = pwm_period_cnt - high_cnt_ww; + + // uapi_pwm_stop_group(CONFIG_PWM_GROUP_ID); + // uapi_pwm_clear_group(CONFIG_PWM_GROUP_ID); + if (!g_device_control.on) { // 如果开关关闭,则占空比为0 + e_printf("spotlight off\r\n"); + high_cnt_cw = 0; + low_cnt_cw = 0; + high_cnt_ww = 0; + low_cnt_ww = 0; + } + cfg_repeat.high_time = high_cnt_cw; + cfg_repeat.low_time = low_cnt_cw; + uapi_pwm_open(channel_id_cw, &cfg_repeat); + // 配置第二个通道 + cfg_repeat.high_time = high_cnt_ww; + cfg_repeat.low_time = low_cnt_ww; + cfg_repeat.offset_time = high_cnt_cw;//配置互补形成相位 + + uapi_pwm_open(channel_id_ww, &cfg_repeat); + // 设置PWM组 + uint8_t channel_ids[2] = {channel_id_cw, channel_id_ww}; + // uapi_pwm_set_group(CONFIG_PWM_GROUP_ID, channel_ids, 2); + + // // 更新PWM占空比 + // uapi_pwm_update_duty_ratio(channel_id_cw, low_cnt_cw, high_cnt_cw); + // uapi_pwm_update_duty_ratio(channel_id_ww, low_cnt_ww, high_cnt_ww); + + // 启动PWM组 + uapi_pwm_start_group(CONFIG_PWM_GROUP_ID); + if (should_print(&pwm_limiter)) { + e_printf("cw:high:%u, low:%u, duty:%u/1000, ww:high:%u, low:%u, duty:%u/1000, offset_time:%u\r\n", + high_cnt_cw, low_cnt_cw, g_device_control.duty_cw, high_cnt_ww, low_cnt_ww, g_device_control.duty_ww, cfg_repeat.offset_time); + } +} + +#define ABS(x) ((x) < 0 ? -(x) : (x)) + +// 计算渐变步长 +static void calculate_fade_steps(void) +{ + // 从g_device_control获取渐变时间 + fade_ctx.smooth_time_us = g_device_control.fade_time * 1000 * 1000;//s->us + e_printf("fade brightness: curr:%d, target:%d, cct: curr:%d, target:%d\r\n", + fade_ctx.current_brightness, fade_ctx.target_brightness, fade_ctx.current_cct, fade_ctx.target_cct); + // 计算亮度渐变步长 + int32_t brightness_diff = fade_ctx.target_brightness - fade_ctx.current_brightness; + int32_t cct_diff = fade_ctx.target_cct - fade_ctx.current_cct; + + // 计算需要的总步数(取亮度和色温中变化较大的那个) + uint32_t max_diff = (ABS(brightness_diff) > ABS(cct_diff)) ? ABS(brightness_diff) : ABS(cct_diff); + uint32_t min_steps = 0; + uint32_t available_interval = 0; + + // 如果变化太小,直接设置目标值 EKKO:这里需要确认是直接设置目标值还是强制等待渐变时长 + if (max_diff == 0) { + fade_ctx.step_brightness = 0; + fade_ctx.step_cct = 0; + fade_ctx.update_interval = fade_ctx.smooth_time_us; + goto lab_exit; + } + + min_steps = max_diff; + + // 计算实际可用的时间间隔 + available_interval = fade_ctx.smooth_time_us / min_steps; + + // 如果时间间隔太小(小于允许的最小更新间隔),则增加步长 + if (available_interval < FADE_INTERVAL_MIN) { + // 重新计算步长,确保在指定时间内完成 + fade_ctx.update_interval = FADE_INTERVAL_MIN; + int32_t actual_steps = fade_ctx.smooth_time_us / fade_ctx.update_interval; + + // 计算新的步长 + fade_ctx.step_brightness = brightness_diff / actual_steps; + fade_ctx.step_cct = cct_diff / actual_steps; + + // 确保至少有一个最小步长 + if (fade_ctx.step_brightness == 0 && brightness_diff != 0) { + fade_ctx.step_brightness = (brightness_diff > 0) ? 1 : -1; + } + if (fade_ctx.step_cct == 0 && cct_diff != 0) { + fade_ctx.step_cct = (cct_diff > 0) ? 1 : -1; + } + } else { + // 使用计算出的时间间隔 + fade_ctx.update_interval = available_interval; + fade_ctx.step_brightness = (brightness_diff > 0) ? 1 : -1; + fade_ctx.step_cct = (cct_diff > 0) ? 1 : -1; + } + // 如果亮度或色温没有变化,则步长为0 + if (brightness_diff == 0) { + fade_ctx.step_brightness = 0; + } + if (cct_diff == 0) { + fade_ctx.step_cct = 0; + } +lab_exit: + if (should_print(&fade_ctx.print_limiter)) { + e_printf("max_diff:%d, fade time:%uus, Brightness: diff:%d, step:%d, CCT: diff:%d, step:%d, update_interval:%uus\r\n", + max_diff, fade_ctx.smooth_time_us, brightness_diff, fade_ctx.step_brightness, cct_diff, fade_ctx.step_cct, fade_ctx.update_interval); + } +} + +static void cancel_current_light_fade(void) +{ + uapi_timer_stop(fade_ctx.timer_handle); +} + +// 计算校验和 +static uint32_t calculate_checksum(const device_data_t* data) +{ + uint32_t sum = 0; + const uint32_t* ptr = (const uint32_t*)(((char*)data) + sizeof(data->checksum)); + size_t len = sizeof(device_data_t) - sizeof(data->checksum); // 减去checksum字段的大小 + + for (size_t i = 0; i < len / 4; i++) { + sum ^= ptr[i]; + } + return sum; +} + +// 验证数据有效性 +static bool verify_device_data(const device_data_t* data) +{ + if (data->magic != DEVICE_DATA_MAGIC) { + e_printf("magic error:%x\n", data->magic); + return false; + } + + if (data->version > DEVICE_DATA_VERSION) { + e_printf("versoin missmatch:%x\n", data->version); + return false; + } + + uint32_t checksum = calculate_checksum(data); + if (checksum != data->checksum) { + e_printf("check sum error:%x\n", data->version); + return false; + } + + return true; +} + +// 从指定地址读取设备数据 +static bool read_device_data_from_addr(uint32_t addr, device_data_t* data) +{ + int ret = hfuflash_read(addr, (char*)data, sizeof(device_data_t)); + if (ret != sizeof(device_data_t)) { + e_printf("从地址0x%x读取设备数据失败,实际读取长度:%d\r\n", addr, ret); + return false; + } + + if (!verify_device_data(data)) { + e_printf("地址0x%x的数据无效\r\n", addr); + return false; + } + + return true; +} + +// 写入数据到指定地址 +static bool write_device_data_to_addr(uint32_t addr, const device_data_t* data) +{ + int ret = hfuflash_erase_page(addr, 1); + if (ret != HF_SUCCESS) { + e_printf("擦除地址0x%x的Flash页失败,错误码:%d\r\n", addr, ret); + return false; + } + + ret = hfuflash_write(addr, (char*)data, sizeof(device_data_t)); + if (ret != sizeof(device_data_t)) { + e_printf("写入数据到地址0x%x失败,实际写入长度:%d\r\n", addr, ret); + return false; + } + + return true; +} + +void read_device_data(void) +{ +#if SUPPORT_SAVE_TO_FLASH + device_data_t data; + bool main_valid = false; + bool backup_valid = false; + + // 尝试读取主数据区 + main_valid = read_device_data_from_addr(DEVICE_DATA_FLASH_ADDR, &data); + + // 如果主数据区无效,尝试读取备份区 + if (!main_valid) { + backup_valid = read_device_data_from_addr(DEVICE_DATA_BACKUP_ADDR, &data); + } + + if (!main_valid && backup_valid) { // 如果主数据区无效但备份区有效,从备份区恢复 + e_printf("从备份区恢复数据\r\n"); + write_device_data_to_addr(DEVICE_DATA_FLASH_ADDR, &data); + } else if (!main_valid && !backup_valid) { // 如果两个区域都无效,使用默认值 + e_printf("主数据和备份数据都无效,使用默认值\r\n"); + // 只打印g_device_control的当前值,不打印data.control + // e_printf("读取完成状态: %d\r\n", g_device_control.read_done); + e_printf("上电次数: %d\r\n", g_device_control.power_on_cnt); + e_printf("配网状态: %d\r\n", g_device_control.is_networked); + e_printf("开关状态: %d\r\n", g_device_control.on); + e_printf("灯光模式: %d\r\n", g_device_control.elightMode); + e_printf("亮度: %d\r\n", g_device_control.brightness_local); + e_printf("色温: %d\r\n", g_device_control.cct_local); + e_printf("冷白LED: %d\r\n", g_device_control.duty_cw); + e_printf("暖白LED: %d\r\n", g_device_control.duty_ww); + e_printf("渐变时间: %d\r\n", g_device_control.fade_time); + goto lab_exit; + } + // 更新设备控制状态 + e_printf("设备状态恢复:\r\n"); + // e_printf("读取完成状态: %d => %d\r\n", g_device_control.read_done, data.control.read_done); + e_printf("上电次数: %d => %d\r\n", g_device_control.power_on_cnt, data.control.power_on_cnt); + e_printf("配网状态: %d => %d\r\n", g_device_control.is_networked, data.control.is_networked); + e_printf("开关状态: %d => %d\r\n", g_device_control.on, data.control.on); + e_printf("灯光模式: %d => %d\r\n", g_device_control.elightMode, data.control.elightMode); + e_printf("亮度: %d => %d\r\n", g_device_control.brightness_local, data.control.brightness_local); + e_printf("色温: %d => %d\r\n", g_device_control.cct_local, data.control.cct_local); + e_printf("冷白LED: %d => %d\r\n", g_device_control.duty_cw, data.control.duty_cw); + e_printf("暖白LED: %d => %d\r\n", g_device_control.duty_ww, data.control.duty_ww); + e_printf("渐变时间: %d => %d\r\n", g_device_control.fade_time, data.control.fade_time); + + g_device_control = data.control; +lab_exit: +#endif + g_device_control.read_done = true; + e_printf("读取设备数据成功\r\n"); +} +void save_device_data(void) +{ + e_printf("Starting to save device data\n"); +#if SUPPORT_SAVE_TO_FLASH + // 使用静态变量保存上一次的数据状态 + static device_control_t last_control = {0}; + static bool is_first_save = true; + + // 需要比较的字段(根据注释标记的持久化维持部分) + bool need_save = is_first_save || + last_control.on != g_device_control.on || + last_control.elightMode != g_device_control.elightMode || + last_control.brightness_local != g_device_control.brightness_local || + last_control.fade_time != g_device_control.fade_time || + last_control.cct_local != g_device_control.cct_local || + last_control.power_on_cnt != g_device_control.power_on_cnt || + last_control.is_networked != g_device_control.is_networked; + + if (!need_save) { + e_printf("No changes detected, skip saving\n"); + return; + } + + device_data_t data; + + // 准备要保存的数据 + data.magic = DEVICE_DATA_MAGIC; + data.version = DEVICE_DATA_VERSION; + data.control = g_device_control; + data.checksum = calculate_checksum(&data); + + // 先写入备份区 + if (!write_device_data_to_addr(DEVICE_DATA_BACKUP_ADDR, &data)) { + e_printf("Failed to write backup data\n"); + return; + } + + // 再写入主数据区 + if (!write_device_data_to_addr(DEVICE_DATA_FLASH_ADDR, &data)) { + e_printf("Failed to write main data, but backup data saved\n"); + } + + // 更新上一次的数据状态 + last_control = g_device_control; + is_first_save = false; + + e_printf("Device data saved successfully\n"); +#endif + e_printf("Save device data done\n"); +} + +void req_save_device_data(void) +{ + osal_sem_up(&save_data_sem); +} + +// 保存任务函数 +static void *save_data_task(const char *arg) +{ + e_printf("[save_data_task] Save task started\n"); + + while (save_task_running) { + // 等待保存信号 + if (osal_sem_down_timeout(&save_data_sem, 100*1000) != OSAL_SUCCESS) { + continue; + } + while(OSAL_SUCCESS == osal_sem_trydown(&save_data_sem));//消耗所有的信号一次性刷入 + save_device_data(); + } + + e_printf("[save_data_task] Save task exited\n"); + return NULL; +} + +// 初始化保存任务 +static void init_save_task(void) +{ + if (save_task_handle != NULL) { + return; + } + + // 初始化信号量 + if (osal_sem_init(&save_data_sem, 0) != OSAL_SUCCESS) { + e_printf("[init_save_task] Failed to init semaphore\n"); + return; + } + + // 创建保存任务 + osal_kthread_lock(); + save_task_handle = osal_kthread_create((osal_kthread_handler)save_data_task, NULL, "SaveDataTask", + SAVE_TASK_STACK_SIZE); + if (save_task_handle != NULL) { + save_task_running = true; + osal_kthread_set_priority(save_task_handle, SAVE_TASK_PRIO); + e_printf("[init_save_task] Save task created successfully\n"); + } else { + e_printf("[init_save_task] Failed to create save task\n"); + osal_sem_destroy(&save_data_sem); + } + osal_kthread_unlock(); +} + +// 停止保存任务 +static void stop_save_task(void) +{ + if (save_task_handle == NULL) { + return; + } + + save_task_running = false; + osal_sem_up(&save_data_sem); // 唤醒任务以使其退出 + + osal_kthread_lock(); + osal_kthread_destroy(save_task_handle, 0); + save_task_handle = NULL; + osal_kthread_unlock(); + + osal_sem_destroy(&save_data_sem); + + e_printf("[stop_save_task] Save task stopped\n"); +} + +// 设置灯光状态(统一控制函数) +int set_light(int32_t brightness_local, int32_t cct_local) +{ + // 如果任一参数大于等于0,则更新对应值 + e_printf("ligMode:%d, local brightness: curr:%d, target:%d, local cct: curr:%d, target:%d\r\n", + g_device_control.elightMode, g_device_control.brightness_local, brightness_local, g_device_control.cct_local, cct_local); + + fade_ctx.current_brightness = g_device_control.brightness_local; + fade_ctx.current_cct = g_device_control.cct_local; + fade_ctx.target_brightness = fade_ctx.current_brightness ; + fade_ctx.target_cct = fade_ctx.current_cct; + + if (brightness_local >= 0) { + BRIGHTNESS_LITME_RANGE(brightness_local); + + if (g_device_control.elightMode == LIGHT_MODE_CUSTOMER) { + fade_ctx.target_brightness = brightness_local; + } else { + g_device_control.brightness_local = brightness_local; + } + } + + if (cct_local >= 0) { + CCT_LITME_RANGE(cct_local); + + if (g_device_control.elightMode == LIGHT_MODE_CUSTOMER) { + fade_ctx.target_cct = cct_local; + } else { + g_device_control.cct_local = cct_local; + } + } + + switch (g_device_control.elightMode) + { + case LIGHT_MODE_CUSTOMER: + if (g_device_control.fade_time) { + fade_ctx.is_fading = true; + fade_ctx.fade_completed = false; + // 计算渐变步长 + calculate_fade_steps(); + e_printf("start fade\r\n"); + // 启动渐变定时器 + set_switch(true); + uapi_timer_start(fade_ctx.timer_handle, fade_ctx.update_interval, fade_timer_callback, 0); + #if 0 + 等待渐变完成 + while (!fade_ctx.fade_completed) { + msleep(10); + } + return 0; // 同步上报 + #else + return -111; // 异步上报 + #endif + } + // no break; // 如果渐变时间是0则直接一步完成 + default: //情景模式直接切换到对应的色温和亮度 + set_switch(true); + } + start_report_task(report_device_online_status);//直接全量上报 + return -111; // 异步上报 +} + +// 设置开关状态 +int set_switch(bool open) +{ + g_device_control.on = open; + if (fade_ctx.is_fading) //打断当前的渐变计划 + { + cancel_current_light_fade(); + } + calculate_pwm_duty(); + update_pwm_output(); + uapi_gpio_set_val(SWITCH_PIN, open ? OPEN_LIGHT : CLOSE_LIGHT); + + // 保存设备状态 + req_save_device_data(); + return 0; } static void gpio_init(pin_t pin) { - /* PINCTRL init. */ + /* PINCTRL init. */ uapi_pin_init(); uapi_pin_set_mode(pin, HAL_PIO_FUNC_GPIO); uapi_gpio_set_dir(pin, GPIO_DIRECTION_OUTPUT); uapi_pin_set_ds(pin, PIN_DS_3); uapi_pin_set_pull(pin, PIN_PULL_TYPE_DOWN); - uapi_gpio_set_val(pin, GPIO_LEVEL_LOW); + uapi_gpio_set_val(pin, CLOSE_LIGHT); } -static void pwm_sys_init() { +static void pwm_sys_init(void) { uapi_pwm_deinit(); +} + +static void pwm_init(pin_t pin, pin_t pin1) +{ + pwm_config_t cfg_repeat = {0}; + cfg_repeat.repeat = true; + uapi_pin_set_mode(pin, PIN_MODE_1); + uapi_pin_set_mode(pin1, PIN_MODE_1); uapi_pwm_init(); -} - -static void pwm_init(pin_t pin, pin_t pin1, uint8_t channel, uint channel1) -{ - pwm_config_t cfg_no_repeat = { - 0, - 0, - 0, - 0, - true - }; - uint32_t frequency = uapi_pwm_get_frequency(channel); - e_printf("pwm基础时钟频率为: %dHZ\r\n", frequency); - - // 计算1KHz PWM所需的周期值(高电平+低电平) - uint32_t total_period = frequency / 1000; - // 50%占空比,高低电平时间相等 - cfg_no_repeat.high_time = total_period / 2; - cfg_no_repeat.low_time = total_period / 2; - - e_printf("设置1KHz PWM,50%%占空比,周期值: %d\r\n", total_period); - uapi_pin_set_mode(pin, PIN_MODE_3); //set pin as pwm output - uapi_pin_set_mode(pin1, PIN_MODE_3); //set pin as pwm output - uapi_pwm_open(channel, &cfg_no_repeat); - uapi_pwm_open(channel1, &cfg_no_repeat); - - // uapi_tcxo_delay_ms((uint32_t)TEST_TCXO_DELAY_1000MS); - // uapi_pwm_unregister_interrupt(channel); - // uapi_pwm_register_interrupt(channel, pwm_sample_callback); -#ifdef CONFIG_PWM_USING_V151 - uint8_t channel_id[2] = {channel, channel1}; - /* channel_id can also choose to configure multiple channels, and the third parameter also needs to be adjusted - accordingly. */ - uapi_pwm_set_group(CONFIG_PWM_GROUP_ID, channel_id, 2); -#else - uapi_pwm_start(channel); -#endif - -} -void pwm_start() -{ -#ifdef CONFIG_PWM_USING_V151 - /* Here you can also call the uapi_pwm_start interface to open each channel individually. */ + uint32_t frequency = uapi_pwm_get_frequency(pin%8); + // uint32_t pwm_base_period_ns = 1000 * 1000 * 1000 / frequency; + // uint32_t pwm_base_period_ns = 1000 * 1000 * 1000 / (80 * 1000 * 1000);// 80MHZ + // uint32_t pwm_target_period_ns = 1000 * 1000 * 1000 / PWM_FREQUENCY; + pwm_period_cnt = ((80 * 1000 * 1000) / PWM_FREQUENCY); + // 设置PWM组 + channel_id_cw = pin%8; + channel_id_ww = pin1%8; + uint8_t channel_ids[2] = {channel_id_cw, channel_id_ww}; + uapi_pwm_open(channel_id_cw, &cfg_repeat); + uapi_pwm_open(channel_id_ww, &cfg_repeat); + uapi_pwm_set_group(CONFIG_PWM_GROUP_ID, channel_ids, sizeof(channel_ids)); uapi_pwm_start_group(CONFIG_PWM_GROUP_ID); -#endif + e_printf("PWM基础时钟频率: %dHz, 目标时钟频率: %dHz, 周期计数: %d\r\n", frequency, PWM_FREQUENCY, pwm_period_cnt); } -void pwm_sys_deinit(void) + +// 配网状态管理相关函数 +static void handle_network_status(void) { - // uapi_tcxo_delay_ms((uint32_t)TEST_TCXO_DELAY_1000MS); -#ifdef CONFIG_PWM_USING_V151 - uapi_pwm_close(CONFIG_PWM_GROUP_ID); -#else - uapi_pwm_close(CONFIG_PWM_CHANNEL); -#endif - - // uapi_tcxo_delay_ms((uint32_t)TEST_TCXO_DELAY_1000MS); - uapi_pwm_deinit(); -} - -static void pwm_entry(void) -{ - pwm_sys_init(); - pwm_init(BRIGHTNESS_PIN, BRIGHTNESS_PWM_CHANNEL, CCT_PIN, CCT_PWM_CHANNEL); - pwm_start(); -} - - -int spotlight_main(void) { - gpio_init(SWITCH_PIN); - pwm_entry(); - - while (1) { - e_printf("spotlight_main\r\n"); - uapi_gpio_toggle(SWITCH_PIN); - msleep(1000); - + // 如果已经配网,直接返回 + if (g_device_control.is_networked) { + return; } + + // 增加上电计数 + g_device_control.power_on_cnt++; + e_printf("现在是第 %d 次上电\r\n", g_device_control.power_on_cnt); + // 检查是否需要进入配网状态 + if (g_device_control.power_on_cnt >= NET_CFG_ENTRY_CNT) { + e_printf("进入配网状态,上电次数:%d\r\n", g_device_control.power_on_cnt); + g_device_control.power_on_cnt = 0;// + extern int start_hilink_ble_net_config(int32_t net_cfg_time_s); + int ret = start_hilink_ble_net_config(NET_CFG_TOTAL_TIMEOUT/1000); + if (ret) { + // FIXME: 这里简单恢复之前等待配网的灯效即可 + e_printf("start net_cfg fail:%n\r\n", ret); + req_save_device_data(); + // extern int HILINK_RestoreFactorySettings(void); + // HILINK_RestoreFactorySettings(); + return; + } + // 初始化呼吸灯控制 + init_breath_ctx(); + start_breath_timer(); + } + + // 保存设备状态 + req_save_device_data(); +} + + +// 设备上线时的状态上报 +static void report_device_online_status(void) +{ + e_printf("[report_task] Starting to report all service status\r\n"); + fast_report(SVC_ID_SWITCH); + fast_report(SVC_ID_BRIGHTNESS); + fast_report(SVC_ID_CCT); + fast_report(SVC_ID_LIGHT_MODE); + fast_report(SVC_ID_FADE_TIME); + e_printf("[report_task] Status report completed\r\n"); +} + +// 渐变结束时的状态上报 +static void report_fade_complete_status(void) +{ + req_save_device_data(); + e_printf("[report_task] Reporting fade complete status\r\n"); + fast_report(SVC_ID_LIGHT_MODE); + fast_report(SVC_ID_BRIGHTNESS); + fast_report(SVC_ID_CCT); + e_printf("[report_task] Fade status report completed\r\n"); +} + +// 状态上报任务函数 +static void *report_task(const char *arg) +{ + report_func_t report_func = (report_func_t)arg; + e_printf("report_task running...\r\n"); + while (!(g_device_control.read_done && hf_hilink_main_is_runing())) { + // 数据已经正确load并且hilink已经运行才开始上报 + e_printf("read_done%d, hilink:%d\r\n", g_device_control.read_done, hf_hilink_main_is_runing()); + osal_msleep(50); + } + if (report_func) + { + report_func(); + } + report_task_running = false; + e_printf("[report_task] exited\r\n"); + return NULL; +} + +// 启动状态上报任务 +static void start_report_task(report_func_t report_func) +{ + if (report_task_running) { + e_printf("[report_task] Previous task is still running, skip this report\r\n"); + return; + } + if (report_task_handle) { + osal_kthread_destroy(report_task_handle, 0); + report_task_handle = NULL; + } + + e_printf("start report_task\r\n"); + osal_kthread_lock(); + report_task_handle = osal_kthread_create((osal_kthread_handler)report_task, (void*)report_func, "ReportStaTask", + REPORT_TASK_STACK_SIZE); + if (report_task_handle != NULL) { + report_task_running = true; // 设置任务运行标志 + osal_kthread_set_priority(report_task_handle, REPORT_TASK_PRIO); + } + osal_kthread_unlock(); +} + +static int device_online = 0; +// 修改handle_device_online函数 +void handle_device_online(void) +{ + if(device_online) + { + return; + } + e_printf("device online!\r\n"); + if (!g_device_control.is_networked) { + e_printf("设备首次上线,记录配网状态\r\n"); + g_device_control.is_networked = true; + g_device_control.power_on_cnt = 0; // 重置上电计数 + stop_breath_timer(); // 停止呼吸灯 + set_light2net_cfg_done(); + req_save_device_data(); + } + // 重置配网状态 + g_net_breath_state = NET_CFG_IDLE; + g_net_cfg_start_time = 0; + // 启动状态上报任务,使用设备上线状态上报函数 + start_report_task(report_device_online_status); + device_online = 1; +} + +void handle_device_offline(void) +{ + if (!device_online) { + return; + } + device_online = 0; + e_printf("device offline!\r\n"); +} +// 处理设备解绑 +void handle_device_unbind(void) +{ + e_printf("设备被解绑,重置配网状态\r\n"); + if (g_device_control.is_networked) { + g_device_control.is_networked = false; + g_device_control.power_on_cnt = 0; // 重置上电计数 + stop_spotlight_main_task(); + device_control_t tmp = DEFAULT_DEVICE_DATA;//恢复默认 + g_device_control = tmp; + save_device_data(); + } +} + +static void check_net_cfg_power_on_keep_time(void) +{ + if (g_device_control.power_on_cnt <= 0) { + return; + } + if (g_device_control.is_networked) { + return; + } + if (uapi_systick_get_ms() > INIT_NET_CFG_PWOER_ON_KEEP_TIME) { + e_printf("power_on keep time is %llums must < %ds, we will reset cnt\r\n", uapi_systick_get_ms(), INIT_NET_CFG_PWOER_ON_KEEP_TIME/1000); + g_device_control.power_on_cnt = 0; + req_save_device_data(); + } +} + +// spotlight主任务相关变量 +#define SPOTLIGHT_MAIN_TASK_PRIO 23 +#define SPOTLIGHT_MAIN_TASK_STACK_SIZE 0x1000 +static osal_task *spotlight_main_task_handle = NULL; +static bool spotlight_main_task_running = false; +// spotlight主任务函数 +static void *spotlight_main_task(const char *arg) +{ + unused(arg); + e_printf("[spotlight_main_task] Task started\r\n"); + uint32_t current_time = 0; //uapi_systick_get_ms(); + while (spotlight_main_task_running) { + // 检查配网超时 + check_net_cfg_timeout(); + check_net_cfg_power_on_keep_time(); + osal_msleep(20); + } + + // 停止保存任务 + stop_save_task(); + e_printf("[spotlight_main_task] Task exited\r\n"); + return NULL; +} + +// 启动spotlight主任务 +static void start_spotlight_main_task(void) +{ + if (spotlight_main_task_handle != NULL) { + e_printf("[start spotlight task] Task already running\r\n"); + return; + } + + e_printf("[start spotlight task] Starting task\r\n"); + osal_kthread_lock(); + spotlight_main_task_handle = osal_kthread_create((osal_kthread_handler)spotlight_main_task, NULL, "SpotlightMain", + SPOTLIGHT_MAIN_TASK_STACK_SIZE); + if (spotlight_main_task_handle != NULL) { + spotlight_main_task_running = true; + osal_kthread_set_priority(spotlight_main_task_handle, SPOTLIGHT_MAIN_TASK_PRIO); + e_printf("[start spotlight task] Task created successfully\r\n"); + } else { + e_printf("[start spotlight task] Failed to create task\r\n"); + } + osal_kthread_unlock(); +} + +// 停止spotlight主任务 +static void stop_spotlight_main_task(void) +{ + if (spotlight_main_task_handle == NULL) { + return; + } + + spotlight_main_task_running = false; + + osal_kthread_lock(); + osal_kthread_destroy(spotlight_main_task_handle, 0); + spotlight_main_task_handle = NULL; + osal_kthread_unlock(); + + e_printf("[stop_spotlight_main_task] Task stopped\r\n"); +} + +// 检查配网超时 +static void check_net_cfg_timeout(void) +{ + if (g_net_breath_state != NET_CFG_BREATHING) { + return; + } + uint64_t current_time = uapi_systick_get_ms(); + uint64_t elapsed_time = current_time - g_net_cfg_start_time; + + if (elapsed_time >= NET_CFG_TOTAL_TIMEOUT) { + e_printf("[Net Cfg] 配网超过10分钟,退出配网模式\r\n"); + g_net_breath_state = NET_CFG_TIMEOUT; + } +} +// 修改spotlight_main函数 +int spotlight_main(void) { + uapi_systick_init(); + read_device_data(); + e_printf("uapi_timer_get_max_us:%uus\r\n", uapi_timer_get_max_us()); + + // 初始化GPIO并将灯关闭 + gpio_init(SWITCH_PIN); + // 初始化PWM系统 + pwm_sys_init(); + pwm_init(BRIGHTNESS_PIN, CCT_PIN); + + // 初始化渐变控制 + init_fade_ctx(); + + // 初始化保存任务 + init_save_task(); + + // 检查配网状态,确定是否需要进入配网状态 + handle_network_status(); + + set_switch(g_device_control.on);//按照当前值更新light + + start_spotlight_main_task(); return 0; } +// 设置渐变时长 +int set_smooth_time(uint32_t smooth_time) +{ + e_printf("[set_smooth_time] Setting smooth time: %d s\r\n", smooth_time); + if (smooth_time < SMOOTH_TIME_MIN) { + e_printf("[set_smooth_time] Smooth time below minimum, adjusted to: %d s\r\n", SMOOTH_TIME_MIN); + smooth_time = SMOOTH_TIME_MIN; + } + if (smooth_time > SMOOTH_TIME_MAX) { + e_printf("[set_smooth_time] Smooth time above maximum, adjusted to: %d s\r\n", SMOOTH_TIME_MAX); + smooth_time = SMOOTH_TIME_MAX; + } + + g_device_control.fade_time = smooth_time; + req_save_device_data(); + e_printf("[set_smooth_time] Smooth time setting completed\r\n"); + return 0; +} + +// 设置灯光到配网超时状态 +static void set_light2breathtimeout(void) +{ + e_printf("[Net cfg] 配网超过1分钟,退出呼吸灯状态\r\n"); + // 恢复到自定义模式 + g_device_control.brightness_local = BRIGHTNESS_REMOTE2LOCAL(NET_CFG_TIMEOUT_BRIGHTNESS); + g_device_control.cct_local = CCT_REMOTE2LOCAL(NET_CFG_TIMEOUT_CCT); + calculate_pwm_duty(); + update_pwm_output(); +} + +// 设置灯光到配网完成状态 +static void set_light2net_cfg_done(void) +{ + g_device_control.elightMode = NET_CFG_DEFAULT_LIGHTMODE; + g_device_control.brightness_local = BRIGHTNESS_REMOTE2LOCAL(NET_CFG_DEFAULT_BRIGHTNESS); + g_device_control.cct_local = CCT_REMOTE2LOCAL(NET_CFG_DEFAULT_CCT); + g_device_control.fade_time = NET_CFG_DEFAULT_FADE_TIME; + calculate_pwm_duty(); + update_pwm_output(); +} + +// 启动呼吸灯 +static void start_breath_timer(void) +{ + g_net_breath_state = NET_CFG_BREATHING; + g_net_cfg_start_time = uapi_systick_get_ms(); + breath_ctx.is_initial_breath = true; + uapi_timer_start(breath_ctx.timer_handle, breath_ctx.update_interval, breath_timer_callback, 0); +} + diff --git a/application/ws63/user_main/spotlight/spotlight_ut.c b/application/ws63/user_main/spotlight/spotlight_ut.c new file mode 100644 index 0000000..421a261 --- /dev/null +++ b/application/ws63/user_main/spotlight/spotlight_ut.c @@ -0,0 +1,116 @@ +#include "hsf.h" +#include "spotlight.h" +#include "pinctrl.h" +#include "gpio.h" +#include "pinctrl.h" +#include "pwm.h" +#include "timer.h" +#include "soc_osal.h" +#include "systick.h" + +extern device_control_t g_device_control; +extern void update_pwm_output(void); +extern void calculate_pwm_duty(void); + +// AT指令:设置PWM占空比 +// 格式:AT+PWM=, +// channel: 0-冷白LED, 1-暖白LED +// duty: 0-1000 +int hf_atcmd_pwm(pat_session_t s, int argc, char *argv[], char *rsp, int len) +{ + if (argc != 2) { + e_printf(rsp, "ERROR: Invalid parameters. Usage: AT+PWM=,"); + return -1; + } + + int channel = atoi(argv[0]); + int duty = atoi(argv[1]); + + // 参数检查 + if (channel < 0 || channel > 1) { + e_printf(rsp, "ERROR: Invalid channel. Must be 0(CW) or 1(WW)"); + return -1; + } + + if (duty < 0 || duty > PWM_DUTY_RATIO_MAX) { + e_printf(rsp, "ERROR: Invalid duty. Must be 0-%d", PWM_DUTY_RATIO_MAX); + return -1; + } + + // 设置PWM占空比 + if (channel == 0) { + g_device_control.duty_cw = duty; + } else { + g_device_control.duty_ww = duty; + } + + // 更新PWM输出 + update_pwm_output(); + + sprintf(rsp, "OK: Channel %d PWM duty set to %d", channel, duty); + return 0; +} + +// AT指令:获取当前PWM占空比 +// 格式:AT+PWM? +int hf_atcmd_pwm_query(pat_session_t s, int argc, char *argv[], char *rsp, int len) +{ + sprintf(rsp, "+PWM: CW=%d,WW=%d", g_device_control.duty_cw, g_device_control.duty_ww); + return 0; +} + +// AT指令:直接设置色温和亮度 +// 格式:AT+LIGHT=, +// brightness: 0-1000 +// cct: 2700-6500 +int hf_atcmd_light(pat_session_t s, int argc, char *argv[], char *rsp, int len) +{ + if (argc != 2) { + e_printf(rsp, "ERROR: Invalid parameters. Usage: AT+LIGHT=,"); + return -1; + } + + int brightness = atoi(argv[0]); + int cct = atoi(argv[1]); + + // 参数检查 + if (brightness < 0 || brightness > BRIGHTNESS_MAX) { + e_printf(rsp, "ERROR: Invalid brightness. Must be 0-%d", BRIGHTNESS_MAX); + return -1; + } + + if (cct < CCT_MIN || cct > CCT_MAX) { + e_printf(rsp, "ERROR: Invalid CCT. Must be %d-%d", CCT_MIN, CCT_MAX); + return -1; + } + + // 设置亮度和色温 + g_device_control.brightness_local = BRIGHTNESS_REMOTE2LOCAL(brightness); + g_device_control.cct_local = CCT_REMOTE2LOCAL(cct); + + // 计算PWM占空比并更新输出 + calculate_pwm_duty(); + update_pwm_output(); + + sprintf(rsp, "OK: Brightness=%d, CCT=%d, CW=%d%%%%, WW=%d%%%%", + brightness, cct, g_device_control.duty_cw, g_device_control.duty_ww); + return 0; +} + +// AT指令:获取当前亮度和色温 +// 格式:AT+LIGHT? +int hf_atcmd_light_query(pat_session_t s, int argc, char *argv[], char *rsp, int len) +{ + sprintf(rsp, "+LIGHT: Brightness=%d, CCT=%d, CW=%d, WW=%d", + g_device_control.brightness_local, + g_device_control.cct_local, + g_device_control.duty_cw, + g_device_control.duty_ww); + return 0; +} + +// 注册AT指令 +const hfat_cmd_t spotlight_ut_cmds_table[] = { + + {NULL, NULL, NULL, NULL} +}; \ 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 18d9172..4a2841f 100755 --- a/build/config/target_config/ws63/config.py +++ b/build/config/target_config/ws63/config.py @@ -265,7 +265,7 @@ target = { }, 'ws63-liteos-app-iot': { 'base_target_name': 'target_ws63_app_rom_template', - # 'liteos_kconfig': 'ws63_iot', + 'liteos_kconfig': 'ws63_iot', # EKKO remove indie upgrade 'os': 'liteos', 'defines': [ "USE_CMSIS_OS", @@ -308,9 +308,12 @@ 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_SPOTLIGHT_UT", "CONFIG_DHCPS_GW", "_HSF_", + # "DEBUG_LOG_ENABLE", # EKKO ADD + "CONFIG_PWM_USING_V150", #EKKO ADD # "ENABLE_BLE_SCAN" #open ble scan # "HF_MCU_OTA" #open mcu ota ], @@ -354,10 +357,10 @@ target = { 'cjson', 'xo_trim_port', 'hilink', - 'app_addr_map', - # 'hilinkdevicesdk', - # 'hilinkota', - # 'hilinkbtsdk', + # 'app_addr_map', # EKKO add for remove indie upgrade + 'hilinkdevicesdk', # EKKO remove indie upgrade + 'hilinkota', # EKKO remove indie upgrade + 'hilinkbtsdk', # EKKO remove indie upgrade 'huks_sdk', 'deviceauth', 'little_fs', 'littlefs_adapt_ws63', diff --git a/build/config/target_config/ws63/menuconfig/acore/ws63_liteos_app_iot.config b/build/config/target_config/ws63/menuconfig/acore/ws63_liteos_app_iot.config old mode 100755 new mode 100644 index b538f72..2efe4d0 --- a/build/config/target_config/ws63/menuconfig/acore/ws63_liteos_app_iot.config +++ b/build/config/target_config/ws63/menuconfig/acore/ws63_liteos_app_iot.config @@ -258,6 +258,7 @@ CONFIG_DRIVER_SUPPORT_PWM=y CONFIG_PWM_USING_V151=y # CONFIG_PWM_USING_V150 is not set CONFIG_PWM_GROUP_NUM=8 +CONFIG_PWM_CHANNEL_NUM=8 # end of PWM Configuration # CONFIG_DRIVER_SUPPORT_HASH is not set # CONFIG_DRIVER_SUPPORT_MPU is not set diff --git a/compile_commands.json b/compile_commands.json new file mode 120000 index 0000000..93e3330 --- /dev/null +++ b/compile_commands.json @@ -0,0 +1 @@ +output/ws63/acore/ws63-liteos-app-iot/compile_commands.json \ No newline at end of file diff --git a/indie_build.py b/indie_build.py index 1e065f6..6826583 100755 --- a/indie_build.py +++ b/indie_build.py @@ -149,6 +149,9 @@ def all_build(): print("make full pkg err:", errcode) exit(-1) shutil.rmtree(temp_dir) + print("gen package.zip") + file_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "package.py") + os.system("cd " + file_dir + " && python3 package.py") def sdk_build(): builder = CMakeBuilder(["-c", "ws63-liteos-hilink"]) diff --git a/package.py b/package.py new file mode 100755 index 0000000..315ebae --- /dev/null +++ b/package.py @@ -0,0 +1,61 @@ +from hashlib import sha256 +import json +import os +import shutil +import zipfile + +# 获取当前目录 +current_dir = os.path.dirname(os.path.abspath(__file__)) + +# 构建输出目录路径 +package_dir = os.path.join(current_dir, "output", "package") + +shutil.rmtree(package_dir, ignore_errors=True) +shutil.rmtree(os.path.join(current_dir, "output", "package.zip"), ignore_errors=True) + +# 确保输出目录存在 +os.makedirs(package_dir, exist_ok=True) + +# 复制文件 +shutil.copy(os.path.join(current_dir, "output", "LPT262_hilink_UPGRADE.bin"), + os.path.join(package_dir, "image2_all_ota1.bin")) +shutil.copy(os.path.join(current_dir, "output", "LPT262_hilink_UPGRADE.bin"), + os.path.join(package_dir, "image2_all_ota2.bin")) + +print("文件已复制到输出目录") + +json_file = os.path.join(current_dir, "output", "package", "filelist.json") +json_data = {} + +def calc_sha256(filepath): + h = sha256() + with open(filepath, "rb") as f: + for chunk in iter(lambda: f.read(4096), b""): + h.update(chunk) + return h.hexdigest() + +file1 = os.path.join(package_dir, "image2_all_ota1.bin") +file2 = os.path.join(package_dir, "image2_all_ota2.bin") + +json_data['image2_all_ota1.bin'] = {} +json_data['image2_all_ota1.bin']['sha256'] = calc_sha256(file1) +json_data['image2_all_ota2.bin'] = {} +json_data['image2_all_ota2.bin']['sha256'] = calc_sha256(file2) + +print("file1 sha1: ", json_data['image2_all_ota1.bin']['sha256']) +print("file2 sha2: ", json_data['image2_all_ota2.bin']['sha256']) + +with open(json_file, "w") as f: + json.dump(json_data, f) + +package_file = os.path.join(current_dir, "output", "package.zip") +with zipfile.ZipFile(package_file, "w") as zip: + # 遍历package_dir目录及其所有子文件夹,将所有文件打包,并保留目录结构 + for root, dirs, files in os.walk(package_dir): + for file in files: + file_path = os.path.join(root, file) + # 归一化存储在zip中的路径,使其以package_dir为根目录 + arcname = os.path.relpath(file_path, os.path.dirname(package_dir)) + zip.write(file_path, arcname) + +print("升级包:package.zip sha256: ", calc_sha256(package_file)) \ No newline at end of file