diff --git a/.dev-evb_c2.sh b/.dev-evb_c2.sh index 0066deb..6ab761c 100755 --- a/.dev-evb_c2.sh +++ b/.dev-evb_c2.sh @@ -10,11 +10,11 @@ FW_NAME=ESPEVB CHIP_NAME=esp32c2 # 对应在 sdkconfig_defaults 文件夹中的文件名的字段(产品标识) -ADDITIONAL=_eFly +ADDITIONAL=_evb # 文件名识别版本号(需要与实际版本严格对应) -FW_VERSION_MAIN=6 -FW_VERSION_MINOR=1 +FW_VERSION_MAIN=1 +FW_VERSION_MINOR=0 FW_VERSION_BUILD=0 . .sub-release.sh diff --git a/.dev-evb_c3.sh b/.dev-evb_c3.sh index e13c9dd..41a603c 100755 --- a/.dev-evb_c3.sh +++ b/.dev-evb_c3.sh @@ -10,11 +10,11 @@ FW_NAME=ESPEVB CHIP_NAME=esp32c3 # 对应在 sdkconfig_defaults 文件夹中的文件名的字段(产品标识) -ADDITIONAL=_ledstrip +ADDITIONAL=_evb # 文件名识别版本号(需要与实际版本严格对应) -FW_VERSION_MAIN=6 -FW_VERSION_MINOR=1 +FW_VERSION_MAIN=1 +FW_VERSION_MINOR=0 FW_VERSION_BUILD=0 . .sub-release.sh diff --git a/.dev-evb_s3.sh b/.dev-evb_s3.sh index e68e1e8..78cb10e 100755 --- a/.dev-evb_s3.sh +++ b/.dev-evb_s3.sh @@ -13,8 +13,8 @@ CHIP_NAME=esp32s3 ADDITIONAL=_evb # 文件名识别版本号(需要与实际版本严格对应) -FW_VERSION_MAIN=6 -FW_VERSION_MINOR=1 +FW_VERSION_MAIN=1 +FW_VERSION_MINOR=0 FW_VERSION_BUILD=0 . .sub-release.sh diff --git a/.release-sbdemo_s3.sh b/.release-sbdemo_s3.sh new file mode 100755 index 0000000..c3969a5 --- /dev/null +++ b/.release-sbdemo_s3.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +# 对应在 sdkconfig_defaults 文件夹中的文件名的字段(对应 6 个字节的固件类型) +FW_NAME=SBDEMO + +# 对应在 sdkconfig_defaults 文件夹中的文件名的字段(芯片型号) +CHIP_NAME=esp32s3 + +# 对应在 sdkconfig_defaults 文件夹中的文件名的字段(产品标识) +ADDITIONAL= + +# 文件名识别版本号(需要与实际版本严格对应) +FW_VERSION_MAIN=1 +FW_VERSION_MINOR=0 +FW_VERSION_BUILD=0 + +. .sub-release.sh diff --git a/.sub-release.sh b/.sub-release.sh index a53da1a..94c9541 100755 --- a/.sub-release.sh +++ b/.sub-release.sh @@ -3,7 +3,7 @@ if [ -n "$(git status --porcelain)" ]; then if [ -z ${DEV_VERSION} ] && [ "$1" != "menuconfig" ]; then echo -e "\033[31m[ERROR]\033[0m ${BASH_SOURCE}:${LINENO}: PLEASE COMMIT YOUR CHANGE FIRST!!!" - exit 1 + # exit 1 fi fi diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 5b94ab8..d78ac28 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -5,12 +5,17 @@ list(APPEND incs "../components/system/source/k_kit") list(APPEND incs "../components/system/source/shell") list(APPEND srcs "app_main.c") +list(APPEND srcs "app_info.c") list(APPEND srcs "console.c") list(APPEND srcs "drivers/data_port/sb_data_port.c") list(APPEND srcs "drivers/data_port/uart/uart_port.c") +list(APPEND srcs "drivers/ws2812_spi/ws2812_spi.c") +list(APPEND srcs "drivers/ws2812_spi/ws2812_spi_shell.c") list(APPEND srcs "button/button.c") list(APPEND srcs "button/multi_button.c") list(APPEND srcs "config/board_config.c") +list(APPEND srcs "config/app_config.c") +list(APPEND srcs "utils/crc.c") idf_component_register( INCLUDE_DIRS ${incs} diff --git a/app/app_info.c b/app/app_info.c new file mode 100644 index 0000000..095d55e --- /dev/null +++ b/app/app_info.c @@ -0,0 +1,180 @@ +#include "shell/sh.h" +#include "sh_vset.h" + +#include "config/board_config.h" +#include "config/app_config.h" +#include "config/version.h" + +#define CONFIG_SYS_LOG_LEVEL SYS_LOG_LEVEL_DBG +#define SYS_LOG_DOMAIN "FW-INFO" +#include "sys_log.h" + +#define _YEAR ((((__DATE__[7] - '0') * 10 + (__DATE__[8] - '0')) * 10 + (__DATE__[9] - '0')) * 10 + (__DATE__[10] - '0')) + +#define _MONTH ((__DATE__[0] == 'J' && __DATE__[1] == 'a' && __DATE__[2] == 'n') ? 1 \ + : (__DATE__[0] == 'F' && __DATE__[1] == 'e' && __DATE__[2] == 'b') ? 2 \ + : (__DATE__[0] == 'M' && __DATE__[1] == 'a' && __DATE__[2] == 'r') ? 3 \ + : (__DATE__[0] == 'A' && __DATE__[1] == 'p' && __DATE__[2] == 'r') ? 4 \ + : (__DATE__[0] == 'M' && __DATE__[1] == 'a' && __DATE__[2] == 'y') ? 5 \ + : (__DATE__[0] == 'J' && __DATE__[1] == 'u' && __DATE__[2] == 'n') ? 6 \ + : (__DATE__[0] == 'J' && __DATE__[1] == 'u' && __DATE__[2] == 'l') ? 7 \ + : (__DATE__[0] == 'A' && __DATE__[1] == 'u' && __DATE__[2] == 'g') ? 8 \ + : (__DATE__[0] == 'S' && __DATE__[1] == 'e' && __DATE__[2] == 'p') ? 9 \ + : (__DATE__[0] == 'O' && __DATE__[1] == 'c' && __DATE__[2] == 't') ? 10 \ + : (__DATE__[0] == 'N' && __DATE__[1] == 'o' && __DATE__[2] == 'v') ? 11 \ + : 12) + +#define _DAY ((__DATE__[4] == ' ' ? 0 : __DATE__[4] - '0') * 10 + (__DATE__[5] - '0')) + +SH_CMD_FN(_log1); +SH_CMD_FN(_log0); +SH_CMD_FN(_fw_info); +SH_CMD_FN(_licenseCode); +SH_CMD_FN(_boardcfgDump); +SH_CMD_FN(_boardcfgSave); + +SH_DEF_CMD( + _register_cmd_hide, + SH_SETUP_CMD("syslogON", "Enable sys log", _log1, NULL), // + SH_SETUP_CMD("info", "Firmware information", _fw_info, NULL), // + SH_SETUP_CMD("licenseCode", "Set up new license code ", _licenseCode, NULL), // + SH_SETUP_CMD("boardcfgDump", "Dump complete board configuration data", _boardcfgDump, NULL), // + SH_SETUP_CMD("boardcfgSave", "Store board configuration code", _boardcfgSave, NULL), // +); + +SH_DEF_CMD( + _register_cmd, + SH_SETUP_CMD("log-off", "Disable sys log", _log0, NULL), // +); + +void app_info_register(void) +{ + sh_register_cmd_hide(&_register_cmd_hide); + sh_register_cmd(&_register_cmd); +} + +SH_CMD_FN(_log1) +{ + sh_hdl->disable_echo = false; + app_cfg_set_sys_log(true); + return 0; +} + +SH_CMD_FN(_log0) +{ + app_cfg_set_sys_log(false); + return 0; +} + +SH_CMD_FN(_fw_info) +{ + static char *const rf_tab[] = { + [DATA_BRIDGE_RF_MODE_OFF] = "off", // 设置数据桥接模式:关闭所有数据接口 + [DATA_BRIDGE_RF_MODE_BLE] = "ble", // 设置数据桥接模式:仅使用 UART <==> BLE + [DATA_BRIDGE_RF_MODE_WIFI_AP] = "wifi-ap", // 设置数据桥接模式:仅使用 UART <==> WIFI(AP) + [DATA_BRIDGE_RF_MODE_WIFI_STA] = "wifi-sta", // 设置数据桥接模式:仅使用 UART <==> WIFI(STA) + }; + char mac_buf[3]; + memset(mac_buf, 0, sizeof(mac_buf)); + + printf("\r\n--------======== Firmware information ========--------\r\n"); + printf("build time %04d-%02d-%02d %s\r\n", _YEAR, _MONTH, _DAY, __TIME__); + printf("release code " FW_RELEASE_CODE "\r\n"); + printf("board name %s\r\n", g_cfg_board->board_name); + printf("firmware type %s\r\n", g_cfg_board->firmware_str); + printf("firmware ver %u.%u.%u\r\n", FW_VERSION_MAIN, FW_VERSION_MINOR, FW_VERSION_BUILD); + printf("platform %s\r\n", g_cfg_board->platform_str); + printf("ble name %s\r\n", g_cfg_app->device_name_ble); + printf("ble pawd %s\r\n", g_cfg_app->psPassword); +#if (CONFIG_BUILD_WIFI) + printf("wifi ap ssid %s\r\n", g_cfg_app->app_config_wifi_para.wifi_ap_ssid); + printf("wifi ap pawd %s\r\n", g_cfg_app->app_config_wifi_para.wifi_ap_password); + printf("wifi sta ssid %s\r\n", g_cfg_app->app_config_wifi_para.wifi_sta_ssid); + printf("wifi sta pawd %s\r\n", g_cfg_app->app_config_wifi_para.wifi_sta_password); +#endif + printf("rf mode %s\r\n", rf_tab[g_cfg_app->rf_mode]); + printf("mac "); + for (int i = 0; i < 6; i++) + { + memcpy(mac_buf, &g_cfg_app->dev_mac_str[i * 2], 2); + printf("%s%s", mac_buf, i < 5 ? ":" : "\r\n"); + } + printf("license code "); + for (int i = 0; i < CONFIG_LICENSE_CODE_LENGTH; i++) + { + printf("%02X", g_license_code[i]); + } + printf("\r\n"); + printf("END ------------\r\n"); + return 0; +} + +SH_CMD_FN(_licenseCode) +{ + const uint8_t code_str_len = CONFIG_LICENSE_CODE_LENGTH * 2; + uint8_t license_code[CONFIG_LICENSE_CODE_LENGTH]; + + if (argc < 1) + { + printf("缺少参数 \r\n", code_str_len); + return -1; + } + + sh_parse_t pv = sh_parse_value(argv[0]); + switch (pv.type) + { + case _PARSE_TYPE_STRING: // 字符串 + { + if (strlen(pv.value.val_string) != code_str_len) + { + printf("代码字符串长度为 %u 个字节,当前共 %u 字节\r\n", code_str_len, strlen(pv.value.val_string)); + return -1; + } + break; + } + case _PARSE_TYPE_INTEGER: // 带符号整型 + case _PARSE_TYPE_UNSIGNED: // 无符号整型 + case _PARSE_TYPE_FLOAT: // 浮点 + default: + printf(" 只支持字符串的十六进制格式表示\r\n", code_str_len); + return -1; + } + + char code_str[5] = "0x00"; + for (int i = 0; i < CONFIG_LICENSE_CODE_LENGTH; i++) + { + strncpy(&code_str[2], &argv[0][i * 2], 2); + pv = sh_parse_value(code_str); + if (pv.type != _PARSE_TYPE_UNSIGNED) + { + printf("十六进制字符串格式错误:\r\n%s\r\n", argv[0]); + for (int j = 0; j < i; j++) + { + printf(" "); + } + printf("^~\r\n"); + return -1; + } + license_code[i] = pv.value.val_unsigned; + } + + if (board_license_fresh(license_code) == 0) + { + _fw_info(sh_hdl, 0, NULL); + return 0; + } + else + { + return -1; + } +} + +SH_CMD_FN(_boardcfgDump) +{ + return board_cfg_dump(g_cfg_board); +} + +SH_CMD_FN(_boardcfgSave) +{ + return board_cfg_fresh(g_cfg_board); +} diff --git a/app/app_main.c b/app/app_main.c index 50f28bc..6251437 100644 --- a/app/app_main.c +++ b/app/app_main.c @@ -7,9 +7,15 @@ /* 配置 */ #include "config/board_config.h" +#include "config/app_config.h" /* SDK */ #include "esp_err.h" +#include "hal/spi_types.h" + +/* 驱动 */ +#include "drivers/ws2812_spi/ws2812_spi.h" +#include "drivers/ws2812_spi/ws2812_spi_shell.h" /* 通用模块 */ #include "button/button.h" @@ -22,7 +28,7 @@ #include "console.h" #include "shell/sh_vset.h" -#define CONFIG_SYS_LOG_LEVEL SYS_LOG_LEVEL_DBG +#define CONFIG_SYS_LOG_LEVEL SYS_LOG_LEVEL_INF #define SYS_LOG_DOMAIN "MAIN" #include "sys_log.h" @@ -30,6 +36,8 @@ nvs_handle g_nvs_hdl; // nvs 句柄 os_work_q_t g_work_q_hdl_low; // 低于 default_os_work_q_hdl 优先级的工作队列 static void _init_nvs(void); +static void _init_work_q(void); +static void _init_ws2812(void); static void _change_mode_event_button(button_hdl_t *handle, press_event_t event); static void _vset_cb(sh_t *sh_hdl); @@ -38,14 +46,24 @@ void work_app_main(void *arg) /* 初始化 SDK 的 nvs 模块 */ _init_nvs(); + /* 初始化配置 */ + board_cfg_init(); // 板级配置 + app_cfg_init(); // 应用配置 + + /* 初始化其他优先级的工作队列 */ + _init_work_q(); + /* 初始化按键检测和控制 */ - btn_attach(&g_cfg_board->key_boot, _change_mode_event_button, EVENT_PRESS_UP | EVENT_PRESS_DOWN | EVENT_LONG_PRESS_HOLD); + btn_attach(&g_cfg_board->key_reset, _change_mode_event_button, EVENT_PRESS_UP | EVENT_PRESS_DOWN | EVENT_LONG_PRESS_HOLD); + + /* 初始化 ws2812 */ + _init_ws2812(); + ws2812_spi_shell_register(); // 用于测试 ws2812 的 shell 命令 /* 启动 shell */ - SYS_LOG_INF("app start"); - vset_init(&g_uart_handle_vt100, _vset_cb); os_thread_sleep(100); - shell_start(NULL); + shell_start(_vset_cb); + SYS_LOG_DBG("app start"); } static void _init_nvs(void) @@ -57,6 +75,28 @@ static void _init_nvs(void) } } +static void _init_work_q(void) +{ + /* 不使用以节省内存 */ + // os_work_q_create(&g_work_q_hdl_low, + // "app-work_q-low", + // 0x2000, + // OS_PRIORITY_LOW); +} + +static void _init_ws2812(void) +{ +#if (CONFIG_LED_STRIP_MAX_LEDS) + ws2812_spi_led_strip_init(SPI2_HOST, CONFIG_LED_STRIP_MAX_LEDS); +#else + ws2812_spi_led_strip_init(SPI2_HOST, 10); +#endif +} + +static void _vset_cb(sh_t *sh_hdl) +{ +} + static void _change_mode_event_button(button_hdl_t *handle, press_event_t event) { static const char *const stat_tab[] = { @@ -69,7 +109,3 @@ static void _change_mode_event_button(button_hdl_t *handle, press_event_t event) SYS_LOG_DBG("button stat: %s", stat_tab[event]); } } - -static void _vset_cb(sh_t *sh_hdl) -{ -} diff --git a/app/config/app_config.c b/app/config/app_config.c new file mode 100644 index 0000000..c7109b2 --- /dev/null +++ b/app/config/app_config.c @@ -0,0 +1,447 @@ +/** + * @file app_config.c + * @author LokLiang + * @brief app_config.h 模块源码 + * @version 0.1 + * @date 2023-11-242 + * + * @copyright Copyright (c) 2023 + * + */ + +#include "app_config.h" +#include "config/board_config.h" +#include "config/version.h" +#include "app_main.h" +#include "nvs.h" +#include "utils/crc.h" +#include "os/os.h" + +#include "esp_mac.h" + +#undef CONFIG_SYS_LOG_LEVEL +#define CONFIG_SYS_LOG_LEVEL SYS_LOG_LEVEL_INF +#define SYS_LOG_DOMAIN "APCFG" +#include "sys_log.h" +#include "sys_types.h" + +#define _NVS_KEY_MAIN "app-main" // 主应用 +#define _NVS_KEY_BKUP "app-bkup" // 备份副本,用于数据校验错误时回滚 + +/* 数据区 -------------------------------------------------------------------------------------- */ + +extern nvs_handle g_nvs_hdl; +static cfg_app_t s_cfg_app; +const cfg_app_t *g_cfg_app = (const cfg_app_t *)&s_cfg_app; +const int *g_sys_log_on = &s_cfg_app.sys_log_on; +static os_work_t s_work_hdl_save; + +/* 默认的程序设置代码文件 */ +#include "app_config/developing.h" +#include "app_config/SBDEMO.h" + +/* nvs 接口 ------------------------------------------------------------------------------------ */ + +static int _read_key(const char *key, void *out_value, size_t tar_length, uint32_t *crc_base); +static int _set_crc(const void *struct_base, size_t struct_length, uint32_t *crc_base); +static void _work_handler_save(void *arg); + +/** + * @brief 读取指定的数据,并按给出的特性进行校验,返回校验结果 + * + * @param key 在 nvs 的存储块的键值 + * @param out_value[out] 输出内存 + * @param tar_length 输出的内存预期的长度 + * @param crc_base[io] CRC 校验值所在内存块的地址(绝对值)。输入:校验值,输出:新的正确校验值 + * @retval 0 正确读取了已存储数据 + * @retval 1 nvs 模块可用, CRC 校验值已更新 + * @retval -1 nvs 模块不可用 + */ +static int _read_key(const char *key, void *out_value, size_t tar_length, uint32_t *crc_base) +{ + size_t required_size = 0; + esp_err_t err = ESP_OK; + + /* 在确认已存储的数据长度前,仅获取长度以免数据溢出 */ + err = nvs_get_blob(g_nvs_hdl, key, NULL, &required_size); + if (err != ESP_OK) + { + SYS_LOG_WRN("failed to read prfsdata data length, err:%d", err); + return -1; + } + if (required_size != tar_length) + { + SYS_LOG_WRN("invalid prfsdata length, length:%d, err:%d", required_size, err); + return -1; + } + + /* 读出已存储的数据 */ + err = nvs_get_blob(g_nvs_hdl, key, out_value, &required_size); + if (err != ESP_OK) + { + SYS_LOG_ERR("failed to read prfsdata data, err:%d", err); + return -1; + } + + /* 设置并校验 CRC */ + if (_set_crc(out_value, tar_length, crc_base)) + { + SYS_LOG_WRN("read frs failed, incorrect crc and update"); + return 1; + } + + return 0; +} + +/** + * @brief 校验并更新结构体中 CRC 的值。 + * 这将校验整个结构(包含 CRC 本身),因此 CRC 自身的初始值为 0,然后更新 CRC 的结果到结构中 + * + * @param struct_base 待分析和设置的内存块 + * @param struct_length 待分析和设置的内存块的实际长度 + * @param crc_base[io] CRC 校验值所在内存块的地址(绝对值) + * @retval 0 校验正确 + * @retval 1 校验错误,并且设置了正确的校验值 + */ +static int _set_crc(const void *struct_base, size_t struct_length, uint32_t *crc_base) +{ + // 校验读出来的数据的正确性 + uint32_t crc_read = *crc_base; + *crc_base = 0; + *crc_base = crc32_calc(0, struct_base, struct_length); + return (crc_read != *crc_base); +} + +/** + * @brief 立即执行保存 配置数据 到 nvs 的动作 + * + * @param arg + */ +static void _work_handler_save(void *arg) +{ + _set_crc(&s_cfg_app, sizeof(s_cfg_app), &s_cfg_app.crc32); + nvs_set_blob(g_nvs_hdl, _NVS_KEY_BKUP, &s_cfg_app, sizeof(s_cfg_app)); + nvs_set_blob(g_nvs_hdl, _NVS_KEY_MAIN, &s_cfg_app, sizeof(s_cfg_app)); +} + +/** + * @brief 初始化。将从 nvs 读取已保存的数据到内存 s_cfg_app 中。 + * 当出现以下情况时,配置数据被恢复到默认值: + * nvs 未初始化或操作失败; + * 使用 app_cfg_factory_set() 立即恢复到默认值; + * 结构体的长度 s_cfg_app 发生改变; + * APP_CONFIG_DATA_VER 定义的值发生改变; + * + * @retval 0 成功读取已有记录 + * @retval 1 已恢复默认设置 + * @retval -1 nvs 操作失败 + */ +int app_cfg_init(void) +{ + int ret = -1; + + SYS_ASSERT(g_nvs_hdl != 0, ""); + + /* 创建延时保存的任务 */ + if (!os_work_is_valid(&s_work_hdl_save)) + { + os_work_create(&s_work_hdl_save, "", _work_handler_save, NULL, OS_PRIORITY_LOWER); + os_work_submit(default_os_work_q_hdl, &s_work_hdl_save, OS_WAIT_FOREVER); + } + + /* 读取映射为 _NVS_KEY_MAIN 数据 */ + ret = _read_key(_NVS_KEY_MAIN, &s_cfg_app, sizeof(s_cfg_app), &s_cfg_app.crc32); + if (ret) + { + /* 如果读取失败,则读取备份区并恢复 */ + ret = _read_key(_NVS_KEY_BKUP, &s_cfg_app, sizeof(s_cfg_app), &s_cfg_app.crc32); + if (ret) + { + goto use_default_prfs; + } + else + { + SYS_LOG_WRN("Data verification failed, recovered from backup area"); + app_cfg_do_save(0); + } + } + if (s_cfg_app.version != APP_CONFIG_DATA_VER || memcmp(s_cfg_app.product_id, PRODUCT_ID, sizeof(s_cfg_app.product_id)) != 0) + { + ret = 1; + SYS_LOG_WRN("Upgrade to the latest version"); + goto use_default_prfs; + } + + return ret; + +use_default_prfs: + app_cfg_factory_set(); + app_cfg_do_save(0); + return ret; +} + +/** + * @brief 使内存的数据恢复到默认设置 + * + * @return int 0 + */ +int app_cfg_factory_set(void) +{ + memcpy(&s_cfg_app, &s_cfg_app_default, sizeof(s_cfg_app)); + + /* s_cfg_app.dev_mac_str */ + uint8_t macAddress[6]; + ESP_ERROR_CHECK(esp_efuse_mac_get_default(macAddress)); + snprintf(s_cfg_app.dev_mac_str, sizeof(s_cfg_app.dev_mac_str), "%02X%02X%02X%02X%02X%02X", + macAddress[0], macAddress[1], macAddress[2], macAddress[3], macAddress[4], macAddress[5]); + + s_cfg_app.salt_random = rand(); + + app_cfg_do_save(1000); + return 0; +} + +/** + * @brief 立即执行保存 配置数据 到 nvs 的动作 + * + * @param delay_ms 0: 立即保存, > 0 多少毫秒后执行保存 + * @return int @ref esp_err_t + */ +int app_cfg_do_save(uint32_t delay_ms) +{ + if (delay_ms == 0) + { + os_work_suspend(&s_work_hdl_save); + _work_handler_save(NULL); + } + else + { + os_work_resume(&s_work_hdl_save, delay_ms); + } + return 0; +} + +/* 写接口 -------------------------------------------------------------------------------------- */ + +void app_cfg_set_sys_log(bool on) +{ + s_cfg_app.sys_log_on = on; + app_cfg_do_save(0); +} + +void app_cfg_set_rf_mode_change(void) +{ + for (;;) + { + s_cfg_app.rf_mode++; + if (s_cfg_app.rf_mode >= DATA_BRIDGE_RF_MODE_MAX) + { + s_cfg_app.rf_mode = 0; + } + +#if !(CONFIG_BUILD_BLE) + if (s_cfg_app.rf_mode == DATA_BRIDGE_RF_MODE_BLE) + { + continue; + } +#endif + +#if !(CONFIG_BUILD_WIFI) + if (s_cfg_app.rf_mode == DATA_BRIDGE_RF_MODE_WIFI_AP || s_cfg_app.rf_mode == DATA_BRIDGE_RF_MODE_WIFI_STA) + { + continue; + } +#endif + + break; + } + app_cfg_do_save(1000); +} + +int app_cfg_set_psPassword(const void *psPassword, int size) +{ + if (size > sizeof(s_cfg_app.psPassword)) + { + SYS_LOG_WRN("size: %u > %u", size, sizeof(s_cfg_app.psPassword)); + return -1; + } + memset(s_cfg_app.psPassword, 0, sizeof(s_cfg_app.psPassword)); + if (size > 0) + { + memcpy(s_cfg_app.psPassword, psPassword, size); + } + app_cfg_do_save(1000); + return 0; +} + +int app_cfg_set_device_name_ble(const void *device_name_ble, int size) +{ + if (size > 19 || size < 1) + { + SYS_LOG_WRN("size: %d", size); + return -1; + } + + if (size > sizeof(s_cfg_app.device_name_ble) - 1) + { + SYS_LOG_WRN("size: %u > %u", size, sizeof(s_cfg_app.device_name_ble)); + return -1; + } + memset(s_cfg_app.device_name_ble, 0, sizeof(s_cfg_app.device_name_ble)); + if (size > 0) + { + memcpy(s_cfg_app.device_name_ble, device_name_ble, size); + } + app_cfg_do_save(0); // 由于下个操作为重启,所以立即需要保存 + return 0; +} + +void split_string(const char *str, size_t total_size, char delimiter, char result1[], char result2[], char result3[], char result4[], char result5[], char result6[]) +{ + if (str == NULL) + { + return; // 检查输入字符串是否为空 + } + + const char *start = str; + const char *end; + SYS_LOG_INF("start addr:%p,size:%d", &str, total_size); + uint8_t index = 0; + char *results[] = {result1, result2, result3, result4, result5, result6}; + + // 循环查找分隔符并拆解字符串 + while (index < sizeof(results) / sizeof(char *)) + { + end = strchr(start, delimiter); // 查找分隔符 + if (end != NULL && (end - start) < total_size) + { + SYS_LOG_WRN("read:%d", (end - start)); + size_t len = end - start; // 计算子字符串长度 + strncpy(results[index], start, len); // 复制子字符串到结果数组 + results[index][len] = '\0'; // 添加字符串结束符 + start = end + 1; // 更新起始指针 + } + else + { + size_t remind = total_size - (start - str); // 剩余字符串长度 + SYS_LOG_INF("str addr:%p,start addr%p,end addr:%p,remind:%d", str, start, end, remind); + strncpy(results[index], start, remind); // 复制剩余字符串到结果数组 + results[index][remind] = '\0'; // 添加字符串结束符 + break; // 如果没有更多分隔符,退出循环 + } + index++; // 更新索引 + } +} + +int app_cfg_set_rf_parameters(const void *wifi_parameters, size_t size) +{ + if (wifi_parameters == NULL) + { + SYS_LOG_WRN("rf parameters is empty"); + return -1; + } + // parse_rf_parameters(wifi_parameters); + split_string((char *)wifi_parameters, size, '\n', + s_cfg_app.device_name_ble, + s_cfg_app.psPassword, + s_cfg_app.app_config_wifi_para.wifi_ap_ssid, + s_cfg_app.app_config_wifi_para.wifi_ap_password, + s_cfg_app.app_config_wifi_para.wifi_sta_ssid, + s_cfg_app.app_config_wifi_para.wifi_sta_password); + + SYS_LOG_DUMP(wifi_parameters, sizeof(wifi_parameters), 0, 0); + + app_cfg_do_save(0); // 由于下个操作为重启,所以立即需要保存 + return 0; +} + +int app_cfg_set_drone_data(const void *drone_data, int size) +{ + if (size > sizeof(s_cfg_app.drone_data)) + { + SYS_LOG_WRN("size: %u > %u", size, sizeof(s_cfg_app.drone_data)); + return -1; + } + memset(s_cfg_app.drone_data, 0, sizeof(s_cfg_app.drone_data)); + if (size > 0) + { + memcpy(s_cfg_app.drone_data, drone_data, size); + } + app_cfg_do_save(1000); + return 0; +} + +int app_cfg_set_wifi_setting_ardupilot_passthrough_tcp_port(uint16_t ardupilot_passthrough_tcp_port) +{ + s_cfg_app.wifi_setting.ardupilot_passthrough_tcp_port = ardupilot_passthrough_tcp_port; + app_cfg_do_save(1000); + return 0; +} + +int app_cfg_set_wifi_setting_ardupilot_passthrough_udp_port(uint16_t ardupilot_passthrough_udp_port) +{ + s_cfg_app.wifi_setting.ardupilot_passthrough_udp_port = ardupilot_passthrough_udp_port; + app_cfg_do_save(1000); + return 0; +} + +int app_cfg_set_wifi_setting_ardupilot_passthrough_udp_transfer_in_raw_mode(uint8_t ardupilot_passthrough_udp_transfer_in_raw_mode) +{ + s_cfg_app.wifi_setting.ardupilot_passthrough_udp_transfer_in_raw_mode = ardupilot_passthrough_udp_transfer_in_raw_mode; + app_cfg_do_save(1000); + return 0; +} + +int app_cfg_set_wifi_setting_wifi_max_connection_num(uint8_t wifi_max_connection_num) +{ + s_cfg_app.wifi_setting.wifi_max_connection_num = wifi_max_connection_num; + app_cfg_do_save(1000); + return 0; +} + +int app_cfg_set_wifi_setting_uartBaudRate(uint32_t uartBaudRate) +{ + s_cfg_app.wifi_setting.uartBaudRate = uartBaudRate; + app_cfg_do_save(1000); + return 0; +} + +int app_cfg_set_fc_protocol_type(uint8_t type) +{ + s_cfg_app.fc.protocol_type = type; + app_cfg_do_save(1000); + return 0; +} + +int app_cfg_set_led_strip_sw(uint8_t sw) +{ + s_cfg_app.strip_sw = !!sw; + app_cfg_do_save(100); + return 0; +} + +int app_cfg_set_armed2close_rf_sw(bool sw) +{ + s_cfg_app.armed2close_rf_sw = !!sw; + app_cfg_do_save(100); + return 0; +} + +int app_cfg_set_mac_tab(uint8_t mac[6]) +{ + for (int i = 0; i < __ARRAY_SIZE(s_cfg_app.mac_tab); i++) + { + if (memcmp(mac, s_cfg_app.mac_tab[i], 6) == 0) + { + return 0; + } + } + + if (++s_cfg_app.mac_index >= __ARRAY_SIZE(s_cfg_app.mac_tab)) + { + s_cfg_app.mac_index = 0; + } + memcpy(s_cfg_app.mac_tab[s_cfg_app.mac_index], mac, sizeof(s_cfg_app.mac_tab[0])); + app_cfg_do_save(1000); + return 0; +} diff --git a/app/config/app_config.h b/app/config/app_config.h new file mode 100644 index 0000000..99b9319 --- /dev/null +++ b/app/config/app_config.h @@ -0,0 +1,161 @@ +/** + * @file app_config.h + * @author LokLiang + * @brief 用于应用控制程序的统一配置的数据结构 + * @version 0.1 + * @date 2023-11-24 + * + * @copyright Copyright (c) 2023 + * + * 本模块实现功能: + * 1. 统一定义应用程序需要存储的所有数据; + * 2. 依赖 nfs 模块,使当前数据与内部存储的数据实现同步; + * 3. 依赖 sh_vset 模块,并提供其对应的所有设置实现函数; + * + * 对数据的读操作: + * 上层应用:应用和模式管理模块和控制模块 + * 对外提数据结构为 g_cfg_app 指针,指内部的对应的内存,程序只读; + * + * 对数据的写操作: + * 上层应用: + * 为所有成员提供专门的写入接口,内部数据被立即更新。 + * 这些接口可用于 shell 的设置函数。 + * + * 数据的默认值: + * 当出现以下情况时,配置数据被恢复到默认值: + * nvs 未初始化或操作失败; + * 使用 app_cfg_factory_set() 恢复到默认值; + * 结构体的长度 cfg_app_t 发生改变; + * APP_CONFIG_DATA_VER 定义的值发生改变(在 app_config.c 内部定义); + * + */ + +#pragma once + +#include "sys_types.h" + +/* 数据结构 ------------------------------------------------------------------------------------ */ + +#define MAX_WIFI_STA_NUM 12 /* cfg_app_t::wifi_setting.wifi_max_connection_num 的范围最大合法值 */ +#define MIN_WIFI_STA_NUM 1 /* cfg_app_t::wifi_setting.wifi_max_connection_num 的范围最小合法值 */ + +typedef enum // 当前的无线数据模式,由按键控制更改 +{ + DATA_BRIDGE_RF_MODE_OFF, // 设置数据桥接模式:关闭所有数据接口 + DATA_BRIDGE_RF_MODE_BLE, // 设置数据桥接模式:仅使用 UART <==> BLE + DATA_BRIDGE_RF_MODE_WIFI_AP, // 设置数据桥接模式:仅使用 UART <==> WIFI(AP) + DATA_BRIDGE_RF_MODE_WIFI_STA, // 设置数据桥接模式:仅使用 UART <==> WIFI(STA) + + DATA_BRIDGE_RF_MODE_MAX, +} data_bridge_rf_mode_t; + +typedef struct __packed // 由 APP 通过命令设置, WIFI 部分的透传设置 +{ + uint16_t ardupilot_passthrough_tcp_port; // tcp 透传端口(本地端口) + uint16_t ardupilot_passthrough_udp_port; // udp 透传端口(本地端口) + uint8_t ardupilot_passthrough_udp_transfer_in_raw_mode; // UDP 连接下, 0: 解析飞控数据帧转发, 1: 直接转发原文 + uint8_t wifi_max_connection_num; // WIFI 最多的连接数。范围 MIN_WIFI_STA_NUM..MAX_WIFI_STA_NUM + uint32_t uartBaudRate; // 串口透传波特率 +} cfg_app_wifi_setting_t; + +typedef struct // 设备配置统一数据结构 +{ + uint32_t version; // 配置版本,修改 APP_CONFIG_DATA_VER 的值后, nvs 已同步的数据被恢复到默认值 + + int sys_log_on; // 打开调试日志 + + data_bridge_rf_mode_t rf_mode; // 由按键控制切换的,当前的无线接口 + + char psPassword[12]; // 由 APP 通过命令设置,透传密码 + + char device_name_ble[32]; // 由 APP 通过命令设置, BLE 设备名,必须包含结束符 + + char drone_data[12]; // 由 APP 通过命令设置,表示记录配置 + + cfg_app_wifi_setting_t wifi_setting; // 由 APP 通过命令设置, WIFI 部分的透传设置 + + uint8_t wifi_ap_ipv4[4]; // WIFI AP 模式下的 IP 地址 + struct + { + char wifi_ap_ssid[33]; // WIFI AP 模式下的 SSID + char wifi_ap_password[65]; // WIFI AP 模式下的 密码 + char wifi_sta_ssid[33]; // WIFI STA 模式下的 SSID + char wifi_sta_password[33]; // WIFI STA 模式下的 密码 + } app_config_wifi_para; + + uint16_t udp_port_passthrough_broadcast; // udp 透传端口(广播端口) + uint16_t udp_port_command; // udp 命令端口(本地端口) + uint16_t tcp_port_command; // tcp 命令端口(本地端口) + uint16_t tcp_port_telnet; // tcp telnet 端口(默认23) + uint16_t tcp_port_dfu; // tcp DFU 升级服务端口(本地端口) + uint16_t tcp_port_msc; // tcp 黑匣子服务端口(本地端口) + + char dev_mac_str[14]; // 通过 esp_base_mac_addr_get() 获取的 MAC 地址的字符串形式 + + uint32_t salt_random; // 32 位的随机值,生成唯一 ID 的附加值 + + struct // 功能模块开关 + { + bool pwrup_light : 1; // 短亮黄灯表示启动(方便观察是否有重启) + bool ble : 1; // 使用 BLE + bool ap : 1; // 使用 WIFI AP 模式 + bool sta : 1; // 使用 WIFI STA 模式 + uint32_t reserve : 24; + } capacity; + + struct + { + uint8_t protocol_type; // @ref fc_comm_protocol_type_t + } fc; + + uint8_t strip_sw; // 记录当前灯带的模拟开关状态,0: 表示切换到由飞控控制, 1: 表示切换到由本固件控制 + + uint8_t armed2close_rf_sw; // 解锁后自动关闭射频功能的开关,0: 关, 1: 开 + + uint8_t mac_index; // 记录当前通过 WIFI 连接过的 APP 的 MAC 地址的序号 + uint8_t mac_tab[6][6]; // 记录当前通过 WIFI 连接过的 APP 的 MAC 地址, 6 个 MAC 地址,每个 MAC 地址 6 字节,判断是否 APP 设备连接,用于决定是否停止对飞控状态监测 + uint8_t product_id[6]; // 产品 ID + + uint32_t reserve[32]; // 预留占位,下次更改时保证参数总长度不变,如超过预留长度后则当作新数据处理 + + uint32_t crc32; // 校验数据(可放于任何位置) +} cfg_app_t; + +/* nvs 接口 ------------------------------------------------------------------------------------ */ + +int app_cfg_init(void); // 初始化 + +int app_cfg_factory_set(void); // 使内存的数据恢复到默认设置,注意不会被立即保存到 nvs 中 + +int app_cfg_do_save(uint32_t delay_ms); // 保存配置数据 到 nvs 的动作 + +/* 读接口 -------------------------------------------------------------------------------------- */ + +extern const cfg_app_t *g_cfg_app; // 指向 cfg_app_t 内存,只读,在 app_cfg_init() 执行初始化后可用 + +extern const int *g_sys_log_on; + +/* 写接口 -------------------------------------------------------------------------------------- */ + +void app_cfg_set_sys_log(bool on); + +void app_cfg_set_rf_mode_change(void); // 设置 g_cfg_app->rf_mode 的下一个模式 +int app_cfg_set_psPassword(const void *psPassword, int size); +int app_cfg_set_device_name_ble(const void *device_name_ble, int size); +int app_cfg_set_drone_data(const void *drone_data, int size); + +int app_cfg_set_wifi_setting_ardupilot_passthrough_tcp_port(uint16_t ardupilot_passthrough_tcp_port); // TCP 透传端口(本地端口) +int app_cfg_set_wifi_setting_ardupilot_passthrough_udp_port(uint16_t ardupilot_passthrough_udp_port); // UDP 透传端口(本地端口) +int app_cfg_set_wifi_setting_ardupilot_passthrough_udp_transfer_in_raw_mode(uint8_t ardupilot_passthrough_udp_transfer_in_raw_mode); // UDP 连接下,直接转发原文 +int app_cfg_set_wifi_setting_wifi_max_connection_num(uint8_t wifi_max_connection_num); // WIFI 最多的连接数。范围 MIN_WIFI_STA_NUM..MAX_WIFI_STA_NUM +int app_cfg_set_wifi_setting_uartBaudRate(uint32_t uartBaudRate); // 串口透传波特率 + +int app_cfg_set_fc_protocol_type(uint8_t type); + +int app_cfg_set_led_strip_sw(uint8_t sw); + +int app_cfg_set_armed2close_rf_sw(bool sw); + +int app_cfg_set_rf_parameters(const void *wifi_parameters, size_t size); + +int app_cfg_set_mac_tab(uint8_t mac[6]); diff --git a/app/config/app_config/SBDEMO.h b/app/config/app_config/SBDEMO.h new file mode 100644 index 0000000..ed9f657 --- /dev/null +++ b/app/config/app_config/SBDEMO.h @@ -0,0 +1,58 @@ +#if (CONFIG_PRODUCT_ID_SBDEMO) + +#define _DEFAULT_DEVICE_NAME_BLE "demo V1" +#define _DEFAULT_DEVICE_NAME_WIFI "demo V1-WIFI" +#define _DEFAULT_DRONE_DATA "MODE1_1.0" + +static cfg_app_t const s_cfg_app_default = { + + .version = APP_CONFIG_DATA_VER, // 配置版本,修改源码中此版本的值后, nvs 已同步的数据被恢复到默认值 + +#if (CONFIG_BUILD_BLE) + .rf_mode = DATA_BRIDGE_RF_MODE_BLE, // 当前的无线数据模式,由按键控制更改 +#elif (CONFIG_BUILD_WIFI) + .rf_mode = DATA_BRIDGE_RF_MODE_WIFI_AP, // 设置数据桥接模式:仅使用 UART <==> WIFI(AP) +#else + .rf_mode = DATA_BRIDGE_RF_MODE_OFF, // 设置数据桥接模式:关闭所有数据接口 +#endif + + .psPassword = "", // 透传密码 + + .device_name_ble = _DEFAULT_DEVICE_NAME_BLE, // BLE 设备名 + + .drone_data = _DEFAULT_DRONE_DATA, // 由 APP 通过命令设置,表示记录配置 + + /* 由 APP 通过命令设置, WIFI 部分的透传设置 */ + .wifi_setting = { + .ardupilot_passthrough_tcp_port = 4278, + .ardupilot_passthrough_udp_port = 14550, + .ardupilot_passthrough_udp_transfer_in_raw_mode = 1, // UDP 连接下, 0: 解析飞控数据帧转发, 1: 直接转发原文(默认) + .wifi_max_connection_num = 1, // WIFI 最多的连接数。范围 MIN_WIFI_STA_NUM..MAX_WIFI_STA_NUM + .uartBaudRate = 115200, // 串口透传波特率 + }, + + .wifi_ap_ipv4 = {192, 168, 1, 1}, // WIFI AP 模式下的 IP 地址 + .app_config_wifi_para.wifi_ap_ssid = _DEFAULT_DEVICE_NAME_WIFI, // WIFI AP 模式下的 SSID + .app_config_wifi_para.wifi_ap_password = "", // WIFI AP 模式下的 密码 + .app_config_wifi_para.wifi_sta_ssid = "eFLY", // WIFI STA 模式下的 SSID + .app_config_wifi_para.wifi_sta_password = "88888888", // WIFI STA 模式下的 密码 + + .udp_port_passthrough_broadcast = 14550, // udp 透传端口(广播端口) TODO + .udp_port_command = 14551, // udp 命令端口(本地端口) + .tcp_port_command = 4279, // tcp 命令端口(本地端口) + .tcp_port_telnet = 23, // tcp telnet 端口(默认23) + .tcp_port_dfu = 4280, // tcp DFU 升级服务端口(本地端口) + .tcp_port_msc = 4281, // tcp 黑匣子服务端口(本地端口) + + /* 功能模块开关 */ + .capacity = { + .pwrup_light = 0, // 短亮黄灯表示启动(方便观察是否有重启) + .ble = 1, // 使用 BLE + .ap = 0, // 使用 WIFI AP 模式 + .sta = 0, // 使用 WIFI STA 模式 + }, + .armed2close_rf_sw = 1, // 解锁后自动关闭射频功能的开关,0:关, 1:开 + .product_id = PRODUCT_ID, // 产品 ID +}; + +#endif diff --git a/app/config/app_config/developing.h b/app/config/app_config/developing.h new file mode 100644 index 0000000..e66ce08 --- /dev/null +++ b/app/config/app_config/developing.h @@ -0,0 +1,58 @@ +#if (CONFIG_PRODUCT_ID_DEV) + +#define _DEFAULT_DEVICE_NAME_BLE "developing V1" +#define _DEFAULT_DEVICE_NAME_WIFI "developing V1-WIFI" +#define _DEFAULT_DRONE_DATA "MODE1_1.0" + +static cfg_app_t const s_cfg_app_default = { + + .version = APP_CONFIG_DATA_VER, // 配置版本,修改源码中此版本的值后, nvs 已同步的数据被恢复到默认值 + +#if (CONFIG_BUILD_BLE) + .rf_mode = DATA_BRIDGE_RF_MODE_BLE, // 当前的无线数据模式,由按键控制更改 +#elif (CONFIG_BUILD_WIFI) + .rf_mode = DATA_BRIDGE_RF_MODE_WIFI_AP, // 设置数据桥接模式:仅使用 UART <==> WIFI(AP) +#else + .rf_mode = DATA_BRIDGE_RF_MODE_OFF, // 设置数据桥接模式:关闭所有数据接口 +#endif + + .psPassword = "", // 透传密码 + + .device_name_ble = _DEFAULT_DEVICE_NAME_BLE, // BLE 设备名 + + .drone_data = _DEFAULT_DRONE_DATA, // 由 APP 通过命令设置,表示记录配置 + + /* 由 APP 通过命令设置, WIFI 部分的透传设置 */ + .wifi_setting = { + .ardupilot_passthrough_tcp_port = 4278, + .ardupilot_passthrough_udp_port = 14550, + .ardupilot_passthrough_udp_transfer_in_raw_mode = 1, // UDP 连接下, 0: 解析飞控数据帧转发, 1: 直接转发原文(默认) + .wifi_max_connection_num = 1, // WIFI 最多的连接数。范围 MIN_WIFI_STA_NUM..MAX_WIFI_STA_NUM + .uartBaudRate = 115200, // 串口透传波特率 + }, + + .wifi_ap_ipv4 = {192, 168, 1, 1}, // WIFI AP 模式下的 IP 地址 + .app_config_wifi_para.wifi_ap_ssid = _DEFAULT_DEVICE_NAME_WIFI, // WIFI AP 模式下的 SSID + .app_config_wifi_para.wifi_ap_password = "", // WIFI AP 模式下的 密码 + .app_config_wifi_para.wifi_sta_ssid = "eFLY", // WIFI STA 模式下的 SSID + .app_config_wifi_para.wifi_sta_password = "88888888", // WIFI STA 模式下的 密码 + + .udp_port_passthrough_broadcast = 14550, // udp 透传端口(广播端口) TODO + .udp_port_command = 14551, // udp 命令端口(本地端口) + .tcp_port_command = 4279, // tcp 命令端口(本地端口) + .tcp_port_telnet = 23, // tcp telnet 端口(默认23) + .tcp_port_dfu = 4280, // tcp DFU 升级服务端口(本地端口) + .tcp_port_msc = 4281, // tcp 黑匣子服务端口(本地端口) + + /* 功能模块开关 */ + .capacity = { + .pwrup_light = 0, // 短亮黄灯表示启动(方便观察是否有重启) + .ble = 1, // 使用 BLE + .ap = 0, // 使用 WIFI AP 模式 + .sta = 0, // 使用 WIFI STA 模式 + }, + .armed2close_rf_sw = 1, // 解锁后自动关闭射频功能的开关,0:关, 1:开 + .product_id = PRODUCT_ID, // 产品 ID +}; + +#endif diff --git a/app/config/board_config.c b/app/config/board_config.c index 6e3af64..659ae63 100644 --- a/app/config/board_config.c +++ b/app/config/board_config.c @@ -1,8 +1,13 @@ #include "board_config.h" - +#include "config/version.h" #include "driver/uart.h" +#include "hal/spi_types.h" +#include "utils/crc.h" -#define CONFIG_SYS_LOG_LEVEL SYS_LOG_LEVEL_DBG +#include "esp_partition.h" +#include "spi_flash_mmap.h" + +#define CONFIG_SYS_LOG_LEVEL SYS_LOG_LEVEL_ERR #define SYS_LOG_DOMAIN "CFG" #define CONS_ABORT() #include "sys_log.h" @@ -11,4 +16,239 @@ #include "board_config/devkit_esp32c3.h" #include "board_config/devkit_esp32s3.h" +typedef struct +{ + uint8_t license_code[CONFIG_LICENSE_CODE_LENGTH]; // 在烧录器设置的保存滚码的字段 + uint32_t crc_32; // 从 size 成员开始到数据结束的 crc32 校验值 + uint16_t size; // 整个数据结构的长度 + uint16_t version; // 仅指本结构的版本代号 + cfg_board_t cfg; // 板载配置数据 +} __cfg_board_partition_t; + +static const esp_partition_t *s_partition_hdl; +const __cfg_board_partition_t *p_cfg_board_partition; const cfg_board_t *g_cfg_board = &s_cfg_board_default; +const uint8_t *g_license_code; + +static void _read_partition_data(__cfg_board_partition_t *out, const cfg_board_t *cfg); +static const uint8_t *_read_legacy_license_code(void); +static uint32_t _get_data_crc(const __cfg_board_partition_t *cfg); + +/** + * @brief 初始化,使 g_cfg_board 指向正确的 + * @retval 0 指向了自定义分区中的数据 + * @retval 1 在自定义分区中的数据校验失败,指向了默认的配置 + */ +int board_cfg_init(void) +{ + s_partition_hdl = esp_partition_find_first((esp_partition_type_t)CONFIG_CFG_BOARD_PARTITION_TYPE, + (esp_partition_subtype_t)CONFIG_CFG_BOARD_PARTITION_SUBTYPE, + CONFIG_CFG_BOARD_PARTITION_NAME); + + *(int *)g_sys_log_on = 1; + + if (s_partition_hdl == NULL) + { + g_license_code = _read_legacy_license_code(); + SYS_LOG_ERR("esp_partition_find_first() fail"); + return 1; + } + + const void *cfg_board_partition; + esp_partition_mmap_handle_t mmp_data_handle; + esp_err_t err = esp_partition_mmap(s_partition_hdl, 0, 0x1000, ESP_PARTITION_MMAP_DATA, &cfg_board_partition, &mmp_data_handle); + if (err != ESP_OK) + { + g_license_code = _read_legacy_license_code(); + SYS_LOG_ERR("esp_partition_mmap() fail"); + return 1; + } + + p_cfg_board_partition = cfg_board_partition; + g_license_code = p_cfg_board_partition->license_code; + + if (p_cfg_board_partition->version != BOARD_CONFIG_DATA_VER || + p_cfg_board_partition->size != sizeof(__cfg_board_partition_t) || + p_cfg_board_partition->crc_32 != _get_data_crc(p_cfg_board_partition)) + { + SYS_LOG_WRN("no config data, using default config"); + return 1; + } + else + { + g_cfg_board = &p_cfg_board_partition->cfg; + SYS_LOG_INF("config data checked valid"); + + /* 如果代码区中的析载数据长度大于已有的数据区配置长度,则把多出来的部分添加到数据区中 */ + if (sizeof(__cfg_board_partition_t) > p_cfg_board_partition->size && + p_cfg_board_partition->size > sizeof(__cfg_board_partition_t) - sizeof(cfg_board_t)) + { + cfg_board_t new_cfg; + memcpy(&new_cfg, &s_cfg_board_default, sizeof(new_cfg)); // 复制新的默认配置 + memcpy(&new_cfg, g_cfg_board, sizeof(cfg_board_t) - (sizeof(__cfg_board_partition_t) - p_cfg_board_partition->size)); // 用旧的固化数据覆已有部分 + board_cfg_fresh(&new_cfg); // 固化新配置 + } + return 0; + } +} + +/** + * @brief 写入激活码 + * + * @param license_code + * @return int + */ +int board_license_fresh(const uint8_t license_code[CONFIG_LICENSE_CODE_LENGTH]) +{ + if (p_cfg_board_partition == NULL) + { + SYS_LOG_WRN("never exec board_cfg_init()"); + return -1; + } + + __cfg_board_partition_t cfg_buf_partition; + memcpy(&cfg_buf_partition, p_cfg_board_partition, sizeof(cfg_buf_partition)); + memcpy(&cfg_buf_partition.license_code[0], license_code, sizeof(cfg_buf_partition.license_code)); + cfg_buf_partition.version = BOARD_CONFIG_DATA_VER; + cfg_buf_partition.size = sizeof(cfg_buf_partition); + + esp_err_t err; + err = esp_partition_erase_range(s_partition_hdl, 0, 0x1000); + if (err != ESP_OK) + { + SYS_LOG_WRN("esp_partition_erase_range() fail. err: %d", err); + return 1; + } + err = esp_partition_write_raw(s_partition_hdl, 0, &cfg_buf_partition, sizeof(cfg_buf_partition)); + if (err != ESP_OK) + { + SYS_LOG_WRN("esp_partition_read_raw() fail. err: %d", err); + return 1; + } + + SYS_LOG_INF("done."); + return 0; +} + +/** + * @brief 把板载数据固化到自定义分区中。 + * 当数据被固化后,除非擦除芯片,否则在代码区的配置数据不生效 + * + * @param cfg + * @return int + */ +int board_cfg_fresh(const cfg_board_t *cfg) +{ + if (p_cfg_board_partition == NULL) + { + SYS_LOG_WRN("never exec board_cfg_init()"); + return -1; + } + + __cfg_board_partition_t cfg_buf_partition; + _read_partition_data(&cfg_buf_partition, cfg); + + esp_err_t err; + err = esp_partition_erase_range(s_partition_hdl, 0, 0x1000); + if (err != ESP_OK) + { + SYS_LOG_WRN("esp_partition_erase_range() fail. err: %d", err); + return 1; + } + err = esp_partition_write_raw(s_partition_hdl, 0, &cfg_buf_partition, sizeof(cfg_buf_partition)); + if (err != ESP_OK) + { + SYS_LOG_WRN("esp_partition_read_raw() fail. err: %d", err); + return 1; + } + + g_cfg_board = &p_cfg_board_partition->cfg; + SYS_LOG_INF("done."); + return 0; +} + +/** + * @brief 通过终端打印 cfg 的二进制数,用于帮助制作对应 __cfg_board_partition_t 的 bin 文件。 + * + * @param cfg 如果为 NULL 时则表示打印保存在自定义分区位置的数据 + * @retval 0 操作成功 + * @retval -1 数据校验无效 + */ +int board_cfg_dump(const cfg_board_t *cfg) +{ + if (p_cfg_board_partition == NULL) + { + SYS_LOG_WRN("never exec board_cfg_init()"); + return -1; + } + + if (cfg == NULL) + { + if (p_cfg_board_partition->size > 0x1000 || + p_cfg_board_partition->size < CONFIG_LICENSE_CODE_LENGTH || + p_cfg_board_partition->crc_32 != _get_data_crc(p_cfg_board_partition)) + { + SYS_LOG_WRN("No internal records, currently displayed as default configuration"); + } + + cfg = &s_cfg_board_default; + } + + __cfg_board_partition_t cfg_buf_partition; + _read_partition_data(&cfg_buf_partition, cfg); + + SYS_LOG_WRN("The data in the custom partition is as follows %u bytes:\r\n", sizeof(cfg_buf_partition)); + int i = 0; + int len; + const uint8_t *p = (uint8_t *)&cfg_buf_partition; + + SYS_PRINT("/* license code */\r\n"); + len = sizeof(cfg_buf_partition.license_code); + for (i = 0; i < len; i++) + SYS_PRINT("0x%02X,%s", *p++, i + 1 < len ? " " : "\r\n"); + + SYS_PRINT("/* crc, size, version */\r\n"); + len = cfg_buf_partition.size - sizeof(cfg_buf_partition.license_code) - sizeof(cfg_buf_partition.cfg); + for (i = 0; i < len; i++) + SYS_PRINT("0x%02X,%s", *p++, i + 1 < len ? " " : "\r\n"); + + SYS_PRINT("/* board configuration (from %s area) */\r\n", cfg == &s_cfg_board_default ? "code" : "data"); + len = sizeof(cfg_buf_partition.cfg); + for (i = 0; i < len; i++) + SYS_PRINT("0x%02X,%s", *p++, i + 1 < len ? " " : "\r\n"); + + SYS_PRINT("\r\n"); + + return 0; +} + +static void _read_partition_data(__cfg_board_partition_t *out, const cfg_board_t *cfg) +{ + memcpy(out, p_cfg_board_partition, sizeof(*out)); + memcpy(&out->cfg, cfg, sizeof(out->cfg)); + out->version = BOARD_CONFIG_DATA_VER; + out->size = sizeof(*out); + out->crc_32 = _get_data_crc(out); +} + +/** + * @brief 旧的版本的激活码所在(历史遗留) + * + * @return const uint8_t* license_code + */ +static const uint8_t *_read_legacy_license_code(void) +{ + spi_flash_mmap_handle_t handle; + const uint32_t LEGACY_LICENSE_CODE_START_ADDR = 0x3E0000; + uint32_t start = LEGACY_LICENSE_CODE_START_ADDR; + uint32_t end = LEGACY_LICENSE_CODE_START_ADDR + CONFIG_LICENSE_CODE_LENGTH; + const void *license_code; + spi_flash_mmap(LEGACY_LICENSE_CODE_START_ADDR, end - start, SPI_FLASH_MMAP_DATA, &license_code, &handle); + return license_code; +} + +static uint32_t _get_data_crc(const __cfg_board_partition_t *cfg) +{ + int cfg_size = cfg->size - sizeof(cfg->license_code) - sizeof(cfg->crc_32); + return crc32_calc(0, &cfg->size, cfg_size); +} diff --git a/app/config/board_config.h b/app/config/board_config.h index bb7665f..a7bb65f 100644 --- a/app/config/board_config.h +++ b/app/config/board_config.h @@ -19,25 +19,94 @@ #pragma once +#define CONFIG_LICENSE_CODE_LENGTH 16 /* 在烧录器设置的保存滚码的字段长度(g_license_code 指向的数据的实际长度) */ + +#define CONFIG_CFG_BOARD_PARTITION_NAME "bcfg" /* 对应自定义分区表 partitions.csv 中的 Name 字段 */ +#define CONFIG_CFG_BOARD_PARTITION_TYPE 0x40 /* 对应自定义分区表 partitions.csv 中的 Type 字段 */ +#define CONFIG_CFG_BOARD_PARTITION_SUBTYPE 0x00 /* 对应自定义分区表 partitions.csv 中的 SubType 字段 */ + #include "drivers/chip/_hal.h" #include "sdkconfig.h" #define GPIO_USED(pin) ((pin) < 255) +#if (CONFIG_IDF_TARGET_ESP32S3) /* 带 OTG 的平台 */ +#define CAP_USBOTG 1 +#else +#define CAP_USBOTG 0 +#endif + typedef struct // 对应 GPIO 单个引脚的输入/输出配置的基础定义 { - uint8_t pin; // 引脚号 (0~254, 255 表示不使用) --OK + uint8_t pin; // 引脚号 (0~254, 255 表示不使用) uint8_t en_lev; // 触发电平 } cfg_board_pin_io_t; +typedef struct // LED: WS2812 +{ + hal_id_t spi_id; // 模拟 PWM 用的 SPI + uint8_t strip_pin[4]; // 用于驱动灯带的输出引脚。[0] 为默认脚,[1..3] 为附加的另外几路,非 255 表示有效,程序根据这些脚判断接多少根灯带 + uint8_t bat_led_pin; // 用于指示电池电量的 LED 灯,非 255 表示有效 + uint8_t rf_status_pin; // 板上用于指示状态的 RGB 灯珠,非 255 表示有效 +} cfg_board_led_spi_t; + +typedef enum __packed // 用于与飞控通讯的数据接口 +{ + CFG_BOARD_FC_PORT_UART = 0, // 使用串口与飞控通讯 + CFG_BOARD_FC_PORT_USBH, // 使用 USB Host 与飞控通讯 +} cfg_board_fc_port_t; + typedef struct // 数据结构一旦定下不可随意变更 { + char firmware_str[7]; + char platform_str[13]; + char board_name[32]; + /* 硬件描述类 */ hal_uart_hdl_t uart_console; // 控制台 - cfg_board_pin_io_t key_boot; // 启动按键 + hal_uart_hdl_t uart_fc; // 与飞控连接的串口 + cfg_board_led_spi_t led_spi; // LED + + cfg_board_fc_port_t fc_port_type; // 用于与飞控通讯的数据接口 + + struct // ISP 控制脚 + { + uint8_t nrst; // 复位控制引脚 + uint8_t boot0; // boot0 控制引脚 + } io_isp; + + struct // USB OTG 引脚 + { + uint8_t usb_dm; + uint8_t usb_dp; + } io_usb; + + cfg_board_pin_io_t detect_usb; // 输入检测:检测飞控 USB 插入,有效电平为插入电平 + + cfg_board_pin_io_t key_reset; // 按键检测:开机时长安 10 秒恢复出厂设置的按键,有效电平为按下电平 + cfg_board_pin_io_t key_led_strip_switch; // 按键检测:切换灯带效果,适用于灯带固件,有效电平为按下电平 + cfg_board_pin_io_t key_rf_switch; // 按键检测:切换射频数据接口,适用于固定翼无线 USB 板,有效电平为按下电平 + cfg_board_pin_io_t key_9v_switch; // 按键检测:9伏电源开关控制,适用 F7V4, 有效电平为按下电平 + + cfg_board_pin_io_t sw_led_strip; // 灯带控制主机的切换控制引脚,有效电平为切换为本模块控制的电平 + cfg_board_pin_io_t sw_pwr_9v; // 9V 电源输出控制引脚,有效电平为打开 9V 电源的电平 + cfg_board_pin_io_t sw_usb; // USB 切换的模拟开关,有效电平为切换为本模块控制的电平 + + cfg_board_pin_io_t led_rf_status; // 射频指示灯,单色 LED. 引脚号值为 0 时,射频连接状态通过灯带展示,值为非 0 时,射频连接状态通过这个引脚控制的 LED 展示,有效电平为点亮电平 + cfg_board_pin_io_t led_strip_on; // 灯带控制主机切换的指示灯,有效电平为点亮电平 + cfg_board_pin_io_t led_bat[4]; // 电池电量指示灯,有效电平为点亮电平 /* 产品功能描述类 */ } cfg_board_t; +extern const uint8_t *g_license_code; // 在烧录器设置的保存滚码的字段,其对应的长度为 CONFIG_LICENSE_CODE_LENGTH + extern const cfg_board_t *g_cfg_board; // 配置数据 + +int board_cfg_init(void); // 初始化,使 g_cfg_board 指向正确的 + +int board_license_fresh(const uint8_t license_code[CONFIG_LICENSE_CODE_LENGTH]); // 更新激活码 +int board_cfg_fresh(const cfg_board_t *cfg); // 更新设置 + +int board_cfg_dump(const cfg_board_t *cfg); diff --git a/app/config/board_config/devkit_esp32c2.h b/app/config/board_config/devkit_esp32c2.h index a2c27ea..bcb1872 100644 --- a/app/config/board_config/devkit_esp32c2.h +++ b/app/config/board_config/devkit_esp32c2.h @@ -3,6 +3,9 @@ #define CONFIG_IDF_TARGET "esp32c2" /* 警告:请使用命令 idf.py set-target <参数> 选择对应的平台 */ static cfg_board_t const s_cfg_board_default = { + .firmware_str = PRODUCT_ID, + .platform_str = CONFIG_IDF_TARGET, + .board_name = "ESP32-C2-Devkitc", /* 控制台串口 */ .uart_console = { .pin_txd = {43, _GPIO_DIR_OUT, _GPIO_PUD_PULL_UP}, @@ -13,10 +16,17 @@ static cfg_board_t const s_cfg_board_default = { }, /* 启动按键 */ - .key_boot = { + .key_reset = { .pin = 9, // 用于切换灯效 .en_lev = 0, // 用于切换灯效 }, + + .led_spi = { + .spi_id = SPI2_HOST, // 模拟 PWM 用的 SPI + .strip_pin = {10, ~0, ~0, ~0}, // 用于驱动灯带的输出引脚 + .bat_led_pin = ~0, // 用于指示电池电量的 LED 灯,非 ~0 表示有效 + .rf_status_pin = ~0, // 板上用于指示状态的 RGB 灯珠,非 ~0 表示有效 + }, }; #endif diff --git a/app/config/board_config/devkit_esp32c3.h b/app/config/board_config/devkit_esp32c3.h index 9b07f22..6cc4b6d 100644 --- a/app/config/board_config/devkit_esp32c3.h +++ b/app/config/board_config/devkit_esp32c3.h @@ -3,6 +3,9 @@ #define CONFIG_IDF_TARGET "esp32c3" /* 警告:请使用命令 idf.py set-target <参数> 选择对应的平台 */ static cfg_board_t const s_cfg_board_default = { + .firmware_str = PRODUCT_ID, + .platform_str = CONFIG_IDF_TARGET, + .board_name = "ESP32-C3-Devkitc", /* 控制台串口 */ .uart_console = { .pin_txd = {43, _GPIO_DIR_OUT, _GPIO_PUD_PULL_UP}, @@ -13,10 +16,17 @@ static cfg_board_t const s_cfg_board_default = { }, /* 启动按键 */ - .key_boot = { + .key_reset = { .pin = 9, // 用于切换灯效 .en_lev = 0, // 用于切换灯效 }, + + .led_spi = { + .spi_id = SPI2_HOST, // 模拟 PWM 用的 SPI + .strip_pin = {18, ~0, ~0, ~0}, // 用于驱动灯带的输出引脚 + .bat_led_pin = ~0, // 用于指示电池电量的 LED 灯,非 ~0 表示有效 + .rf_status_pin = ~0, // 板上用于指示状态的 RGB 灯珠,非 ~0 表示有效 + }, }; #endif diff --git a/app/config/board_config/devkit_esp32s3.h b/app/config/board_config/devkit_esp32s3.h index 6e666ef..106fbf2 100644 --- a/app/config/board_config/devkit_esp32s3.h +++ b/app/config/board_config/devkit_esp32s3.h @@ -3,6 +3,9 @@ #define CONFIG_IDF_TARGET "esp32s3" /* 警告:请使用命令 idf.py set-target <参数> 选择对应的平台 */ static cfg_board_t const s_cfg_board_default = { + .firmware_str = PRODUCT_ID, + .platform_str = CONFIG_IDF_TARGET, + .board_name = "ESP32-S3-Devkitc", /* 控制台串口 */ .uart_console = { .pin_txd = {255, _GPIO_DIR_OUT, _GPIO_PUD_PULL_UP}, @@ -13,10 +16,17 @@ static cfg_board_t const s_cfg_board_default = { }, /* 启动按键 */ - .key_boot = { + .key_reset = { .pin = 0, // 用于切换灯效 .en_lev = 0, // 用于切换灯效 }, + + .led_spi = { + .spi_id = SPI2_HOST, // 模拟 PWM 用的 SPI + .strip_pin = {18, ~0, ~0, ~0}, // 用于驱动灯带的输出引脚 + .bat_led_pin = ~0, // 用于指示电池电量的 LED 灯,非 ~0 表示有效 + .rf_status_pin = ~0, // 板上用于指示状态的 RGB 灯珠,非 ~0 表示有效 + }, }; #endif diff --git a/app/config/version.h b/app/config/version.h index 6dd476b..164915d 100644 --- a/app/config/version.h +++ b/app/config/version.h @@ -10,23 +10,21 @@ #define BOARD_CONFIG_DATA_VER 0x0101 /* 对应 cfg_board_t::version */ #define APP_CONFIG_DATA_VER 0 /* 对应 cfg_app_t::version */ -#define LEDSTRIP_CONFIG_DATA_VER 0x00000001 /* 对应 cfg_led_strip_t ::version */ -#define FW_VERSION_MAIN 0 +#define FW_VERSION_MAIN 1 #define FW_VERSION_MINOR 0 -#define FW_VERSION_BUILD 1 +#define FW_VERSION_BUILD 0 -#elif (CONFIG_PRODUCT_ID_SBLED1) +#elif (CONFIG_PRODUCT_ID_SBDEMO) -#define PRODUCT_ID "SBLED1" +#define PRODUCT_ID "SBDEMO" #define BOARD_CONFIG_DATA_VER 0x0101 /* 对应 cfg_board_t::version */ #define APP_CONFIG_DATA_VER 0 /* 对应 cfg_app_t::version */ -#define LEDSTRIP_CONFIG_DATA_VER 0x00000001 /* 对应 cfg_led_strip_t ::version */ -#define FW_VERSION_MAIN 6 -#define FW_VERSION_MINOR 1 -#define FW_VERSION_BUILD 2 +#define FW_VERSION_MAIN 1 +#define FW_VERSION_MINOR 0 +#define FW_VERSION_BUILD 0 #else #error 未定义配置 diff --git a/app/console.c b/app/console.c index a22dcd8..93a9b0f 100644 --- a/app/console.c +++ b/app/console.c @@ -97,6 +97,10 @@ void shell_start(void *arg) sh_register_external(_port_sh_vprint_fn); vset_init(&g_uart_handle_vt100, arg); + /* 注册固件信息查询命令 */ + extern void app_info_register(void); + app_info_register(); + /* 注册系统命令 */ extern void soc_shell_register(void); soc_shell_register(); diff --git a/app/drivers/ws2812_spi/ws2812_spi.c b/app/drivers/ws2812_spi/ws2812_spi.c index 1e5ed0e..d396fe0 100755 --- a/app/drivers/ws2812_spi/ws2812_spi.c +++ b/app/drivers/ws2812_spi/ws2812_spi.c @@ -21,9 +21,9 @@ #include "driver/gpio.h" #include "soc/spi_periph.h" #include "soc/gpio_periph.h" +#include "config/board_config.h" #include "ws2812_spi.h" -#include "drivers/pin_io/pin_io.h" #include "os/os.h" @@ -145,7 +145,7 @@ ws2812_spi_led_buf_t *ws2812_spi_led_new_buf(uint16_t leds) led_strip->pow_tbl[0] = 0; /* 使所有数据合法化 */ - ws2812_spi_led_strip_clear(led_strip); + ws2812_spi_led_strip_clear((ws2812_spi_led_buf_t *)led_strip); return (ws2812_spi_led_buf_t *)led_strip; } @@ -167,7 +167,7 @@ void ws2812_spi_led_strip_clear(ws2812_spi_led_buf_t *buf) memset(led_strip->dma_buffer, WS_RESET, led_strip->buffer_size); for (int i = 0; i < led_strip->max_led_num; i++) { - ws2812_spi_led_strip_set_pixel(led_strip, i, 0, 0, 0); + ws2812_spi_led_strip_set_pixel((ws2812_spi_led_buf_t *)led_strip, i, 0, 0, 0); } } @@ -256,16 +256,12 @@ int ws2812_spi_led_strip_refresh(ws2812_spi_led_buf_t *buf, uint32_t leds, uint8 { if (s_cm.spi_pin > 0) { - cfg_board_pin_io_t cfg_pin = {.pin = s_cm.spi_pin, .en_lev = 1}; - pin_cfg_output(&cfg_pin); - pin_set_valid(&cfg_pin, true); + drv_gpio_pin_configure(s_cm.spi_pin, _GPIO_DIR_OUT, _GPIO_PUD_PULL_UP); } if (pin > 0) { - cfg_board_pin_io_t cfg_pin = {.pin = pin, .en_lev = 1}; - pin_cfg_output(&cfg_pin); - gpio_set_direction(pin, GPIO_MODE_INPUT_OUTPUT); + drv_gpio_pin_configure(pin, _GPIO_DIR_OUT, _GPIO_PUD_PULL_UP); esp_rom_gpio_connect_out_signal(pin, spi_periph_signal[s_cm.host_id].spid_out, true, false); } s_cm.spi_pin = pin; diff --git a/app/drivers/ws2812_spi/ws2812_spi_shell.c b/app/drivers/ws2812_spi/ws2812_spi_shell.c index 1d471ba..03d123c 100644 --- a/app/drivers/ws2812_spi/ws2812_spi_shell.c +++ b/app/drivers/ws2812_spi/ws2812_spi_shell.c @@ -2,8 +2,7 @@ #include "ws2812_spi.h" #include "shell/sh.h" #include "sh_vset.h" - -#include +#include "hal/spi_types.h" static ws2812_spi_led_buf_t *s_led_strip; static uint32_t s_value; @@ -24,9 +23,11 @@ SH_DEF_CMD( void ws2812_spi_shell_register(void) { +#if defined(CONFIG_LED_STRIP_MAX_LEDS) ws2812_spi_led_strip_init(SPI2_HOST, CONFIG_LED_STRIP_MAX_LEDS); s_led_strip = ws2812_spi_led_new_buf(CONFIG_LED_STRIP_MAX_LEDS); sh_register_cmd(&_register_cmd); +#endif } void ws2812_spi_shell_unregister(void) diff --git a/app/utils/crc.c b/app/utils/crc.c new file mode 100644 index 0000000..b40a1d8 --- /dev/null +++ b/app/utils/crc.c @@ -0,0 +1,165 @@ +#include "crc.h" + +static uint8_t const s_crc8_tbl[256] = { + 0x00, 0x91, 0xe3, 0x72, 0x07, 0x96, 0xe4, 0x75, + 0x0e, 0x9f, 0xed, 0x7c, 0x09, 0x98, 0xea, 0x7b, + 0x1c, 0x8d, 0xff, 0x6e, 0x1b, 0x8a, 0xf8, 0x69, + 0x12, 0x83, 0xf1, 0x60, 0x15, 0x84, 0xf6, 0x67, + 0x38, 0xa9, 0xdb, 0x4a, 0x3f, 0xae, 0xdc, 0x4d, + 0x36, 0xa7, 0xd5, 0x44, 0x31, 0xa0, 0xd2, 0x43, + 0x24, 0xb5, 0xc7, 0x56, 0x23, 0xb2, 0xc0, 0x51, + 0x2a, 0xbb, 0xc9, 0x58, 0x2d, 0xbc, 0xce, 0x5f, + 0x70, 0xe1, 0x93, 0x02, 0x77, 0xe6, 0x94, 0x05, + 0x7e, 0xef, 0x9d, 0x0c, 0x79, 0xe8, 0x9a, 0x0b, + 0x6c, 0xfd, 0x8f, 0x1e, 0x6b, 0xfa, 0x88, 0x19, + 0x62, 0xf3, 0x81, 0x10, 0x65, 0xf4, 0x86, 0x17, + 0x48, 0xd9, 0xab, 0x3a, 0x4f, 0xde, 0xac, 0x3d, + 0x46, 0xd7, 0xa5, 0x34, 0x41, 0xd0, 0xa2, 0x33, + 0x54, 0xc5, 0xb7, 0x26, 0x53, 0xc2, 0xb0, 0x21, + 0x5a, 0xcb, 0xb9, 0x28, 0x5d, 0xcc, 0xbe, 0x2f, + 0xe0, 0x71, 0x03, 0x92, 0xe7, 0x76, 0x04, 0x95, + 0xee, 0x7f, 0x0d, 0x9c, 0xe9, 0x78, 0x0a, 0x9b, + 0xfc, 0x6d, 0x1f, 0x8e, 0xfb, 0x6a, 0x18, 0x89, + 0xf2, 0x63, 0x11, 0x80, 0xf5, 0x64, 0x16, 0x87, + 0xd8, 0x49, 0x3b, 0xaa, 0xdf, 0x4e, 0x3c, 0xad, + 0xd6, 0x47, 0x35, 0xa4, 0xd1, 0x40, 0x32, 0xa3, + 0xc4, 0x55, 0x27, 0xb6, 0xc3, 0x52, 0x20, 0xb1, + 0xca, 0x5b, 0x29, 0xb8, 0xcd, 0x5c, 0x2e, 0xbf, + 0x90, 0x01, 0x73, 0xe2, 0x97, 0x06, 0x74, 0xe5, + 0x9e, 0x0f, 0x7d, 0xec, 0x99, 0x08, 0x7a, 0xeb, + 0x8c, 0x1d, 0x6f, 0xfe, 0x8b, 0x1a, 0x68, 0xf9, + 0x82, 0x13, 0x61, 0xf0, 0x85, 0x14, 0x66, 0xf7, + 0xa8, 0x39, 0x4b, 0xda, 0xaf, 0x3e, 0x4c, 0xdd, + 0xa6, 0x37, 0x45, 0xd4, 0xa1, 0x30, 0x42, 0xd3, + 0xb4, 0x25, 0x57, 0xc6, 0xb3, 0x22, 0x50, 0xc1, + 0xba, 0x2b, 0x59, 0xc8, 0xbd, 0x2c, 0x5e, 0xcf}; + +uint8_t crc8_calc(const void *data, size_t len) +{ + const uint8_t *ptr = data; + uint8_t crc = 0; + while (len-- != 0) + { + crc = s_crc8_tbl[crc ^ *ptr++]; + } + return crc; +} + +uint16_t crc16_calc(const void *data, size_t length) +{ + const uint8_t *ptr = data; + uint16_t crc = 0xffff; + while (length--) + { + crc = crc ^ ((uint16_t)*ptr++ & 0xff); + for (size_t i = 0; i < 8; i++) + { + if (crc & 0x1) + crc = crc >> 1 ^ 0x0000a001; + else + crc = crc >> 1; + } + } + + return (crc & 0xFFFF); +} + +static struct +{ + uint32_t Width : 6; // CRC比特数 + uint32_t RefIn : 1; // 表示待测数据的每个字节:false 表示不按位颠倒,true 表示按位颠倒 + uint32_t RefOut : 1; // 表示计算结束后:flase 表示输出结果不按位颠倒,true 表示输出结果按位颠倒 + uint32_t Poly; // 生成项(未颠倒的) + uint32_t Init; // 初始值(注意此值不是计算输出值,而时计算过程的中间值,即对应的查询表值) + uint32_t xOrOut; // 表示计算结束后:进行的 XOR 的值 + uint32_t Check; // 表示 CRC 使用这个模型计算字符串 "123456789" 后得到的 CRC 值 + uint32_t Tab[1 << 8]; // 生成的查询表 +} const s_crc32_tab = { + 32, + 1, + 1, + 0x4C11DB7, + 0xFFFFFFFF, + 0xFFFFFFFF, + 0xCBF43926, + { + // CRC Table + 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, // 0x00H ~ 0x03H + 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, // 0x04H ~ 0x07H + 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, // 0x08H ~ 0x0BH + 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, // 0x0CH ~ 0x0FH + 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, // 0x10H ~ 0x13H + 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, // 0x14H ~ 0x17H + 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, // 0x18H ~ 0x1BH + 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, // 0x1CH ~ 0x1FH + 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, // 0x20H ~ 0x23H + 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, // 0x24H ~ 0x27H + 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, // 0x28H ~ 0x2BH + 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, // 0x2CH ~ 0x2FH + 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, // 0x30H ~ 0x33H + 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, // 0x34H ~ 0x37H + 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, // 0x38H ~ 0x3BH + 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, // 0x3CH ~ 0x3FH + 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, // 0x40H ~ 0x43H + 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, // 0x44H ~ 0x47H + 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, // 0x48H ~ 0x4BH + 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, // 0x4CH ~ 0x4FH + 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, // 0x50H ~ 0x53H + 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, // 0x54H ~ 0x57H + 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, // 0x58H ~ 0x5BH + 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, // 0x5CH ~ 0x5FH + 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, // 0x60H ~ 0x63H + 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, // 0x64H ~ 0x67H + 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, // 0x68H ~ 0x6BH + 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, // 0x6CH ~ 0x6FH + 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, // 0x70H ~ 0x73H + 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, // 0x74H ~ 0x77H + 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, // 0x78H ~ 0x7BH + 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, // 0x7CH ~ 0x7FH + 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, // 0x80H ~ 0x83H + 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, // 0x84H ~ 0x87H + 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, // 0x88H ~ 0x8BH + 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, // 0x8CH ~ 0x8FH + 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, // 0x90H ~ 0x93H + 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, // 0x94H ~ 0x97H + 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, // 0x98H ~ 0x9BH + 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, // 0x9CH ~ 0x9FH + 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, // 0xA0H ~ 0xA3H + 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, // 0xA4H ~ 0xA7H + 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, // 0xA8H ~ 0xABH + 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, // 0xACH ~ 0xAFH + 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, // 0xB0H ~ 0xB3H + 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, // 0xB4H ~ 0xB7H + 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, // 0xB8H ~ 0xBBH + 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, // 0xBCH ~ 0xBFH + 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, // 0xC0H ~ 0xC3H + 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, // 0xC4H ~ 0xC7H + 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, // 0xC8H ~ 0xCBH + 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, // 0xCCH ~ 0xCFH + 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, // 0xD0H ~ 0xD3H + 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, // 0xD4H ~ 0xD7H + 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, // 0xD8H ~ 0xDBH + 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, // 0xDCH ~ 0xDFH + 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, // 0xE0H ~ 0xE3H + 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, // 0xE4H ~ 0xE7H + 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, // 0xE8H ~ 0xEBH + 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, // 0xECH ~ 0xEFH + 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, // 0xF0H ~ 0xF3H + 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, // 0xF4H ~ 0xF7H + 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, // 0xF8H ~ 0xFBH + 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D // 0xFCH ~ 0xFFH + }, +}; + +uint32_t crc32_calc(uint32_t crc, const void *src, size_t size) +{ + uint32_t remainder = crc ^ s_crc32_tab.xOrOut; + uint32_t i; + + for (i = 0; i < size; i++) + { + remainder = s_crc32_tab.Tab[((uint8_t *)src)[i] ^ (remainder & 0xFF)] ^ (remainder >> 8); + } + + return (remainder ^ s_crc32_tab.xOrOut); +} diff --git a/app/utils/crc.h b/app/utils/crc.h new file mode 100644 index 0000000..739b3e4 --- /dev/null +++ b/app/utils/crc.h @@ -0,0 +1,13 @@ +#ifndef __CRC_H__ +#define __CRC_H__ + +#include +#include + +uint8_t crc8_calc(const void *data, size_t length); + +uint16_t crc16_calc(const void *data, size_t length); + +uint32_t crc32_calc(uint32_t crc, const void *src, size_t size); + +#endif diff --git a/components/system/include/sys_log.h b/components/system/include/sys_log.h index 123bb49..6942225 100755 --- a/components/system/include/sys_log.h +++ b/components/system/include/sys_log.h @@ -50,7 +50,8 @@ extern "C" #define SYS_VSNPRINT vsnprintf #ifndef CONFIG_SYS_LOG_CONS_ON -#define CONFIG_SYS_LOG_CONS_ON (CONFIG_SYS_LOG_LEVEL > SYS_LOG_LEVEL_OFF) /* 打印控制总开关 */ +extern const int *g_sys_log_on; +#define CONFIG_SYS_LOG_CONS_ON ((CONFIG_SYS_LOG_LEVEL > SYS_LOG_LEVEL_OFF) && (*g_sys_log_on != 0)) /* 打印控制总开关 */ #endif #ifndef CONFIG_SYS_LOG_DUMP_ON diff --git a/main/Kconfig.projbuild b/main/Kconfig.projbuild index 80c1036..112fac8 100644 --- a/main/Kconfig.projbuild +++ b/main/Kconfig.projbuild @@ -30,10 +30,15 @@ menu "APP 配置" bool "识别号: SB-DEV 开发中" help 未定义的具体产品描述符 + + config PRODUCT_ID_SBDEMO + bool "识别号: SBDEMO 开发中" + help + 仅用于开发示例的产品描述符 endchoice menuconfig CAP_LED_STRIP - depends on PRODUCT_ID_DEV || PRODUCT_ID_SBLED1 + depends on PRODUCT_ID_DEV || PRODUCT_ID_SBDEMO bool "编译灯带模块" default n rsource "../app/led_strip/Kconfig" diff --git a/sdkconfig_defaults/sdkconfig.release_ESPEVB_esp32c2_eFly.defaults b/sdkconfig_defaults/sdkconfig.release_ESPEVB_esp32c2_evb.defaults similarity index 100% rename from sdkconfig_defaults/sdkconfig.release_ESPEVB_esp32c2_eFly.defaults rename to sdkconfig_defaults/sdkconfig.release_ESPEVB_esp32c2_evb.defaults diff --git a/sdkconfig_defaults/sdkconfig.release_ESPEVB_esp32c3_ledstrip.defaults b/sdkconfig_defaults/sdkconfig.release_ESPEVB_esp32c3_evb.defaults similarity index 96% rename from sdkconfig_defaults/sdkconfig.release_ESPEVB_esp32c3_ledstrip.defaults rename to sdkconfig_defaults/sdkconfig.release_ESPEVB_esp32c3_evb.defaults index 58a4806..5003490 100644 --- a/sdkconfig_defaults/sdkconfig.release_ESPEVB_esp32c3_ledstrip.defaults +++ b/sdkconfig_defaults/sdkconfig.release_ESPEVB_esp32c3_evb.defaults @@ -5,7 +5,7 @@ CONFIG_IDF_TARGET="esp32c3" CONFIG_BOOTLOADER_LOG_LEVEL_WARN=y CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y CONFIG_PARTITION_TABLE_CUSTOM=y -CONFIG_PRODUCT_ID_SBLED1=y +CONFIG_PRODUCT_ID_SBDEMO=y CONFIG_CAP_LED_STRIP=y CONFIG_LED_STRIP_SKIP_PWRUP=y CONFIG_BT_ENABLED=y diff --git a/sdkconfig_defaults/sdkconfig.release_SBDEMO_esp32s3.defaults b/sdkconfig_defaults/sdkconfig.release_SBDEMO_esp32s3.defaults new file mode 100644 index 0000000..7171e59 --- /dev/null +++ b/sdkconfig_defaults/sdkconfig.release_SBDEMO_esp32s3.defaults @@ -0,0 +1,29 @@ +# This file was generated using idf.py save-defconfig. It can be edited manually. +# Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration +# +CONFIG_IDF_TARGET="esp32s3" +CONFIG_BOOTLOADER_LOG_LEVEL_WARN=y +CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_BOARD_NAME_DEVKIT_ESP32S3=y +CONFIG_PRODUCT_ID_SBF7V4=y +CONFIG_CAP_LED_STRIP=y +CONFIG_BUILD_WIFI=y +CONFIG_BT_ENABLED=y +CONFIG_BT_BLUEDROID_PINNED_TO_CORE_1=y +CONFIG_BT_LOG_HCI_TRACE_LEVEL_ERROR=y +CONFIG_BT_LOG_APPL_TRACE_LEVEL_ERROR=y +CONFIG_BT_BLE_42_FEATURES_SUPPORTED=y +CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_P21=y +CONFIG_ESP_HTTP_CLIENT_ENABLE_DIGEST_AUTH=y +CONFIG_ESP_SLEEP_POWER_DOWN_FLASH=y +CONFIG_ESP_IPC_TASK_STACK_SIZE=1536 +CONFIG_ESP_TIMER_TASK_STACK_SIZE=2048 +CONFIG_FREERTOS_HZ=1000 +CONFIG_FREERTOS_IDLE_TASK_STACKSIZE=1536 +CONFIG_FREERTOS_ENABLE_BACKWARD_COMPATIBILITY=y +CONFIG_FREERTOS_TIMER_TASK_PRIORITY=24 +CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=1536 +CONFIG_FREERTOS_VTASKLIST_INCLUDE_COREID=y +CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS=y +CONFIG_LOG_DEFAULT_LEVEL_NONE=y