Browse Source

2023.12.25出货版本

wanghechen 10 tháng trước cách đây
mục cha
commit
281cc91e4d
100 tập tin đã thay đổi với 32812 bổ sung0 xóa
  1. 59 0
      .gitignore
  2. 8 0
      CMakeLists.txt
  3. 6 0
      components/Decection/CMakeLists.txt
  4. 266 0
      components/Decection/Decection.c
  5. 19 0
      components/Decection/include/Decection.h
  6. 8 0
      components/EPD/CMakeLists.txt
  7. 1974 0
      components/EPD/EPD.c
  8. 1456 0
      components/EPD/GUI_Paint.c
  9. BIN
      components/EPD/connect.jpg
  10. BIN
      components/EPD/disconnect.jpg
  11. 15747 0
      components/EPD/image.c
  12. 158 0
      components/EPD/include/EPD.h
  13. 315 0
      components/EPD/include/GUI_Paint.h
  14. 97 0
      components/EPD/include/fonts.h
  15. 136 0
      components/EPD/include/qr_consts.h
  16. 87 0
      components/EPD/include/qr_encode.h
  17. 1085 0
      components/EPD/qr_encode.c
  18. BIN
      components/EPD/墨水屏驱动芯片UC8179C.pdf
  19. BIN
      components/EPD/色块.zip
  20. BIN
      components/EPD/色块/__MACOSX/._在线.jpg
  21. BIN
      components/EPD/色块/__MACOSX/._离线.jpg
  22. 7 0
      components/EPD/色块/online.c
  23. BIN
      components/EPD/色块/在线.jpg
  24. BIN
      components/EPD/色块/离线.jpg
  25. 7 0
      components/FONT_LIB/CMakeLists.txt
  26. 284 0
      components/FONT_LIB/FONT_LIB.c
  27. 42 0
      components/FONT_LIB/include/FONT_LIB.h
  28. 118 0
      components/FONT_LIB/include/GT5DL32A3W.h
  29. 65 0
      components/FONT_LIB/include/QRCode.h
  30. 3 0
      components/LED/CMakeLists.txt
  31. 124 0
      components/LED/LED.c
  32. 56 0
      components/LED/include/LED.h
  33. 4 0
      components/LORA/CMakeLists.txt
  34. 3184 0
      components/LORA/LORA.c
  35. 318 0
      components/LORA/include/LORA.h
  36. 101 0
      components/LORA/include/y_ringbuf.h
  37. 656 0
      components/LORA/y_ringbuf.c
  38. 17 0
      components/USER_SPIFFS/CMakeLists.txt
  39. 252 0
      components/USER_SPIFFS/SPIFFS.c
  40. 193 0
      components/USER_SPIFFS/include/SPIFFS.h
  41. BIN
      components/USER_SPIFFS/spiffs/left_display.bin
  42. BIN
      components/USER_SPIFFS/spiffs/right_display.bin
  43. BIN
      components/USER_SPIFFS/spiffs/yc_data.bin
  44. 88 0
      components/button/CHANGELOG.md
  45. 15 0
      components/button/CMakeLists.txt
  46. 59 0
      components/button/Kconfig
  47. 33 0
      components/button/README.md
  48. 341 0
      components/button/button_adc.c
  49. 48 0
      components/button/button_gpio.c
  50. 59 0
      components/button/button_matrix.c
  51. 76 0
      components/button/include/button_adc.h
  52. 54 0
      components/button/include/button_gpio.h
  53. 80 0
      components/button/include/button_matrix.h
  54. 291 0
      components/button/include/iot_button.h
  55. 142 0
      components/button/include/user_button.h
  56. 709 0
      components/button/iot_button.c
  57. 202 0
      components/button/license.txt
  58. 9 0
      components/button/test_apps/CMakeLists.txt
  59. 9 0
      components/button/test_apps/main/CMakeLists.txt
  60. 704 0
      components/button/test_apps/main/button_test.c
  61. 5 0
      components/button/test_apps/main/component.mk
  62. 23 0
      components/button/test_apps/pytest_button.py
  63. 9 0
      components/button/test_apps/sdkconfig.defaults
  64. 605 0
      components/button/user_button.c
  65. 3 0
      components/c_linked_list/CMakeLists.txt
  66. 186 0
      components/c_linked_list/README.md
  67. BIN
      components/c_linked_list/figures/MergeSort.png
  68. BIN
      components/c_linked_list/figures/deleteNode.png
  69. BIN
      components/c_linked_list/figures/newNode.png
  70. BIN
      components/c_linked_list/figures/orderInsert.png
  71. BIN
      components/c_linked_list/figures/postInsert.png
  72. BIN
      components/c_linked_list/figures/preInsert.png
  73. 331 0
      components/c_linked_list/list.c
  74. 84 0
      components/c_linked_list/list.h
  75. 1 0
      components/esp_ble_ota/.component_hash
  76. 60 0
      components/esp_ble_ota/CHANGELOG.md
  77. 29 0
      components/esp_ble_ota/CMakeLists.txt
  78. 25 0
      components/esp_ble_ota/Kconfig
  79. 15 0
      components/esp_ble_ota/README.md
  80. 7 0
      components/esp_ble_ota/README_CN.md
  81. 6 0
      components/esp_ble_ota/examples/ble_ota/CMakeLists.txt
  82. 104 0
      components/esp_ble_ota/examples/ble_ota/README.md
  83. 64 0
      components/esp_ble_ota/examples/ble_ota/README_CN.md
  84. 9 0
      components/esp_ble_ota/examples/ble_ota/main/CMakeLists.txt
  85. 70 0
      components/esp_ble_ota/examples/ble_ota/main/Kconfig.projbuild
  86. 365 0
      components/esp_ble_ota/examples/ble_ota/main/app_main.c
  87. 6 0
      components/esp_ble_ota/examples/ble_ota/main/idf_component.yml
  88. 8 0
      components/esp_ble_ota/examples/ble_ota/partitions.csv
  89. 40 0
      components/esp_ble_ota/examples/ble_ota/rsa_key/private.pem
  90. 1 0
      components/esp_ble_ota/examples/ble_ota/sdkconfig.ci.bluedroid
  91. 1 0
      components/esp_ble_ota/examples/ble_ota/sdkconfig.ci.nimble
  92. 22 0
      components/esp_ble_ota/examples/ble_ota/sdkconfig.defaults
  93. 181 0
      components/esp_ble_ota/include/ble_ota.h
  94. 4 0
      components/esp_ble_ota/include/esp_ble_ota.h
  95. 368 0
      components/esp_ble_ota/include/manager.h
  96. 57 0
      components/esp_ble_ota/include/ota.h
  97. 104 0
      components/esp_ble_ota/include/scheme_ble.h
  98. 202 0
      components/esp_ble_ota/license.txt
  99. 46 0
      components/esp_ble_ota/proto-c/constants.pb-c.c
  100. 0 0
      components/esp_ble_ota/proto-c/constants.pb-c.h

+ 59 - 0
.gitignore

@@ -0,0 +1,59 @@
+#不需要的文件不要上传
+build
+managed_components
+sdkconfig.old
+python_Tool
+.vscode
+README.en.md
+# Prerequisites
+*.d
+
+# Object files
+*.o
+*.ko
+*.obj
+*.elf
+
+# Linker output
+*.ilk
+*.map
+*.exp
+
+# Precompiled Headers
+*.gch
+*.pch
+
+# Libraries
+*.lib
+*.a
+*.la
+*.lo
+
+# Shared objects (inc. Windows DLLs)
+*.dll
+*.so
+*.so.*
+*.dylib
+
+# Executables
+*.exe
+*.out
+*.app
+*.i*86
+*.x86_64
+*.hex
+
+# Debug files
+*.dSYM/
+*.su
+*.idb
+*.pdb
+
+# Kernel Module Compile Results
+*.mod*
+*.cmd
+.tmp_versions/
+modules.order
+Module.symvers
+Mkfile.old
+dkms.conf

+ 8 - 0
CMakeLists.txt

@@ -0,0 +1,8 @@
+# The following lines of boilerplate have to be in your project's CMakeLists
+# in this exact order for cmake to work correctly
+cmake_minimum_required(VERSION 3.16)
+
+include($ENV{IDF_PATH}/tools/cmake/project.cmake)
+
+
+project(ulp-riscv-adc-example)

+ 6 - 0
components/Decection/CMakeLists.txt

@@ -0,0 +1,6 @@
+idf_component_register(SRCS "Decection.c"
+                    INCLUDE_DIRS "include"
+                    REQUIRES    "esp_adc"
+                                "driver"
+                                "hal"
+                                "USER_SPIFFS")

+ 266 - 0
components/Decection/Decection.c

@@ -0,0 +1,266 @@
+#include <stdio.h>
+#include "Decection.h"
+
+
+#include "esp_log.h"
+#include "esp_adc/adc_oneshot.h"
+#include "esp_adc/adc_cali.h"
+#include "esp_adc/adc_cali_scheme.h"
+#include "esp_heap_caps.h"
+#include "hal/adc_hal.h"
+#include "driver/gpio.h"
+#include "esp_sleep.h"
+const static char *TAG = "Decection";
+
+//#include "LED.h"
+
+
+static int adc_raw[2][10];
+static int voltage[2][10];
+
+#define USER_KEY_CHANNEL  ADC_CHANNEL_7
+
+#define USER_ADC_CHANNEL  ADC_CHANNEL_2
+#define USER_ADC_UNIT   ADC_UNIT_1
+static bool adc_calibration_init(adc_unit_t unit, adc_atten_t atten, adc_cali_handle_t *out_handle);
+static void adc_calibration_deinit(adc_cali_handle_t handle);
+
+adc_cali_handle_t adc2_cali_handle = NULL;
+
+// void adc1_deinit(adc_oneshot_unit_handle_t adc_handle)
+// {
+//     ESP_ERROR_CHECK(adc_oneshot_del_unit(adc_handle));
+//     if (do_calibration) {
+//         adc_calibration_deinit(adc2_cali_handle);
+//     }
+// }
+int adc_read_power_pin(adc_oneshot_unit_handle_t adc_handle)
+{
+    bool do_calibration;
+
+    adc_oneshot_chan_cfg_t config = {
+    .bitwidth = ADC_BITWIDTH_DEFAULT,
+    .atten = ADC_ATTEN_DB_11,
+    };
+
+    do_calibration = adc_calibration_init(USER_ADC_UNIT, ADC_ATTEN_DB_11, &adc2_cali_handle);
+    //-------------ADC Config---------------//
+    adc_oneshot_config_channel(adc_handle, USER_ADC_CHANNEL, &config);
+    //adc_ll_set_power_manage(ADC_POWER_BY_FSM);
+    adc_oneshot_read(adc_handle, USER_ADC_CHANNEL, &adc_raw[0][0]);
+    //adc_ll_set_power_manage(ADC_POWER_SW_OFF);
+
+    ESP_LOGD(TAG, "ADC%d Channel[%d] Raw Data: %d", USER_ADC_UNIT + 1, USER_ADC_CHANNEL, adc_raw[0][0]);
+
+    if (do_calibration) {
+        ESP_ERROR_CHECK(adc_cali_raw_to_voltage(adc2_cali_handle, adc_raw[0][0], &voltage[0][0]));
+        ESP_LOGD(TAG, "ADC%d Channel[%d] Cali Voltage: %d mV", USER_ADC_UNIT + 1, USER_ADC_CHANNEL, voltage[0][0]);
+        adc_calibration_deinit(adc2_cali_handle);
+    }   
+    int batt_percent;
+
+    //电量范围映射
+    float result_voltage = (float)(voltage[0][0]) / 1000.0/*4095.0 * 3.3*/; // 假设ADC参考电压为3.3V
+    float battery_percentage = ((result_voltage - 1.8) / (2.2 - 1.8)) * 100.0;
+    // 限制电量范围在0%到100%之间
+    if (battery_percentage < 0) {
+    battery_percentage = 0;
+    } else if (battery_percentage > 100) {
+    battery_percentage = 100;
+    }
+
+    batt_percent = (int )battery_percentage; 
+
+    #if 0
+        ESP_LOGI(TAG,"batt_percent = [%d],voltage[0][0] = [%d]",batt_percent, voltage[0][0]);
+    #else
+        printf("batt_percent = [%d],voltage[0][0] = [%d],adc value %d\r\n",batt_percent, voltage[0][0],(int)battery_percentage);
+    #endif
+    return batt_percent;
+}
+
+
+
+
+int adc_read_left_key_pin(adc_oneshot_unit_handle_t adc_handle)
+{
+    bool do_calibration;
+
+    adc_oneshot_chan_cfg_t config = {
+    .bitwidth = ADC_BITWIDTH_DEFAULT,
+    .atten = ADC_ATTEN_DB_0,
+    };
+
+    do_calibration = adc_calibration_init(USER_ADC_UNIT, ADC_ATTEN_DB_0, &adc2_cali_handle);
+    //-------------ADC Config---------------//
+    ESP_ERROR_CHECK(adc_oneshot_config_channel(adc_handle, USER_KEY_CHANNEL, &config));
+    //adc_ll_set_power_manage(ADC_POWER_BY_FSM);
+    ESP_ERROR_CHECK(adc_oneshot_read(adc_handle, USER_KEY_CHANNEL, &adc_raw[0][0]));
+    //adc_ll_set_power_manage(ADC_POWER_SW_OFF);
+
+    ESP_LOGD(TAG, "ADC%d Channel[%d] Raw Data: %d", USER_ADC_UNIT + 1, USER_KEY_CHANNEL, adc_raw[0][0]);
+
+    if (do_calibration) {
+        ESP_ERROR_CHECK(adc_cali_raw_to_voltage(adc2_cali_handle, adc_raw[0][0], &voltage[0][0]));
+        ESP_LOGD(TAG, "ADC%d Channel[%d] Cali Voltage: %d mV", USER_ADC_UNIT + 1, USER_KEY_CHANNEL, voltage[0][0]);
+        adc_calibration_deinit(adc2_cali_handle);
+    }   
+    int batt_percent;
+
+    #if 0
+    //电量范围映射
+    float result_voltage = (float)(voltage[0][0]) / 1000.0/*4095.0 * 3.3*/; // 假设ADC参考电压为3.3V
+    float battery_percentage = ((result_voltage - 1.5) / (2.2 - 1.5)) * 100.0;
+    // 限制电量范围在0%到100%之间
+    if (battery_percentage < 0) {
+    battery_percentage = 0;
+    } else if (battery_percentage > 100) {
+    battery_percentage = 100;
+    }
+
+    batt_percent = (int )battery_percentage; 
+
+    #if 0
+        ESP_LOGI(TAG,"batt_percent = [%d],voltage[0][0] = [%d]",batt_percent, voltage[0][0]);
+    #else
+        printf("batt_percent = [%d],voltage[0][0] = [%d],adc value %d\r\n",batt_percent, voltage[0][0],(int)battery_percentage);
+    #endif
+    #else
+
+
+     printf("left key voltage:%d\r\n",voltage[0][0]);
+
+    batt_percent = voltage[0][0];
+
+
+    #endif
+    return batt_percent;
+}
+extern adc_oneshot_unit_handle_t adc1_handle;
+
+
+int read_battery_voltage()
+{
+    printf("读电量\n");
+    return adc_read_power_pin(adc1_handle);
+}
+
+/*---------------------------------------------------------------
+        ADC Calibration
+---------------------------------------------------------------*/
+static bool adc_calibration_init(adc_unit_t unit, adc_atten_t atten, adc_cali_handle_t *out_handle)
+{
+    adc_cali_handle_t handle = NULL;
+    esp_err_t ret = ESP_FAIL;
+    bool calibrated = false;
+
+    if (!calibrated) {
+        ESP_LOGD(TAG, "calibration scheme version is %s", "Curve Fitting");
+        adc_cali_curve_fitting_config_t cali_config = {
+            .unit_id = unit,
+            .atten = atten,
+            .bitwidth = ADC_BITWIDTH_DEFAULT,
+        };
+        ret = adc_cali_create_scheme_curve_fitting(&cali_config, &handle);
+        if (ret == ESP_OK) {
+            calibrated = true;
+        }
+    }
+
+    *out_handle = handle;
+    if (ret == ESP_OK) {
+        ESP_LOGD(TAG, "Calibration Success");
+    } else if (ret == ESP_ERR_NOT_SUPPORTED || !calibrated) {
+        ESP_LOGW(TAG, "eFuse not burnt, skip software calibration");
+    } else {
+        ESP_LOGE(TAG, "Invalid arg or no memory");
+    }
+
+    return calibrated;
+}
+
+static void adc_calibration_deinit(adc_cali_handle_t handle)
+{
+    ESP_LOGD(TAG, "deregister %s calibration scheme", "Curve Fitting");
+    ESP_ERROR_CHECK(adc_cali_delete_scheme_curve_fitting(handle));
+}
+
+
+ //返回充电中状态
+int decection_state_0(void)
+{
+    return gpio_get_level(2);
+}
+
+//返回充满状态
+int decection_state_1(void)
+{
+
+    return 0;//gpio_get_level(38);
+}
+
+
+
+
+
+
+
+
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "freertos/queue.h"
+#include "SPIFFS.h"
+extern QueueHandle_t right_screen_queue;
+extern RTC_FAST_ATTR Machine_info_t   Machine_info;
+static void IRAM_ATTR gpio_isr_handler(void* arg)
+{
+    uint32_t gpio_num = (uint32_t) arg;
+    switch(gpio_num)
+    {
+
+      case  2:
+
+           // beep_blink(100,3);
+            //printf("----------------gpio_num%ld中断------------------------\r\n",gpio_num);
+        break;
+
+        case 38:
+
+           // beep_blink(100,4);//rintf("----------------gpio_num%ld中断------------------------\r\n",gpio_num);
+        break;
+
+        default :break;
+    }
+    // if(Machine_info.power_status!=0)
+    // {
+    //     xQueueSendFromISR(right_screen_queue, &Machine_info, NULL);
+    // }
+
+}
+
+void decection_charging_init(void)
+{
+    //zero-initialize the config structure.
+    gpio_config_t io_conf = {};
+    //interrupt of rising edge
+    io_conf.intr_type = GPIO_INTR_NEGEDGE;  //下降沿
+    //bit mask of the pins
+    io_conf.pin_bit_mask =((1ULL<<2) | (1ULL<<38));
+    //set as input mode
+    io_conf.mode = GPIO_MODE_INPUT;
+    io_conf.pull_up_en = GPIO_PULLUP_DISABLE; // 禁用上拉电阻
+    io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE; // 禁用下拉电阻
+    //configure GPIO with the given settings
+    gpio_config(&io_conf);
+
+    ESP_LOGW(TAG, "decection_charging_init");
+   //install gpio isr service
+    gpio_install_isr_service(ESP_INTR_FLAG_EDGE);
+    gpio_isr_handler_add(2, gpio_isr_handler, (void*) 2);
+    gpio_isr_handler_add(38, gpio_isr_handler, (void*) 38);
+}
+// void decection_charging_deinit(void)
+// {
+//     gpio_isr_handler_remove(2);
+//     gpio_isr_handler_remove(38);
+// }

+ 19 - 0
components/Decection/include/Decection.h

@@ -0,0 +1,19 @@
+#ifndef _DETECTION_H_
+#define _DETECTION_H_
+
+#include "esp_adc/adc_oneshot.h"
+#include "esp_adc/adc_cali.h"
+#include "esp_adc/adc_cali_scheme.h"
+
+
+int adc_read_power_pin(adc_oneshot_unit_handle_t adc_handle);
+int read_battery_voltage();
+
+
+int decection_state_0(void);
+int decection_state_1(void);
+void decection_charging_init(void);
+void decection_charging_deinit(void);
+
+int adc_read_left_key_pin(adc_oneshot_unit_handle_t adc_handle);
+#endif/*_DETECTION_H_*/

+ 8 - 0
components/EPD/CMakeLists.txt

@@ -0,0 +1,8 @@
+idf_component_register(SRCS "EPD.c"
+                            "GUI_Paint.c"
+                            "image.c"
+                            "qr_encode.c"
+                    INCLUDE_DIRS "include"  "../../main/"
+                    REQUIRES    "driver"
+                                "FONT_LIB"
+                                "esp_timer")

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 1974 - 0
components/EPD/EPD.c


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 1456 - 0
components/EPD/GUI_Paint.c


BIN
components/EPD/connect.jpg


BIN
components/EPD/disconnect.jpg


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 15747 - 0
components/EPD/image.c


+ 158 - 0
components/EPD/include/EPD.h

@@ -0,0 +1,158 @@
+#ifndef _EPD_H_
+#define _EPD_H_
+#include <inttypes.h>
+#include "driver/gpio.h"
+
+
+#define SOFTWARE_SPI_ENABLE 0
+
+#define QUICK_DISPLAY  0     //快刷
+
+#define HARDWARE_SPI        1
+#if HARDWARE_SPI    
+
+#include "driver/spi_master.h"
+
+#define SPI_MAX_LEN 3200
+
+#define EPD_HOST    SPI2_HOST
+
+#define PIN_SPI_MOSI 45
+#define PIN_SPI_CLK  48
+
+#define PIN_L_CS   39
+#define PIN_L_DC   40
+#define PIN_L_RST  41
+#define PIN_L_BUSY  42
+
+#define PIN_R_CS   47
+#define PIN_R_DC   21
+#define PIN_R_RST  14
+#define PIN_R_BUSY  13
+
+#if !SOFTWARE_SPI_ENABLE //
+#define PIN_EPD_OUTPUT      ((1ULL<<PIN_L_CS)|(1ULL<<PIN_L_DC)|(1ULL<<PIN_L_RST)|(1ULL<<PIN_R_CS)|(1ULL<<PIN_R_DC)|(1ULL<<PIN_R_RST))
+#else
+#define PIN_EPD_OUTPUT      ((1ULL<<PIN_SPI_MOSI)|(1ULL<<PIN_SPI_CLK)|(1ULL<<PIN_L_CS)|(1ULL<<PIN_L_DC)|(1ULL<<PIN_L_RST)|(1ULL<<PIN_R_CS)|(1ULL<<PIN_R_DC)|(1ULL<<PIN_R_RST))
+#endif
+
+
+#define PIN_EPD_INPUT        ((1ULL<<PIN_L_BUSY)|(1ULL<<PIN_R_BUSY))
+
+
+typedef enum {
+    SCREEN_LEFT     = 1,
+    SCREEN_RIGHT    = 2,
+}screen_t;
+
+
+typedef struct {
+    uint8_t cmd;
+    uint8_t data[16];
+    uint8_t databytes; //No of data in data; bit 7 = delay after set; 0xFF = end of cmds.
+} epd_init_cmd_t;
+
+
+extern spi_device_handle_t epd_spi;
+
+void epd_write_cmd(screen_t screen, unsigned char command,bool keep_cs_active);
+void epd_write_data(screen_t screen, const uint8_t *data, int len);
+void epd_init(void);
+void epd_display(screen_t screen,const unsigned char* picData);
+void epd_cache(screen_t screen,const unsigned char* picData);
+void epd_display_partal(uint16_t Xstart, uint16_t Ystart, uint16_t Xend, uint16_t Yend,
+                         screen_t screen,const unsigned char* picData);
+void epd_partial_cache(uint16_t Xstart, uint16_t Ystart, uint16_t Xend, uint16_t Yend,
+                         screen_t screen,const unsigned char* picData);
+
+
+                         void epd_partial_cache1(uint16_t Xstart, uint16_t Ystart, uint16_t Xend, uint16_t Yend,
+                         screen_t screen,const unsigned char* picData);
+void epd_powerOn_refresh(screen_t screen);
+
+
+void epd_init_cmd(screen_t screen);
+
+void epd_sleep(screen_t screen);
+
+
+bool epd_check_status(screen_t screen);
+void epd_refresh(screen_t screen);
+
+
+void epd_check_power_off(screen_t screen);
+void epd_check_power_on(screen_t screen);
+
+
+void epd_cache_quick_full_screen_refresh(screen_t screen,const unsigned char* old,const unsigned char* new);
+
+void epd_cache_quick_partial_screen_refresh(screen_t screen,uint16_t Xstart, uint16_t Ystart, uint16_t Xend, uint16_t Yend,const unsigned char* old,const unsigned char* new);
+
+unsigned int compressRLE(uint8_t *input, unsigned int size, uint8_t *output);
+// 解压函数
+unsigned int decompressRLE(uint8_t *input, unsigned int size, uint8_t *output);
+
+void deepsleep_epd_check_status(screen_t screen);
+
+void deepsleep_epd_powerOn_refresh_sleep(screen_t screen);
+
+
+
+bool epd_cache_quick(screen_t screen,const unsigned char* old,const unsigned char* new);
+void epd_powerOn_refresh_sleep(screen_t screen);
+
+bool left_refresh_timer_isActive();
+bool right_refresh_timer_isActive();
+
+
+#else /*HARDWARE_SPI*/
+
+
+
+
+#define LOW_LEVEL       0
+#define HIGH_LEVEL      1
+
+struct EPD_INFO_SET
+{
+    char* epd_name;
+    uint8_t sclk_pin;
+    uint8_t sda_pin;
+    uint8_t busy_pin;
+    uint8_t res_pin;
+    uint8_t dc_pin;
+    uint8_t cs_pin;
+};
+
+#define epd_set_level(EPD_PIN, LEVEL)     gpio_set_level(EPD_PIN, LEVEL)
+#define epd_get_level(EPD_PIN)               gpio_get_level(EPD_PIN)
+
+
+
+void epd_screen_init(struct EPD_INFO_SET* epd_pin_set);
+void epd_pin_init(struct EPD_INFO_SET* epd_pin_set);
+void epd_spi_write(struct EPD_INFO_SET* epd_pin_set, unsigned char value);
+void epd_write_cmd(struct EPD_INFO_SET* epd_pin_set, unsigned char command);
+void epd_write_data(struct EPD_INFO_SET* epd_pin_set, unsigned char command);
+void epd_init(struct EPD_INFO_SET* epd_pin_set);
+void epd_check_status(struct EPD_INFO_SET* epd_pin_set);
+void epd_refresh(struct EPD_INFO_SET* epd_pin_set);
+void epd_sleep(struct EPD_INFO_SET* epd_pin_set);
+void epd_clear_black(struct EPD_INFO_SET* epd_pin_set);
+void epd_clear_write(struct EPD_INFO_SET* epd_pin_set);
+
+
+
+
+void epd_display(struct EPD_INFO_SET* epd_pin_set,const unsigned char* picData);
+
+
+void epd_display_partal(uint16_t Xstart, uint16_t Ystart, uint16_t Xend, uint16_t Yend,struct EPD_INFO_SET* epd_pin_set,const unsigned char* picData);
+
+void epd_cache(struct EPD_INFO_SET* epd_pin_set,const unsigned char* picData);
+void epd_partial_cache(uint16_t Xstart, uint16_t Ystart, uint16_t Xend, uint16_t Yend,struct EPD_INFO_SET* epd_pin_set,const unsigned char* picData);
+void epd_powerOn_refresh(struct EPD_INFO_SET* epd_pin_set);
+
+#endif /*HARDWARE_SPI*/
+
+#endif/*_EPD_H_*/

+ 315 - 0
components/EPD/include/GUI_Paint.h

@@ -0,0 +1,315 @@
+/******************************************************************************
+* | File      	:   GUI_Paint.h
+* | Author      :   Waveshare electronics
+* | Function    :	Achieve drawing: draw points, lines, boxes, circles and
+*                   their size, solid dotted line, solid rectangle hollow
+*                   rectangle, solid circle hollow circle.
+* | Info        :
+*   Achieve display characters: Display a single character, string, number
+*   Achieve time display: adaptive size display time minutes and seconds
+*----------------
+* |	This version:   V3.0
+* | Date        :   2019-04-18
+* | Info        :
+* -----------------------------------------------------------------------------
+* V3.0(2019-04-18):
+* 1.Change: 
+*    Paint_DrawPoint(..., DOT_STYLE DOT_STYLE)
+* => Paint_DrawPoint(..., DOT_STYLE Dot_Style)
+*    Paint_DrawLine(..., LINE_STYLE Line_Style, DOT_PIXEL Dot_Pixel)
+* => Paint_DrawLine(..., DOT_PIXEL Line_width, LINE_STYLE Line_Style)
+*    Paint_DrawRectangle(..., DRAW_FILL Filled, DOT_PIXEL Dot_Pixel)
+* => Paint_DrawRectangle(..., DOT_PIXEL Line_width, DRAW_FILL Draw_Fill)
+*    Paint_DrawCircle(..., DRAW_FILL Draw_Fill, DOT_PIXEL Dot_Pixel)
+* => Paint_DrawCircle(..., DOT_PIXEL Line_width, DRAW_FILL Draw_Filll)
+*
+* -----------------------------------------------------------------------------
+* V2.0(2018-11-15):
+* 1.add: Paint_NewImage()
+*    Create an image's properties
+* 2.add: Paint_SelectImage()
+*    Select the picture to be drawn
+* 3.add: Paint_SetRotate()
+*    Set the direction of the cache    
+* 4.add: Paint_RotateImage() 
+*    Can flip the picture, Support 0-360 degrees, 
+*    but only 90.180.270 rotation is better
+* 4.add: Paint_SetMirroring() 
+*    Can Mirroring the picture, horizontal, vertical, origin
+* 5.add: Paint_DrawString_CN() 
+*    Can display Chinese(GB1312)   
+*
+* ----------------------------------------------------------------------------- 
+* V1.0(2018-07-17):
+*   Create library
+*
+* Permission is hereby granted, free of charge, to any person obtaining a copy
+* of this software and associated documnetation files (the "Software"), to deal
+* in the Software without restriction, including without limitation the rights
+* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+* copies of the Software, and to permit persons to  whom the Software is
+* furished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included in
+* all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+* LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+* THE SOFTWARE.
+*
+******************************************************************************/
+#ifndef __GUI_PAINT_H
+#define __GUI_PAINT_H
+
+#include "esp_log.h"
+
+
+#define UBYTE   uint8_t
+#define UWORD   uint16_t
+#define UDOUBLE uint32_t
+//whc set start
+
+// extern const unsigned char gImage_right[38880];
+// extern const unsigned char gImage_mid[38880];
+// extern const unsigned char gImage_left[38880];
+// extern const unsigned char gImage_baoyang[600];
+// extern const unsigned char gImage_yunxing[600];
+// extern const unsigned char gImage_tingji[600];
+// extern const unsigned char gImage_dailiao[600];
+// extern const unsigned char gImage_guzhang[600];
+// extern const unsigned char gImage_fengcun[600];
+//extern const unsigned char gImage_in_battery[16];
+
+
+extern const unsigned char gImage_none_people[75];
+extern const unsigned char gImage_people[75];
+extern const unsigned char gImage_no_sound[72];
+extern const unsigned char gImage_sound[72];
+extern const unsigned char gImage_battery[90];
+
+
+
+extern const unsigned char gImage_right_[15360];
+extern const unsigned char gImage_mid_[15360];
+extern const unsigned char gImage_left_[15360];
+extern const unsigned char gImage_button[576];
+extern const unsigned char gImage_button_128x64[1024];
+extern const unsigned char gImage_nfc[264];
+extern const unsigned char gImage_bat_ing[90];
+extern const unsigned char gImage_charging[18];
+extern const unsigned char gImage_filled[18];
+
+
+extern const unsigned char gImage_left_instructions[38880]; 
+extern const unsigned char gImage_right_instruction[38880]; 
+
+extern const unsigned char gImage_low_batt[8162];
+
+
+extern const unsigned char gImage_online[90];
+
+
+
+
+typedef struct _font_type_t
+{
+    char *font_type_name;
+    uint16_t Font_lib_type;
+    uint16_t Font_Width;
+    uint16_t Font_Height;  
+}FONT_TYPE_T;
+
+//whc set end
+typedef struct _tFont
+{    
+  uint8_t *table;
+  uint16_t Width;
+  uint16_t Height;
+  uint8_t  font_size;
+  uint16_t font_num;
+  
+} sFONT;
+
+
+
+//#define LOG_TAG "GUI_PAINT"
+#define Debug(INFO, ... ) ESP_LOGD("GUI_PAINT", INFO, ##__VA_ARGS__)
+
+/**
+ * Image attributes
+**/
+typedef struct {
+    UBYTE *Image;
+    UWORD Width;
+    UWORD Height;
+    UWORD WidthMemory;
+    UWORD HeightMemory;
+    UWORD Color;
+    UWORD Rotate;
+    UWORD Mirror;
+    UWORD WidthByte;
+    UWORD HeightByte;
+    UWORD Scale;
+} PAINT;
+
+extern PAINT Paint_info;
+// extern PAINT Paint_left_info;
+// extern PAINT Paint_right_info;
+/**
+ * Display rotate
+**/
+#define ROTATE_0            0
+#define ROTATE_90           90
+#define ROTATE_180          180
+#define ROTATE_270          270
+
+/**
+ * Display Flip
+**/
+typedef enum {
+    MIRROR_NONE  = 0x00,
+    MIRROR_HORIZONTAL = 0x01,
+    MIRROR_VERTICAL = 0x02,
+    MIRROR_ORIGIN = 0x03,
+} MIRROR_IMAGE;
+#define MIRROR_IMAGE_DFT MIRROR_NONE
+
+/**
+ * image color
+**/
+#define WHITE          0x00
+#define BLACK          0xff
+#define RED            BLACK
+
+//
+#define FONT_FOREGROUND     BLACK
+#define FONT_BACKGROUND     WHITE
+
+#define IMAGE_BACKGROUND    WHITE
+#define PAINT_FOREGROUND     BLACK
+#define PAINT_BACKGROUND     WHITE
+
+#define TRUE 1
+#define FALSE 0
+
+//4 Gray level
+#define  GRAY1 0x03 //Blackest
+#define  GRAY2 0x02
+#define  GRAY3 0x01 //gray
+#define  GRAY4 0x00 //white
+
+/**
+ * The size of the point
+**/
+typedef enum {
+    DOT_PIXEL_1X1  = 1,		// 1 x 1
+    DOT_PIXEL_2X2  , 		// 2 X 2
+    DOT_PIXEL_3X3  ,		// 3 X 3
+    DOT_PIXEL_4X4  ,		// 4 X 4
+    DOT_PIXEL_5X5  , 		// 5 X 5
+    DOT_PIXEL_6X6  , 		// 6 X 6
+    DOT_PIXEL_7X7  , 		// 7 X 7
+    DOT_PIXEL_8X8  , 		// 8 X 8
+} DOT_PIXEL;
+#define DOT_PIXEL_DFT  DOT_PIXEL_1X1  //Default dot pilex
+
+/**
+ * Point size fill style
+**/
+typedef enum {
+    DOT_FILL_AROUND  = 1,		// dot pixel 1 x 1
+    DOT_FILL_RIGHTUP  , 		// dot pixel 2 X 2
+} DOT_STYLE;
+#define DOT_STYLE_DFT  DOT_FILL_AROUND  //Default dot pilex
+
+/**
+ * Line style, solid or dashed
+**/
+typedef enum {
+    LINE_STYLE_SOLID = 0,
+    LINE_STYLE_DOTTED,
+    LINE_STYLE_DOTTED_2,
+} LINE_STYLE;
+
+/**
+ * Whether the graphic is filled
+**/
+typedef enum {
+    DRAW_FILL_EMPTY = 0,
+    DRAW_FILL_FULL,
+} DRAW_FILL;
+
+/**
+ * Custom structure of a time attribute
+**/
+typedef struct {
+    UWORD Year;  //0000
+    UBYTE  Month; //1 - 12
+    UBYTE  Day;   //1 - 30
+    UBYTE  Hour;  //0 - 23
+    UBYTE  Min;   //0 - 59
+    UBYTE  Sec;   //0 - 59
+} PAINT_TIME;
+
+extern PAINT_TIME Paint_time_info;
+
+//init and Clear
+void Paint_NewImage(UBYTE *image, UWORD Width, UWORD Height, UWORD Rotate, UWORD Color );
+void Paint_SelectImage(UBYTE *image );
+void Paint_SetRotate(UWORD Rotate );
+void Paint_SetMirroring(UBYTE mirror );
+void Paint_SetPixel(UWORD Xpoint, UWORD Ypoint, UWORD Color );
+void Paint_SetScale(UBYTE scale );
+
+void Paint_Clear(UWORD Color );
+void Paint_ClearWindows(UWORD Xstart, UWORD Ystart, UWORD Xend, UWORD Yend, UWORD Color );
+
+//Drawing
+void Paint_DrawPoint(UWORD Xpoint, UWORD Ypoint, UWORD Color, DOT_PIXEL Dot_Pixel, DOT_STYLE Dot_FillWay );
+void Paint_DrawLine(UWORD Xstart, UWORD Ystart, UWORD Xend, UWORD Yend, UWORD Color_Background, DOT_PIXEL Line_width, LINE_STYLE Line_Style );
+void Paint_DrawRectangle(UWORD Xstart, UWORD Ystart, UWORD Xend, UWORD Yend, UWORD Color_Background, DOT_PIXEL Line_width, DRAW_FILL Draw_Fill );
+void Paint_DrawCircle(UWORD X_Center, UWORD Y_Center, UWORD Radius, UWORD Color_Background, DOT_PIXEL Line_width, DRAW_FILL Draw_Fill );
+
+// //Display string
+void Paint_DrawChar(UWORD Xpoint, UWORD Ypoint, const char Acsii_Char,FONT_TYPE_T* Ascii_type, UWORD Color_Foreground, UWORD Color_Background );
+void Paint_DrawString_EN(UWORD Xstart, UWORD Ystart, const char * pString,FONT_TYPE_T* Ascii_type, UWORD Color_Foreground, UWORD Color_Background );
+void Paint_DrawChar_CN(UWORD Xstart, UWORD Ystart, const char * pString, FONT_TYPE_T* chinese_type,UWORD Color_Foreground, UWORD Color_Background );
+void Paint_DrawString_CN(UWORD Xstart, UWORD Ystart, const char * pString, FONT_TYPE_T* chinese_type, UWORD Color_Foreground, UWORD Color_Background );
+void Paint_DrawTime(UWORD Xstart, UWORD Ystart, PAINT_TIME *pTime, FONT_TYPE_T*  Font_type,UWORD Color_Foreground, UWORD Color_Background );
+void Paint_DrawString_CN48(UWORD Xstart, UWORD Ystart, const char * pString , int is_black);
+void Paint_DrawChar_CN64(UWORD Xstart, UWORD Ystart, const char * pString );
+void Paint_DrawString_CN64(UWORD Xstart, UWORD Ystart, const char * pString );
+// void Paint_DrawString_CN(UWORD Xstart, UWORD Ystart, const char * pString, cFONT* font, UWORD Color_Foreground, UWORD Color_Background);
+// void Paint_DrawNum(UWORD Xpoint, UWORD Ypoint, int32_t Nummber, sFONT* Font, UWORD Color_Foreground, UWORD Color_Background);
+// void Paint_DrawNumDecimals(UWORD Xpoint, UWORD Ypoint, double Nummber, sFONT* Font, UWORD Digit, UWORD Color_Foreground, UWORD Color_Background); // Able to display decimals
+// void Paint_DrawTime(UWORD Xstart, UWORD Ystart, PAINT_TIME *pTime, sFONT* Font, UWORD Color_Foreground, UWORD Color_Background);
+
+//pic
+void Paint_DrawBitMap_Vertical(const unsigned char* image_buffer );
+void Paint_DrawBitMap(const unsigned char* image_buffer );
+void Paint_DrawBitMap_Paste(const unsigned char* image_buffer, UWORD Xstart, UWORD Ystart, UWORD imageWidth, UWORD imageHeight, UBYTE flipColor );
+//void Paint_DrawBitMap_Half(const unsigned char* image_buffer, UBYTE Region);
+//void Paint_DrawBitMap_OneQuarter(const unsigned char* image_buffer, UBYTE Region);
+//void Paint_DrawBitMap_OneEighth(const unsigned char* image_buffer, UBYTE Region);
+void Paint_DrawBitMap_Block(const unsigned char* image_buffer, UBYTE Region );
+
+
+void Paint_DrawBitMap_Paste_t(const unsigned char* image_buffer, UWORD xStart, UWORD yStart, UWORD imageWidth, 
+                                UWORD imageHeight, UBYTE flipColor );
+
+void drawQuadraticBezierCurve(uint8_t* framebuffer, int width, int height,
+                              int x0, int y0, int x1, int y1, int x2, int y2,
+                              uint8_t color, int thickness) ;
+void drawQuadraticBezierCurve_do(uint8_t* framebuffer, int width, int height,
+                                int x0, int y0, int x1, int y1, int x2, int y2,
+                                uint8_t color, int thickness);
+#endif
+
+
+
+
+

+ 97 - 0
components/EPD/include/fonts.h

@@ -0,0 +1,97 @@
+/**
+  ******************************************************************************
+  * @file    fonts.h
+  * @author  MCD Application Team
+  * @version V1.0.0
+  * @date    18-February-2014
+  * @brief   Header for fonts.c file
+  ******************************************************************************
+  * @attention
+  *
+  * <h2><center>&copy; COPYRIGHT(c) 2014 STMicroelectronics</center></h2>
+  *
+  * Redistribution and use in source and binary forms, with or without modification,
+  * are permitted provided that the following conditions are met:
+  *   1. Redistributions of source code must retain the above copyright notice,
+  *      this list of conditions and the following disclaimer.
+  *   2. Redistributions in binary form must reproduce the above copyright notice,
+  *      this list of conditions and the following disclaimer in the documentation
+  *      and/or other materials provided with the distribution.
+  *   3. Neither the name of STMicroelectronics nor the names of its contributors
+  *      may be used to endorse or promote products derived from this software
+  *      without specific prior written permission.
+  *
+  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+  *
+  ******************************************************************************
+  */
+
+/* Define to prevent recursive inclusion -------------------------------------*/
+#ifndef __FONTS_H
+#define __FONTS_H
+
+/*最大字体微软雅黑24 (32x41) */
+#define MAX_HEIGHT_FONT         41
+#define MAX_WIDTH_FONT          32
+#define OFFSET_BITMAP           
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/* Includes ------------------------------------------------------------------*/
+#include <stdint.h>
+
+//ASCII
+typedef struct _tFont
+{    
+  const uint8_t *table;
+  uint16_t Width;
+  uint16_t Height;
+  
+} sFONT;
+
+
+//GB2312
+typedef struct                                          // 汉字字模数据结构
+{
+  unsigned char index[2];                               // 汉字内码索引
+  const char matrix[MAX_HEIGHT_FONT*MAX_WIDTH_FONT/8];  // 点阵码数据
+}CH_CN;
+
+
+typedef struct
+{    
+  const CH_CN *table;
+  uint16_t size;
+  uint16_t ASCII_Width;
+  uint16_t Width;
+  uint16_t Height;
+  
+}cFONT;
+
+extern sFONT Font24;
+extern sFONT Font20;
+extern sFONT Font16;
+extern sFONT Font12;
+extern sFONT Font8;
+
+extern cFONT Font12CN;
+extern cFONT Font24CN;
+#ifdef __cplusplus
+}
+#endif
+  
+#endif /* __FONTS_H */
+ 
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 136 - 0
components/EPD/include/qr_consts.h


+ 87 - 0
components/EPD/include/qr_encode.h

@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2010 Psytec Inc.
+ * Copyright (c) 2012 Alexey Mednyy <swexru@gmail.com>
+ * Copyright (c) 2012 Pavol Rusnak <stick@gk2.sk>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef __QR_ENCODE_H__
+#define __QR_ENCODE_H__
+
+#include <stdlib.h>
+#include <stdint.h>
+
+// Constants
+
+// Error correction level
+#define QR_LEVEL_L         0    //  7% of codewords can be restored
+#define QR_LEVEL_M         1    // 15% of codewords can be restored
+#define QR_LEVEL_Q         2    // 25% of codewords can be restored
+#define QR_LEVEL_H         3    // 30% of codewords can be restored
+
+// Data Mode
+#define QR_MODE_NUMERAL    0    // numbers
+#define QR_MODE_ALPHABET   1    // alphanumberic
+#define QR_MODE_8BIT       2    // rest
+
+// Group version (Model number)
+#define QR_VERSION_S       0    //  1 -  9 (module  21 -  53)
+#define QR_VERSION_M       1    // 10 - 26 (module  57 - 121)
+#define QR_VERSION_L       2    // 27 - 40 (module 125 - 177)
+
+#ifndef QR_MAX_VERSION
+#define QR_MAX_VERSION QR_VERSION_L
+#endif
+
+// Length constants
+
+#if QR_MAX_VERSION == QR_VERSION_S
+#define QR_MAX_MODULESIZE     (9 * 4 + 17)                                       // Maximum number of modules in a side
+#define QR_MAX_ALLCODEWORD    292                                                // Maximum total number of code words
+#define QR_MAX_DATACODEWORD   232                                                // Maximum data word code
+#endif
+
+#if QR_MAX_VERSION == QR_VERSION_M
+#define QR_MAX_MODULESIZE     (26 * 4 + 17)                                      // Maximum number of modules in a side
+#define QR_MAX_ALLCODEWORD    1706                                               // Maximum total number of code words
+#define QR_MAX_DATACODEWORD   1370                                               // Maximum data word code
+#endif
+
+#if QR_MAX_VERSION == QR_VERSION_L
+#define QR_MAX_MODULESIZE     (40 * 4 + 17)                                      // Maximum number of modules in a side
+#define QR_MAX_ALLCODEWORD    3706                                               // Maximum total number of code words
+#define QR_MAX_DATACODEWORD   2956                                               // Maximum data word code
+#endif
+
+#define QR_MAX_BITDATA        ((QR_MAX_MODULESIZE * QR_MAX_MODULESIZE + 7) / 8)  // Maximum size of bit data
+#define QR_MAX_CODEBLOCK      153                                                // Maximum number of block data code word (including RS code word)
+
+//
+// * level - error correction level, use QR_LEVEL_? macros
+// * version - version of the code (1-40), use 0 for autodetection
+// * source - source data
+// * source_len - length of the source data, use 0 when passing zero-terminated string
+// * result - array to write, writes to bits
+//
+// * function returns the size of the square side
+//
+int qr_encode(int level, int version, const char *source, size_t source_len, uint8_t *result);
+
+#endif

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 1085 - 0
components/EPD/qr_encode.c


BIN
components/EPD/墨水屏驱动芯片UC8179C.pdf


BIN
components/EPD/色块.zip


BIN
components/EPD/色块/__MACOSX/._在线.jpg


BIN
components/EPD/色块/__MACOSX/._离线.jpg


+ 7 - 0
components/EPD/色块/online.c

@@ -0,0 +1,7 @@
+const unsigned char gImage_online[90] = { /* 0X00,0X01,0X25,0X00,0X12,0X00, */
+0X3F,0XFF,0XFF,0XFF,0XE0,0X7F,0XFF,0XFF,0XFF,0XF0,0XFF,0XFF,0XFF,0XFF,0XF8,0XFF,
+0XFF,0XFF,0XFF,0XF8,0XFF,0XFF,0XFF,0XFF,0XF8,0XFF,0XFF,0XFF,0XFF,0XF8,0XFF,0XFF,
+0XFF,0XFF,0XF8,0XFF,0XFF,0XFF,0XFF,0XF8,0XFF,0XFF,0XFF,0XFF,0XF8,0XFF,0XFF,0XFF,
+0XFF,0XF8,0XFF,0XFF,0XFF,0XFF,0XF8,0XFF,0XFF,0XFF,0XFF,0XF8,0XFF,0XFF,0XFF,0XFF,
+0XF8,0XFF,0XFF,0XFF,0XFF,0XF8,0XFF,0XFF,0XFF,0XFF,0XF8,0XFF,0XFF,0XFF,0XFF,0XF8,
+0X7F,0XFF,0XFF,0XFF,0XF0,0X3F,0XFF,0XFF,0XFF,0XE0,};

BIN
components/EPD/色块/在线.jpg


BIN
components/EPD/色块/离线.jpg


+ 7 - 0
components/FONT_LIB/CMakeLists.txt

@@ -0,0 +1,7 @@
+idf_component_register(SRCS "FONT_LIB.c" 
+                    INCLUDE_DIRS "include"
+                    REQUIRES    "driver")
+
+target_link_libraries(${COMPONENT_LIB} INTERFACE "${CMAKE_CURRENT_LIST_DIR}/libGT5DL32A3W.a")
+
+

+ 284 - 0
components/FONT_LIB/FONT_LIB.c

@@ -0,0 +1,284 @@
+#include <stdio.h>
+
+#include "GT5DL32A3W.h"
+#include "FONT_LIB.h"
+
+#include "esp_log.h"
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "freertos/queue.h"
+
+static const char *LOG_TAG = "FONTLIB";
+
+
+static void task_delay_ms(uint32_t ms_count)
+{
+    vTaskDelay(ms_count / portTICK_PERIOD_MS);
+}
+
+void gt_font_pin_init()
+{
+    gpio_config_t fontLib_pin_cfg = {};
+
+    fontLib_pin_cfg.intr_type = GPIO_INTR_DISABLE;
+    fontLib_pin_cfg.mode = GPIO_MODE_OUTPUT;
+    fontLib_pin_cfg.pin_bit_mask =FONT_OUTPUT_PIN_SEL;
+    fontLib_pin_cfg.pull_down_en = 0;
+    fontLib_pin_cfg.pull_up_en = 0;
+    gpio_config(&fontLib_pin_cfg);
+
+    fontLib_pin_cfg.intr_type = GPIO_INTR_DISABLE;
+    fontLib_pin_cfg.mode = GPIO_MODE_INPUT;
+    fontLib_pin_cfg.pin_bit_mask =FONT_INPUT_PIN_SEL;
+    fontLib_pin_cfg.pull_down_en = 0;
+    fontLib_pin_cfg.pull_up_en = 1;
+    gpio_config(&fontLib_pin_cfg);    
+}
+
+void font_init()
+{
+    gt_font_pin_init();
+
+    FONT_CLK_1;
+	FONT_SDA_1;
+	FONT_CS_HIGHT;
+
+	font_exit_sleep(); //退出睡眠
+
+	//task_delay_ms(500); 
+
+#if 1
+    uint8_t id = GT_Font_Init();
+    while(id==0)
+    {
+	  	ESP_LOGE(LOG_TAG,"font fail");
+		task_delay_ms(500);
+      	id = GT_Font_Init();
+    }
+#endif
+	ESP_LOGI(LOG_TAG,"font OK");
+
+}
+void font_spi_write(unsigned char value)
+{
+  int i=0,data=value;   
+
+  gpio_set_level(FONT_CLK_PIN,LOW_LEVEL);
+   
+  for(i=0;i<8;i++)
+  {
+    if(data&0x0080)
+    {
+       FONT_SDA_1;
+    }
+      
+    else
+    {
+       FONT_SDA_0;
+    }
+    //ets_delay_us(1);
+    gpio_set_level(FONT_CLK_PIN,HIGH_LEVEL);
+    //ets_delay_us(1);
+    gpio_set_level(FONT_CLK_PIN,LOW_LEVEL);
+    data=((data<<1)&0xff);
+  }
+     
+}
+
+
+void Send_Byte(unsigned char out)
+{	
+	unsigned char i=0;
+
+	for(i=0;i<8;i++)
+	{
+	  FONT_CLK_0;  
+	  if(((out<<i)&0x80)==0)
+		  FONT_SDA_0;   
+	  else
+		  FONT_SDA_1;
+	  FONT_CLK_1;
+  }
+}
+
+
+
+/*******************************************************************************/
+// Get data sub-pro (STM8,STM32等双向口)    SPI接收点阵数据的算法               /
+/*******************************************************************************/
+unsigned char Get_Byte(void)         
+{
+	unsigned char i;
+	unsigned char read_dat= 0,MISO = 0;
+
+	FONT_CLK_1;
+	for(i=0;i<8;i++)
+	{
+		FONT_CLK_0;
+        MISO = FONT_RD_SDA;
+		read_dat = read_dat<<1;
+		if(MISO)				
+			read_dat|=0x01;
+		else
+			read_dat&=0xfe;
+		FONT_CLK_1;
+	}
+	return(read_dat);
+}
+
+
+
+
+
+unsigned char gt_read_data(unsigned char* sendbuf , unsigned char sendlen , unsigned char* receivebuf, unsigned int receivelen)
+{
+
+    unsigned int i;
+    FONT_CS_LOW;
+    for(i = 0; i < sendlen;i++)
+    {
+        Send_Byte(sendbuf[i] );
+    }
+    for(i = 0; i < receivelen;i++)
+    {
+        receivebuf[i] = Get_Byte();
+    }
+
+    FONT_CS_HIGHT;
+    return 1;
+}
+
+
+
+/*******************************************************************************/
+//                    Send address sub-pro (STM8,STM32,51)                                     /
+/*******************************************************************************/
+void SPI_Address(unsigned char AddH,unsigned char AddM,unsigned char AddL) 
+{
+	Send_Byte(AddH);
+	Send_Byte(AddM);
+	Send_Byte(AddL);
+}
+/*******************************************************************************/
+//                    Get N bytes sub-pro  (STM8,STM32,51)                                    //
+/*******************************************************************************/
+//客户自己实现,从address地址读取len个字节的数据并存入到DZ_Data数组当中
+unsigned char r_dat_bat(unsigned long address,unsigned long DataLen,unsigned char *pBuff)
+{
+    unsigned long i;
+    unsigned char addrHigh;
+    unsigned char addrMid;
+    unsigned char addrLow;
+    addrHigh=address>>16;
+    addrMid=address>>8;
+    addrLow=(unsigned char)address;
+  
+	FONT_CS_LOW;          //片选选中字库芯片
+	Send_Byte(0x03);	//普通读取首先送0X03,然后发送地址高八位addrHigh,中八位addrMid,低八位addrLow。
+	SPI_Address(addrHigh,addrMid,addrLow);	
+	for(i=0;i<DataLen;i++)
+	*(pBuff+i)=Get_Byte();
+	FONT_CS_HIGHT;
+	return 0;
+}
+
+//客户自己实现,从address地址读取一个字节的数据并返回该数据
+unsigned char r_dat(unsigned long address){
+	unsigned char buff;
+	unsigned char addrHigh;
+	unsigned char addrMid;
+	unsigned char addrLow;
+	addrHigh=address>>16;
+	addrMid=address>>8;
+	addrLow=(unsigned char)address;
+	
+	FONT_CS_LOW;
+	Send_Byte(0x03);
+	SPI_Address(addrHigh,addrMid,addrLow);
+	buff = Get_Byte();
+	FONT_CS_HIGHT;
+	return buff;
+}
+
+/******************************************************
+客户自己实现, 库文件函数内部需要调用该函数匹配芯片ID号
+根据说明文件或头文件是否需要, 没有就不需要实现
+******************************************************/
+unsigned char CheckID(unsigned char CMD, unsigned long address,
+	unsigned long byte_long,unsigned char *p_arr)
+{
+	unsigned long j;
+	FONT_CS_LOW;
+	Send_Byte(CMD);
+	Send_Byte((unsigned char)((address)>>16)); 
+	Send_Byte((unsigned char)((address)>>8));   
+	Send_Byte((unsigned char)address); 
+	for(j=0;j<byte_long;j++)
+	{
+			p_arr[j]=Get_Byte();
+	}
+	FONT_CS_HIGHT;
+	return 1;
+}
+
+
+void font_into_sleep()
+{
+
+	unsigned long j;
+	FONT_CS_LOW;
+	Send_Byte(0xB9);
+	FONT_CS_HIGHT;
+	//return 1;
+
+}
+
+
+void font_exit_sleep()
+{
+
+	unsigned long j;
+	FONT_CS_LOW;
+	Send_Byte(0xAB);
+	FONT_CS_HIGHT;
+
+}
+
+
+void font_soft_rst()
+{
+		FONT_CS_LOW;
+		Send_Byte(0x66);
+		FONT_CS_HIGHT;
+		task_delay_ms(10);
+		FONT_CS_LOW;
+		Send_Byte(0x99);
+		FONT_CS_HIGHT;
+		task_delay_ms(10);
+}
+// unsigned char test_ASCII_GetData(unsigned char asc,unsigned long ascii_kind,unsigned char *DZ_Data)
+// {
+//     return ASCII_GetData(  asc,  ascii_kind,  DZ_Data);
+// }
+// void test_spi()
+// {
+//     FONT_CS_LOW;
+// 	Send_Byte(0x78);
+//     FONT_CS_HIGHT;
+
+
+//     FONT_CS_LOW;
+// 	Send_Byte(0x56);
+//     FONT_CS_HIGHT;
+
+//     FONT_CS_LOW;
+// 	Send_Byte(0x9f);
+
+//     for(int i=0;i<3;i++)
+// 	{
+// 	   uint8_t id = Get_Byte();
+// 	}
+//     FONT_CS_HIGHT;
+
+// }
+

+ 42 - 0
components/FONT_LIB/include/FONT_LIB.h

@@ -0,0 +1,42 @@
+#ifndef _FONT_LIB_H_
+#define _FONT_LIB_H_
+
+#include <inttypes.h>
+#include "driver/gpio.h"
+
+#define u32 unsigned long
+
+#define   FONT_CS_PIN           10
+#define   FONT_MISO_PIN         9
+#define   FONT_MOSI_PIN         11
+#define   FONT_CLK_PIN          12
+
+#define LOW_LEVEL               0
+#define HIGH_LEVEL              1
+
+
+#define FONT_CS_LOW         gpio_set_level(FONT_CS_PIN,LOW_LEVEL)
+#define FONT_CS_HIGHT       gpio_set_level(FONT_CS_PIN,HIGH_LEVEL)
+
+
+#define FONT_CLK_0          gpio_set_level(FONT_CLK_PIN,LOW_LEVEL)
+#define FONT_CLK_1          gpio_set_level(FONT_CLK_PIN,HIGH_LEVEL)
+
+#define FONT_SDA_0          gpio_set_level(FONT_MOSI_PIN,LOW_LEVEL)
+#define FONT_SDA_1          gpio_set_level(FONT_MOSI_PIN,HIGH_LEVEL)
+
+#define FONT_RD_SDA         gpio_get_level(FONT_MISO_PIN)
+
+#define FONT_OUTPUT_PIN_SEL         ((1ULL<<FONT_CS_PIN)|(1ULL<<FONT_MOSI_PIN)|(1ULL<<FONT_CLK_PIN))
+#define FONT_INPUT_PIN_SEL          (1ULL<<FONT_MISO_PIN) 
+
+void font_init();
+
+void font_into_sleep();
+
+void font_exit_sleep();
+
+void font_soft_rst();
+
+//unsigned char test_ASCII_GetData(unsigned char asc,unsigned long ascii_kind,unsigned char *DZ_Data);
+#endif/*_FONT_LIB_H_*/

+ 118 - 0
components/FONT_LIB/include/GT5DL32A3W.h

@@ -0,0 +1,118 @@
+#ifndef _GT5DL32A3W_H_
+#define _GT5DL32A3W_H_
+
+#include "QRCode.h"
+
+extern unsigned char r_dat_bat(unsigned long address,unsigned long DataLen,unsigned char *pBuff);
+extern unsigned char r_dat(unsigned long address);
+extern unsigned char gt_read_data(unsigned char* sendbuf , unsigned char sendlen , unsigned char* receivebuf, unsigned int receivelen);
+//字库初始化
+int GT_Font_Init(void);
+
+#define ASCII_5X7              1
+#define ASCII_7X8              2
+#define ASCII_6X12             3
+#define ASCII_8X16						 4	//LATIN
+#define ASCII_12X24						 5
+#define ASCII_12X24_B					 6
+#define ASCII_16X32            7
+#define ASCII_12_N						 8	//Y
+#define ASCII_16_N             9
+#define ASCII_24_N						 10
+#define ASCII_32_N						 11
+
+
+//下列宏定义用于zz_zf函数
+//方体, 立体, 斜体共17个符号, 调用序号0~16
+#define F_FONT_24				20	//方体
+#define F_FONT_32				21
+#define F_FONT_48				22
+
+#define S_FONT_24				23	//立体
+#define S_FONT_32				24
+#define S_FONT_48				25
+
+#define I_FONT_24				26	//斜体
+#define I_FONT_32				27
+#define I_FONT_48				28
+
+//圆角	不等宽		共14个字符, 调用序号0~13
+#define A_16X16_T				30
+#define A_24X24_T				31
+#define A_24X32_T				32
+#define A_40X48_T				33
+#define A_40X64_T				34
+
+//线形	不等宽
+#define B_16X16_T				35
+#define B_24X24_T				36
+#define B_24X32_T				37
+#define B_40X48_T				38
+#define B_40X64_T				39
+
+//共14个字符
+#define T_FONT_24X24		40	//时钟体, 不等宽
+#define T_FONT_24X32		41
+#define T_FONT_40X48		42
+#define T_FONT_48X64		43
+
+//共14个字符, 对应调用序列 0~13
+#define F_8X16_T				44	//方块体, 不等宽
+#define F_16X24_T				45
+#define F_16X32_T				46
+#define F_24X48_T				47
+#define F_32X64_T				48
+
+
+unsigned char ASCII_GetData(unsigned char asc,unsigned long ascii_kind,unsigned char *DZ_Data);
+unsigned long U2G(unsigned int  unicode);
+unsigned long U2K(unsigned int Unicode);
+unsigned long U2J(unsigned int Unicode);
+unsigned long U2BIG5(unsigned int unicode);
+unsigned long BIG5_GBK( unsigned char h,unsigned char l);
+unsigned long Shift_JIS_TO_JIS0208(unsigned int Code16);
+
+unsigned long GB2312_16_GetData(unsigned char MSB, unsigned char LSB,unsigned char *DZ_Data);
+unsigned long BIG5_16_GetData(unsigned char MSB, unsigned char LSB,unsigned char *DZ_Data);
+unsigned long GBK_24_GetData (unsigned char MSB, unsigned char LSB,unsigned char *DZ_Data);
+unsigned long GBK_32_GetData (unsigned char MSB, unsigned char LSB,unsigned char *DZ_Data);
+unsigned long KSC5601_F_16_GetData(unsigned char MSB,unsigned char LSB,unsigned char *DZ_Data);
+unsigned long KSC5601_F_24_GetData(unsigned char MSB,unsigned char LSB,unsigned char *DZ_Data);
+unsigned long KSC5601_F_32_GetData(unsigned char MSB,unsigned char LSB,unsigned char *DZ_Data);
+unsigned long JIS0208_16X16_GetData(unsigned char MSB,unsigned char LSB,unsigned char *DZ_Data);
+unsigned long JIS0208_24X24_GetData(unsigned char MSB,unsigned char LSB,unsigned char *DZ_Data);
+unsigned long JIS0208_32X32_GetData(unsigned char MSB,unsigned char LSB,unsigned char *DZ_Data);
+unsigned long LATIN_8X16_GetData(unsigned int FontCode,unsigned char *DZ_Data);
+unsigned long LATIN_16_GetData(unsigned int FontCode,unsigned char *DZ_Data);
+unsigned long LATIN_12X24_GetData(unsigned int FontCode,unsigned char *DZ_Data);
+unsigned long LATIN_16X32_GetData(unsigned int FontCode,unsigned char *DZ_Data);
+unsigned long  CYRILLIC_8X16_GetData(unsigned int FontCode,unsigned char *DZ_Data);
+unsigned long CYRILLIC_16_GetData(unsigned int FontCode,unsigned char *DZ_Data);
+unsigned long  CYRILLIC_12X24_GetData(unsigned int FontCode,unsigned char *DZ_Data);
+unsigned long  CYRILLIC_16X32_GetData(unsigned int FontCode,unsigned char *DZ_Data);
+unsigned long GREECE_8X16_GetData(unsigned int FontCode,unsigned char *DZ_Data);
+unsigned long GREECE_16_GetData(unsigned int FontCode,unsigned char *DZ_Data);
+unsigned long GREECE_12X24_GetData(unsigned int FontCode,unsigned char *DZ_Data);
+unsigned long GREECE_16X32_GetData(unsigned int FontCode,unsigned char *DZ_Data);
+unsigned long HEBREW_8X16_GetData(unsigned int FontCode,unsigned char *DZ_Data);
+unsigned long HEBREW_12X24_GetData(unsigned int FontCode,unsigned char *DZ_Data);
+unsigned long HEBREW_16X32_GetData(unsigned int FontCode,unsigned char *DZ_Data);
+unsigned long ALB_16_GetData(unsigned int unicode_alb,unsigned char *DZ_Data);
+unsigned long ALB_24_GetData(unsigned int UNICODE_alb,unsigned char *DZ_Data);
+unsigned long ALB_32_GetData(unsigned int UNICODE_alb,unsigned char *DZ_Data);
+unsigned long THAILAND_16X24_GetData(unsigned int FontCode,unsigned char *DZ_Data);
+unsigned long THAILAND_48X48_GetData(unsigned int FontCode,unsigned char *DZ_Data);
+unsigned char zz_zf(unsigned char Sequence,unsigned char kind,unsigned char *DZ_Data);
+
+
+//一维码
+unsigned long *BAR_CODE_EAN13(unsigned char* BAR_NUM);
+unsigned long BAR_CODE39(unsigned char ASCIICODE,unsigned char *DZ_Data);
+unsigned long * BAR_CODE128(unsigned char  *BAR_NUM,unsigned char BAR_NUM_LEN, unsigned char  flag);
+
+
+
+#endif
+/* _GT5DL24A3W_H_ */
+
+

+ 65 - 0
components/FONT_LIB/include/QRCode.h

@@ -0,0 +1,65 @@
+#ifndef __QR_CODE_H
+#define __QR_CODE_H
+
+#include<string.h>
+#include<stdio.h>
+
+//ECLevl�ĺ궨��:
+#define 	ECLevel_L		0
+#define 	ECLevel_M		1
+#define 	ECLevel_Q		2
+#define 	ECLevel_H		3
+
+//MaskNub�ĺ궨��:
+#define   MaskPattern0  0
+#define   MaskPattern1  1
+#define   MaskPattern2  2
+#define   MaskPattern3  3
+#define   MaskPattern4  4
+#define   MaskPattern5  5
+#define   MaskPattern6  6
+#define   MaskPattern7  7
+//CodeMode�ĺ궨��:
+#define 	Mode_Unicode	10
+#define 	Mode_GBK 		11
+#define 	Mode_KSC5601	12
+#define 	Mode_SHIFT_JIS	13
+ //�ú����ο� spi�ο��ļ�
+extern unsigned char r_dat_bat(unsigned long address,unsigned long DataLen,unsigned char *pBuff);
+extern unsigned char versionN;			//�ڲ�����, QR�汾��
+extern unsigned char QRDataBuf[4072]; 		//���ж���, ��ά��ͼ������
+extern unsigned char QRCodeDataBuf[3706];	//�����
+
+
+unsigned char qrcode_get(unsigned char CodeMode,unsigned char ECLevel,unsigned char MaskNub, unsigned char *str,long unsigned int length);
+
+#if 0
+//��������
+unsigned char QRCodeUnicode[]=
+{
+	/*��ת��baiduҳ��   https://www.baidu.com/ */
+	0x00,0x68,0x00,0x74,0x00,0x74,0x00,0x70,0x00,0x73,0x00,0x3A,0x00,0x2f,
+	0x00,0x2f,0x00,0x77,0x00,0x77,0x00,0x77,0x00,0x2e,0x00,0x62,0x00,0x61,
+	0x00,0x69,0x00,0x64,0x00,0x75,0x00,0x2E,0x00,0x63,0x00,0x6f,0x00,0x6d,
+	0x00,0x2f,
+
+	/* ����Unicode���ֲ��� */
+//	0x4e,0x00,0x4e,0x01,0x4e,0x02,0x4e,0x03,0x4e,0x04,0x4e,0x05,0x4e,0x06,0x4e,0x07,0x4e,0x08,0x4e,0x09,
+//	0x4e,0x0a,0x4e,0x0b,0x4e,0x0c,0x4e,0x0d,0x4e,0x0e,0x4e,0x0f,0x4e,0x10,0x4e,0x11,0x4e,0x12,0x4e,0x13,
+//	0x4e,0x14,0x4e,0x15,0x4e,0x16,0x4e,0x17,0x4e,0x18,0x4e,0x19,0x4e,0x1a,0x4e,0x1b,0x4e,0x1c,0x4e,0x1d,
+//	0x4e,0x1e,0x4e,0x1f,0x4e,0x20,0x4e,0x21,0x4e,0x22,0x4e,0x23,0x4e,0x24,0x4e,0x25,0x4e,0x26,0x4e,0x27,
+// 	0x4e,0x28,0x4e,0x29,0x4e,0x2a,0x4e,0x2b,0x4e,0x2c,0x4e,0x2d,0x4e,0x2e,0x4e,0x2f,0x4e,0x30,0x4e,0x31,
+// 	0x4e,0x32,0x4e,0x33,0x4e,0x34,0x4e,0x35,0x4e,0x36,0x4e,0x37,0x4e,0x38,0x4e,0x39,0x4e,0x3a,0x4e,0x3b,
+// 	0x4e,0x3c,0x4e,0x3d,0x4e,0x3e,0x4e,0x3f,0x4e,0x40,0x4e,0x41,0x4e,0x42,0x4e,0x43,0x4e,0x44,0x4e,0x45,
+
+};
+
+void main() {
+	qrcode_get(Mode_Unicode,ECLevel_H,MaskPattern4,QRCodeUnicode,sizeof(QRCodeUnicode));
+	w=4*versionN+17; h_byte = (w+7)/8;
+	//���ú��� ת ���ú��� ��ʾ
+	DisZK_DZ_Y(50,50,w,h_byte*8,BLACK,WHITE,QRDataBuf,1);	//�ο�spi�����ļ�
+}
+#endif
+
+#endif

+ 3 - 0
components/LED/CMakeLists.txt

@@ -0,0 +1,3 @@
+idf_component_register(SRCS "LED.c"
+                    INCLUDE_DIRS "include"
+                    REQUIRES    "driver")

+ 124 - 0
components/LED/LED.c

@@ -0,0 +1,124 @@
+#include <stdio.h>
+#include "LED.h"
+#include "esp_log.h"
+
+
+static const char *LOG_TAG = "LED";
+
+//uint8_t led[6]={0x04,0x20,0x08,0x10,0x02,0x40 };
+
+//uint8_t led[6]={0x20,0x02,0x04,0x08,0x10,0x40 };
+//dailiao baoyang tunxing fengcun  guzhang tingji 
+uint8_t led[6]={0x04,0x40,0x20,0x10,0x08,0x02 };
+
+
+
+#include <stdio.h>
+
+#include "esp_err.h"
+
+static void example_ledc_init(void)
+{
+    // Prepare and then apply the LEDC PWM timer configuration
+    ledc_timer_config_t ledc_timer = {
+        .speed_mode       = LEDC_MODE,
+        .timer_num        = LEDC_TIMER,
+        .duty_resolution  = LEDC_DUTY_RES,
+        .freq_hz          = LEDC_FREQUENCY,  // Set output frequency at 5 kHz
+        .clk_cfg          = LEDC_AUTO_CLK
+    };
+    ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer));
+
+    // Prepare and then apply the LEDC PWM channel configuration
+    ledc_channel_config_t ledc_channel = {
+        .speed_mode     = LEDC_MODE,
+        .channel        = LEDC_CHANNEL,
+        .timer_sel      = LEDC_TIMER,
+        .intr_type      = LEDC_INTR_DISABLE,
+        .gpio_num       = LEDC_OUTPUT_IO,
+        .duty           = 0, // Set duty to 0%
+        .hpoint         = 0
+    };
+    ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel));
+}
+
+
+
+
+
+
+void led_init(void)
+{
+
+    #if 0
+    gpio_config_t led_pin_cfg = {};
+    led_pin_cfg.intr_type = GPIO_INTR_DISABLE;
+    led_pin_cfg.mode = GPIO_MODE_OUTPUT;
+    led_pin_cfg.pin_bit_mask =LED_OUTPUT_PIN_SEL;
+    led_pin_cfg.pull_down_en = 0;
+    led_pin_cfg.pull_up_en = 1;
+    gpio_config(&led_pin_cfg);
+    LED_SCLK_0;
+    LED_DATA_0;
+    LED_LCLK_0;
+    LED_RST_0;
+    LED_RST_1;
+    for(int i=0;i<6;i++)
+    led_set(i,0);
+    #else
+
+    //void app_main(void)
+{
+    // Set the LEDC peripheral configuration
+    example_ledc_init();
+    // Set duty to 50%
+    ESP_ERROR_CHECK(ledc_set_duty(LEDC_MODE, LEDC_CHANNEL, 0));
+    // Update duty to apply the new value
+    ESP_ERROR_CHECK(ledc_update_duty(LEDC_MODE, LEDC_CHANNEL));
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+    #endif
+
+}
+
+static void  _74HC595_WriteByte(unsigned char Byte)
+{
+
+    unsigned char i;
+    for(i=0;i<8;i++)
+    {   
+        uint8_t bit = (Byte >> (i)) & 0x01;
+        gpio_set_level(LED_DATA_PIN,bit);
+        LED_LCLK_0 ;
+        LED_LCLK_1 ; 
+    }
+      LED_SCLK_0;  
+      LED_SCLK_1;    
+}
+
+
+void led_set(uint8_t led_index,uint8_t led_status)
+{
+
+    ESP_LOGI(LOG_TAG,"led_set  %s",led_status ? "open":"close");
+
+    if(led_status)
+    {
+        _74HC595_WriteByte(led[led_index]);
+    }else
+    {
+        _74HC595_WriteByte(0x00);
+    }
+}

+ 56 - 0
components/LED/include/LED.h

@@ -0,0 +1,56 @@
+#ifndef _LED_H_
+#define _LED_H_
+
+#include "driver/gpio.h"
+#include "driver/ledc.h"
+
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+
+#if 1
+
+
+
+#define LEDC_TIMER              LEDC_TIMER_0
+#define LEDC_MODE               LEDC_LOW_SPEED_MODE
+#define LEDC_OUTPUT_IO          (46) // Define the output GPIO
+#define LEDC_CHANNEL            LEDC_CHANNEL_0
+#define LEDC_DUTY_RES           LEDC_TIMER_13_BIT // Set duty resolution to 13 bits
+#define LEDC_DUTY               (4095) // Set duty to 50%. ((2 ** 13) - 1) * 50% = 4095
+#define LEDC_FREQUENCY          (2700) // Frequency in Hertz. Set frequency at 5 kHz
+
+
+// void beep_blink(uint16_t ms,uint16_t count);
+
+
+#endif
+
+#define LED_DATA_PIN  15    //DS    //serial data input
+#define LED_SCLK_PIN  16    //STCP  //storage register clock input
+#define LED_LCLK_PIN  19    //SHCP  //shift register clock input
+
+#define LED_RST_PIN   46    //SHCP  //shift register clock input
+
+#define LED_OUTPUT_PIN_SEL  ((1ULL<<LED_DATA_PIN)|(1ULL<<LED_SCLK_PIN)|(1ULL<<LED_LCLK_PIN)|(1ULL<<LED_RST_PIN))
+
+
+#define LED_SCLK_0              gpio_set_level(LED_SCLK_PIN, 0)
+#define LED_SCLK_1              gpio_set_level(LED_SCLK_PIN, 1)
+
+
+#define LED_DATA_0              gpio_set_level(LED_DATA_PIN, 0)
+#define LED_DATA_1              gpio_set_level(LED_DATA_PIN, 1)
+
+
+#define LED_LCLK_0              gpio_set_level(LED_LCLK_PIN, 0)
+#define LED_LCLK_1              gpio_set_level(LED_LCLK_PIN, 1)
+
+
+#define LED_RST_0               gpio_set_level(LED_RST_PIN, 0)
+#define LED_RST_1               gpio_set_level(LED_RST_PIN, 1)
+
+void led_init(void);
+void led_set(uint8_t led_index,uint8_t led_status);
+
+
+#endif/*_LED_H_*/

+ 4 - 0
components/LORA/CMakeLists.txt

@@ -0,0 +1,4 @@
+idf_component_register(SRCS "LORA.c" "y_ringbuf.c"
+                    INCLUDE_DIRS "include"    "../../main/"
+                    REQUIRES    "driver"
+                                "USER_SPIFFS")

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 3184 - 0
components/LORA/LORA.c


+ 318 - 0
components/LORA/include/LORA.h

@@ -0,0 +1,318 @@
+#ifndef _LORA_H_
+#define _LORA_H_
+
+
+//#define lora_channel   0x02  //信道
+
+#define ringbuffer_size     1024*60
+
+#define LORA_POWER_PIN          (20)
+#define LORA_TXD_PIN            (17)
+#define LORA_RXD_PIN            (18)
+
+#define LORA_BUSY_PIN            (5)
+/*
+
+BUSY_STA,忙状态指示。高电平表示设备当前工作状态忙,不可进行串口操
+作,低电平表示外设可以进行串口操作
+
+*/
+
+#define LORA_DATA_MAX_LEN           1024
+
+
+#define uart_fifo_size  120
+#define buffer_size     1024*20
+#define uart_timerout_times 30 //ms
+
+
+typedef struct 
+{
+    char version;
+    char wireless_ch;
+    char wireless_freq_range;
+    char wireless_tx_power;
+    char wireless_bps;
+    char serial_bps;
+    char packet_fmt;
+    char auto_ack;
+    char outdir;
+    char wireless_ch_interval_bandwidth;
+}LORA_CFG_T;
+
+
+typedef enum
+{
+
+    uart_idle = 0,
+    uart_receving = 1,
+    uart_receive_complete = 2,
+
+}uart_status_t;
+
+
+
+typedef struct {
+    uint8_t data_len;
+    uint8_t data_buf[LORA_DATA_MAX_LEN];
+}LORA_DATA_T;
+
+typedef LORA_DATA_T USE_DATA_T;
+
+extern LORA_CFG_T lora_cfg_data;
+extern uint8_t lora_rssi_data;
+extern uint8_t lora_software_version;
+
+
+void lora_send_queue_callback(uint8_t* data_buf,int len);
+
+void lora_init(void);
+
+void lora_uart_pin_init(void);
+void lora_event_task(void *pvParameters);
+void lora_set_power_level(int level);
+
+int lora_set_by_serial(uint8_t cmd,uint8_t value);
+int lora_reset_software(void);
+int lora_set_factory(void);
+int lora_set_wireless_channel_interval_bandwidth(uint8_t bandwidth);
+int lora_set_data_outdir(uint8_t dir);
+int lora_set_auto_ack(uint8_t ack);
+int lora_set_packet_fmt(uint8_t fmt);
+int lora_set_Wireless_Channel (uint8_t Channel);
+int lora_set_tx_power(uint8_t power);
+int lora_set_Wireless_bps(uint8_t bps);
+int lora_set_serial_bps(uint8_t bps);
+int lora_get_rssi(uint32_t rssi);
+int lora_get_version(void);
+int lora_get_cfg(void);
+int lora_send_data(const char* lora_data,int lora_data_len);
+int lora_set_cfg_mode(uint8_t mode);
+
+
+
+int get_lora_busy_pin();
+//int lora_analytic_data(const uint8_t* data_buf,int len);
+
+
+#if 1
+
+
+
+#define USER_QIXIN  1   //使用祁鑫的初始化
+
+
+
+#define RX_MAX_LENGTH    1024
+
+#include<stdbool.h>
+
+#define LORA_CHANENL    0x00
+
+#define HPD_LORA_ENABLE 0
+#define VJ_LORA_ENABLE  1
+
+#define LORA_UART  UART_NUM_1
+
+
+// IO5	I		LORA_BUSY	LORA总线忙输入,参考LORA模块PDF文档	仅合普顿模块有效
+// IO6	I		LORA_SET	LORA设置参数IO,参考LORA模块PDF文档	
+// IO7	O		LORA_RST	LORA复位输出IO,参考LORA模块PDF文档	
+// IO17	O		LORA_RXD	LORA串口数据RXD	
+// IO18	I		LORA_TXD	LORA串口数据TXD	
+// IO20	O		LORA_POWER_SD	LORA模块电源控制,默认高,低电平LORA模块上电。	
+
+
+#define LORA_BUSY_PIN   5
+#define LORA_SET_PIN    6
+#define LORA_RST_PIN    7
+
+#define LORA_RXD_PIN    18
+#define LORA_TXD_PIN    17
+#define LORA_POWER_PIN  20
+
+
+
+
+#define LORA_CMD_MAX_LENGTH   256
+#define LORA_CMD_NUM          16
+#define MAX_RETRY_NUM         5
+
+
+
+#define LORA_CMD_AH_CMD          "AH"
+#define LORA_CMD_AH_CMD_RSP      "OK"
+
+
+//设置波特率
+#define LORA_CMD_AH_BAUDRATE         "AH+B"
+#define LORA_CMD_AH_BAUDRATE_RSP     "OK+B"
+
+
+#define AT_MAX_RETRY_TIMES   10
+
+
+#if VJ_LORA_ENABLE   
+
+#define LORA_SUCCESS   0x55  //沃进成功返回命令码
+#define LORA_FAIL	   0xEE  //沃进失败返回命令码
+
+#endif
+
+typedef enum
+{
+	Lora_Cmd_Mode  = 0,  //AT命令模式
+	Lora_UART_Mode = 1,  //串口透传模式
+    // Lora_read_data = 2,
+}Lora_Mode_t;
+
+
+typedef enum
+{
+	lora_cmd_system_type  = 0,  //系统上电配置的AT初始化命令模式
+	lora_cmd_user_type    = 1,  //用户自定义可以随时的发送命令
+
+}Lora_cmd_type_t;
+
+
+
+//命令解析函数
+typedef struct 
+{
+   uint8_t cmd[LORA_CMD_MAX_LENGTH]; //命令字符串
+   uint8_t cmd_len;    //命令长度
+   uint32_t cmd_mode;   //当前命令
+   void (*lora_send_cmd_handler)(uint32_t cmd_mode);
+
+   uint8_t cmd_rsp[LORA_CMD_MAX_LENGTH]; //命令回复字符串
+   uint8_t cmd_rsp_len;    //回复命令长度
+   void(*success_hanlder)(unsigned char *cmd,unsigned char *cmd_rsp,int len);       //回调函数
+   void(*timeout_hanlder)(unsigned char *cmd,unsigned char *cmd_rsp,int len);       //超时未响应回调函数
+}lora_cmd_t;
+
+
+typedef struct
+{
+	uint8_t   lora_mode;  	 //Lora_Mode_t
+	lora_cmd_t  *lora_cmd;
+	uint8_t cmd_index;      //当前执行命令索引值
+	bool    timerout;
+	uint8_t retry_times;    //最大重新传输次数
+	uint8_t cmd_type;
+}Lora_t;
+
+extern uint8_t rssi;
+
+void lora_init();
+
+int lora_sendData(unsigned char* data,int len);
+
+void lora_cmd_success_Hander(unsigned char *cmd,unsigned char *cmd_rsp,int len);
+
+void lora_cmd_timerout_Hander(unsigned char *cmd,unsigned char *cmd_rsp,int len);
+
+
+void VJ_Lora_set_cfg_mode(uint8_t mode);
+
+
+
+void lora_set_config_mode(uint8_t mode);
+
+void lora_set_channel(uint8_t channel);
+
+void lora_set_bps(uint8_t bps);
+
+void lora_set_mode(uint8_t mode);
+
+void lora_send_ack(uint8_t cmd,uint8_t *mac,int id_num,uint8_t *cmd_index,uint8_t status);
+
+
+void dymatic_change_chanel(uint8_t chanel);
+
+void lora_set_device_id(uint32_t id);
+
+void lora_set_dst_device_id(uint32_t id);
+
+
+
+
+void dymatic_change_device_id(uint32_t id);
+void dymatic_change_dst_device_id(uint32_t id);
+
+
+void uart_sleep_in_config();
+void uart_sleep_out_config();
+void timer_wake_uart_sleep_out_config();
+
+
+void lora_factory_config();
+void reset_lora(void);
+uint8_t set_lora(uint8_t new_channel,uint8_t eflag);
+uint8_t crc8( uint8_t *pData, 
+                    uint16_t dataLen,
+                    uint8_t initialValue, 
+                    uint8_t polynomial );
+
+#if 0
+bool lora_uart_is_normal(int Baud);
+
+bool lora_send_cmd(String cmd,String cmd_rsp);
+
+bool lora_set_Baud(int baud);
+
+bool lora_set_default();
+
+
+bool lora_set_work_freq(String freq);
+
+bool lora_set_send_power(int baud);
+
+
+bool lora_set_bw(int baud);
+
+bool lora_set_sf(int baud);
+
+bool lora_set_net_id(String id);
+
+bool lora_set_rssi(int rssi);
+#endif
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+#endif
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+#endif/*_LORA_H_*/

+ 101 - 0
components/LORA/include/y_ringbuf.h

@@ -0,0 +1,101 @@
+/// ------------------------------------------------------------------------------------------------------------------------------------
+///
+/// MIT License
+///
+/// Permission is hereby granted, free of charge, to any person obtaining a copy
+/// of this software and associated documentation files (the "Software"), to deal
+/// in the Software without restriction, including without limitation the rights
+/// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+/// copies of the Software, and to permit persons to whom the Software is
+/// furnished to do so, subject to the following conditions:
+///
+/// The above copyright notice and this permission notice shall be included in all
+/// copies or substantial portions of the Software.
+///
+/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+/// SOFTWARE.
+///
+/// Copyright (c) 2022 ycz. All rights reserved.
+///
+/// Created by ycz on 2022/1/12.
+///
+/// @brief
+///     y_ringbuf 是一种用于嵌入式设备的 ringbuf,可以方便用于数据缓存与传递。
+///
+/// ------------------------------------------------------------------------------------------------------------------------------------
+
+/// ------------------------------------------------------------------------------------------------------------------------------------
+/// 防止当前头文件被重复引用
+/// ------------------------------------------------------------------------------------------------------------------------------------
+
+#ifndef _Y_RINGBUF_H__
+#define _Y_RINGBUF_H__
+
+
+
+/// ------------------------------------------------------------------------------------------------------------------------------------
+/// 头文件
+/// ------------------------------------------------------------------------------------------------------------------------------------
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+/// -----------------------------------------------------------------------------------------------------------------------------------
+/// 兼容 c++
+/// ------------------------------------------------------------------------------------------------------------------------------------
+
+
+/// ------------------------------------------------------------------------------------------------------------------------------------
+/// 宏定义
+/// ------------------------------------------------------------------------------------------------------------------------------------
+
+#define Y_RINGBUF_VERSION_MAJOR  0  ///< 版本信息 主版本   ( 主架构变化 )
+#define Y_RINGBUF_VERSION_MINOR  1  ///< 版本信息 次版本   ( 单个功能增加或修改 )
+#define Y_RINGBUF_VERSION_PATCH  2  ///< 版本信息 补丁版本  ( bug修复 )
+
+#define Y_RINGBUF_USE_MUTEX_LOCK 1  ///< 是否使用互斥锁  在使用单线程的情况下可以关闭以降低开销 \n 0 : 不使用  1 : 使用
+
+
+
+/// ------------------------------------------------------------------------------------------------------------------------------------
+/// 结构体
+/// ------------------------------------------------------------------------------------------------------------------------------------
+
+typedef struct ringbuf RINGBUF_st;  ///< ring_buf 实例结构体 对外隐藏具体实现 当不透明指针使用
+
+
+
+/// ------------------------------------------------------------------------------------------------------------------------------------
+/// 函数 API
+/// ------------------------------------------------------------------------------------------------------------------------------------
+
+void        y_ringbuf_print_version();                                                ///< 打印 y_ringbuf 版本号
+uint16_t    y_ringbuf_get_used_size(RINGBUF_st *ringbuf);                             ///< 获取 ringbuf 已使用字节数
+uint16_t    y_ringbuf_get_free_size(RINGBUF_st *ringbuf);                             ///< 获取 ringbuf 可使用字节数
+uint16_t    y_ringbuf_read_clear(RINGBUF_st *ringbuf, uint8_t *data, uint16_t size);  ///< 从 ringbuf 中读取数据并删除
+uint16_t    y_ringbuf_read_only(RINGBUF_st *ringbuf, uint8_t *data, uint16_t size);   ///< 从 ringbuf 中读取数据但不删除
+bool        y_ringbuf_write(RINGBUF_st *ringbuf, uint8_t *data, uint16_t size);       ///< 向 ringbuf 中写入数据
+bool        y_ringbuf_delete_data(RINGBUF_st *ringbuf, uint16_t size);                ///< 删除 ringbuf 指定长度数据
+bool        y_ringbuf_is_full(RINGBUF_st *ringbuf);                                   ///< 判断 ringbuf 是否为满
+bool        y_ringbuf_is_empty(RINGBUF_st *ringbuf);                                  ///< 判断 ringbuf 是否为空
+bool        y_ringbuf_reset(RINGBUF_st *ringbuf);                                     ///< 重置 ringbuf
+bool        y_ringbuf_destroy(RINGBUF_st *ringbuf);                                   ///< 销毁 ringbuf
+RINGBUF_st *y_ringbuf_create(uint16_t length);                                        ///< 创建 ringbuf 长度 (max 65535)
+
+bool y_ringbuf_write_remalloc_memory(RINGBUF_st *ringbuf, uint8_t *data, uint16_t size);
+
+
+/// ------------------------------------------------------------------------------------------------------------------------------------
+/// 条件编译结尾
+/// ------------------------------------------------------------------------------------------------------------------------------------
+
+
+#endif  // _Y_RINGBUF_H 防止当前头文件被重复引用

+ 656 - 0
components/LORA/y_ringbuf.c

@@ -0,0 +1,656 @@
+/// ------------------------------------------------------------------------------------------------------------------------------------
+///
+/// MIT License
+///
+/// Permission is hereby granted, free of charge, to any person obtaining a copy
+/// of this software and associated documentation files (the "Software"), to deal
+/// in the Software without restriction, including without limitation the rights
+/// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+/// copies of the Software, and to permit persons to whom the Software is
+/// furnished to do so, subject to the following conditions:
+///
+/// The above copyright notice and this permission notice shall be included in all
+/// copies or substantial portions of the Software.
+///
+/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+/// SOFTWARE.
+///
+/// Copyright (c) 2022 ycz. All rights reserved.
+///
+/// Created by ycz on 2022/1/12.
+///
+/// @brief
+///     y_ringbuf 具体功能实现
+///
+/// ------------------------------------------------------------------------------------------------------------------------------------
+
+
+
+/// ------------------------------------------------------------------------------------------------------------------------------------
+/// 头文件
+/// ------------------------------------------------------------------------------------------------------------------------------------
+
+#include "y_ringbuf.h"
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "esp_system.h"
+#include "esp_log.h"
+#include "driver/uart.h"
+#include "string.h"
+#include "driver/gpio.h"
+#include "freertos/timers.h"
+
+extern SemaphoreHandle_t debug_Mutex;
+/// ------------------------------------------------------------------------------------------------------------------------------------
+/// 结构体
+/// ------------------------------------------------------------------------------------------------------------------------------------
+
+/// ringbuf 实例结构类型
+struct ringbuf {
+    uint8_t *buf;        ///< ringbuf 指针
+    uint16_t length;     ///< ringbuf 大小 最大64k
+    uint16_t head;       ///< ringbuf 头所在位置
+    uint16_t tail;       ///< ringbuf 尾所在位置
+    uint16_t used_size;  ///< ringbuf 已使用大小
+    uint16_t free_size;  ///< ringbuf 空闲大小
+#if Y_RINGBUF_USE_MUTEX_LOCK == 1
+    int32_t mutex_lock;  ///< 互斥锁句柄
+#endif
+};
+
+
+
+/// ------------------------------------------------------------------------------------------------------------------------------------
+/// 私有函数
+/// ------------------------------------------------------------------------------------------------------------------------------------
+
+#if Y_RINGBUF_USE_MUTEX_LOCK == 1
+/// @brief ringbuf 上锁
+/// @param [in]  ringbuf    ringbuf 句柄指针
+/// @retval true  成功
+/// @retval false 失败
+static bool _y_ringbuf_lock(RINGBUF_st *ringbuf) {
+    // todo : 添加代码
+    xSemaphoreTake( debug_Mutex, portMAX_DELAY );
+    return true;
+}
+
+/// @brief ringbuf 解锁
+/// @param [in]  ringbuf    ringbuf 句柄指针
+/// @retval true  成功
+/// @retval false 失败
+static bool _y_ringbuf_unlock(RINGBUF_st *ringbuf) {
+    // todo : 添加代码
+    xSemaphoreGive( debug_Mutex );
+    return true;
+}
+
+/// @brief 销毁互斥锁
+/// @param [in]  ringbuf    ringbuf 句柄指针
+/// @retval true  成功
+/// @retval false 失败
+static bool _y_ringbuf_destroy_mutex_lock(RINGBUF_st *ringbuf) {
+    // todo : 添加代码
+
+    vSemaphoreDelete(debug_Mutex);
+    return true;
+}
+
+/// @brief 创建互斥锁
+/// @param [in]  ringbuf    ringbuf 句柄指针
+/// @retval true  成功
+/// @retval false 失败
+static bool _y_ringbuf_create_mutex_lock(RINGBUF_st *ringbuf) {
+    // todo : 添加代码
+    debug_Mutex = xSemaphoreCreateMutex();
+    if(debug_Mutex == NULL)
+    {
+        return false;
+    }
+    return true;
+}
+#endif  // Y_RINGBUF_USE_MUTEX_LOCK
+
+/// @brief ringbuf read 子函数
+/// @param [in]  ringbuf    ringbuf 句柄指针
+/// @param [out] data       读到数据存放的地址
+/// @param [in]  size       想要读取的字节数
+/// @param [in]  clear_data 读取后是否清除 buf 中的数据 true:清除  false:不清除
+/// @return  实际读到的字节数(\<=size)
+static uint16_t _y_ringbuf_read_sub(RINGBUF_st *ringbuf, uint8_t *data, uint16_t size, bool clear_data) {
+
+    // 断言
+    if (ringbuf == NULL) {
+        printf("ringbuf is NULL");
+        return 0;
+    }
+    if (data == NULL && size != 0) {
+        printf("data pointer is NULL ,but size != 0 is %d", size);
+        return 0;
+    }
+
+#if Y_RINGBUF_USE_MUTEX_LOCK == 1
+    // 上锁
+    _y_ringbuf_lock(ringbuf);
+#endif
+
+    uint16_t read_size = 0;
+
+    // 判断是否有数据可读
+    if (ringbuf->used_size != 0 && size != 0) {
+
+        // 判断需要可读的大小
+        if (ringbuf->used_size >= size) {
+            read_size = size;
+        } else {
+            read_size = ringbuf->used_size;
+        }
+
+        // 判断头尾位置 确定读取方式
+        if (ringbuf->tail <= ringbuf->head) {
+
+            // 尾在头前面 或 在同一位置(全满)
+            uint16_t end_size = ringbuf->length - ringbuf->head;  // 尾部可读的空间
+            if (end_size > read_size) {
+                // 尾部数据足够读 可以顺序读取
+                memcpy(data, ringbuf->buf + ringbuf->head, read_size);
+                if (clear_data == true) {
+                    ringbuf->head += read_size;
+                }
+            } else if (end_size == read_size) {
+                // 尾部数据刚好够读 可以顺序读取 但头位置移到 buf 开头
+                memcpy(data, ringbuf->buf + ringbuf->head, read_size);
+                if (clear_data == true) {
+                    ringbuf->head = 0;
+                }
+            } else {
+                // 尾部数据不够读 需要分段读取
+                memcpy(data, ringbuf->buf + ringbuf->head, end_size);
+                memcpy(data + end_size, ringbuf->buf, read_size - end_size);
+                if (clear_data == true) {
+                    ringbuf->head = read_size - end_size;
+                }
+            }
+
+        } else {
+
+            // 尾在头后面
+            memcpy(data, ringbuf->buf + ringbuf->head, read_size);
+            if (clear_data == true) {
+                ringbuf->head += read_size;
+            }
+        }
+    }
+
+    // 更新句柄相关状态
+    if (clear_data == true) {
+        ringbuf->used_size -= read_size;
+        ringbuf->free_size += read_size;
+    }
+
+#if Y_RINGBUF_USE_MUTEX_LOCK == 1
+    // 解锁
+    _y_ringbuf_unlock(ringbuf);
+#endif
+
+    //printf("ringbuf read %d byte succeed", read_size);
+    return read_size;
+}
+
+
+
+/// ------------------------------------------------------------------------------------------------------------------------------------
+/// 公有函数
+/// ------------------------------------------------------------------------------------------------------------------------------------
+
+/// @brief 打印 y_ringbuf 版本号
+void y_ringbuf_print_version() {
+    printf("y_ringbuf version : V%d.%d.%d", Y_RINGBUF_VERSION_MAJOR, Y_RINGBUF_VERSION_MINOR, Y_RINGBUF_VERSION_PATCH);
+}
+
+/// @brief 获取 ringbuf 中已使用的字节数
+/// @param [in] ringbuf  ringbuf 句柄指针
+/// @return 已使用的字节数
+uint16_t y_ringbuf_get_used_size(RINGBUF_st *ringbuf) {
+
+    // 断言
+    if (ringbuf == NULL) {
+        printf("ringbuf is NULL");
+        return 0;
+    }
+
+    return ringbuf->used_size;
+}
+
+/// @brief 获取 ringbuf 中剩余可用的字节数
+/// @param [in] ringbuf  ringbuf 句柄指针
+/// @return 剩余可用的字节数
+uint16_t y_ringbuf_get_free_size(RINGBUF_st *ringbuf) {
+
+    // 断言
+    if (ringbuf == NULL) {
+    	printf("ringbuf is NULL");
+        return 0;
+    }
+
+    return ringbuf->free_size;
+}
+
+/// @brief 从 ringbuf 中读取数据后并清除
+/// @param [in]  ringbuf  ringbuf 句柄指针
+/// @param [out] data     读到数据存放的地址
+/// @param [in]  size     想要读取的字节数
+/// @return 实际读到的字节数(\<=size)
+uint16_t y_ringbuf_read_clear(RINGBUF_st *ringbuf, uint8_t *data, uint16_t size) {
+    return _y_ringbuf_read_sub(ringbuf, data, size, true);
+}
+
+/// @brief 从 ringbuf 中读取数据但不清除数据
+/// @param [in]  ringbuf  ringbuf 句柄指针
+/// @param [out] data     读到数据存放的地址
+/// @param [in]  size     想要读取的字节数
+/// @return 实际读到的字节数(\<=size)
+uint16_t y_ringbuf_read_only(RINGBUF_st *ringbuf, uint8_t *data, uint16_t size) {
+    return _y_ringbuf_read_sub(ringbuf, data, size, false);
+}
+
+
+
+/// @brief 写入数据到 ringbuf 长度大于空间时立即分配内存
+/// @param [in] ringbuf  ringbuf 句柄指针
+/// @param [in] data     待写入数据指针
+/// @param [in] size     待写入数据大小
+/// @retval true  成功
+/// @retval false 失败
+bool y_ringbuf_write_remalloc_memory(RINGBUF_st *ringbuf, uint8_t *data, uint16_t size) {
+
+    // 断言
+    if (ringbuf == NULL) {
+    	printf("ringbuf is NULL");
+        return false;
+    }
+    if (data == NULL && size != 0) {
+        printf("data pointer is NULL ,but size != 0 is %d", size);
+        return false;
+    }
+
+#if Y_RINGBUF_USE_MUTEX_LOCK == 1
+    // 上锁
+    _y_ringbuf_lock(ringbuf);
+#endif
+
+    // 判断空闲空间是否满足写入要求
+    if (ringbuf->free_size < size) {
+        printf("ringbuf free size %d ,but need write size %d", ringbuf->free_size, size);
+            //重新设置内存
+            //ringbuf->buf = (uint8_t *) heap_caps_malloc(length);  // 开辟 ringbuf 所需要的空间
+            ringbuf->length    += ringbuf->length ;
+            ringbuf->free_size += ringbuf->length;
+
+            ringbuf->buf = (RINGBUF_st *) heap_caps_realloc( ringbuf->buf, ringbuf->length,MALLOC_CAP_8BIT|MALLOC_CAP_SPIRAM);
+
+            if( ringbuf->buf!=NULL)
+            {
+                    printf("remalloc ringbuf ok  contiune write\r\n");
+            }else
+            {
+
+
+            #if Y_RINGBUF_USE_MUTEX_LOCK == 1
+            // 解锁
+             _y_ringbuf_unlock(ringbuf);
+            #endif
+                    printf("remalloc ringbuf fail  not contiune write\r\n");
+
+                    return false;
+            }
+            //ringbuf = (RINGBUF_st *) heap_caps_malloc(sizeof(RINGBUF_st));  // 开辟 结构体句柄 所需要的空间
+
+        
+           
+
+        //return false;
+    }
+
+    // 判断头尾位置 确定写入方式
+    if (ringbuf->tail < ringbuf->head) {
+
+        // 尾在头前面 可以直接顺序写入
+        memcpy(ringbuf->buf + ringbuf->tail, data, size);
+        ringbuf->tail += size;
+
+    } else {
+
+        // 尾在头后面 或 在同一位置(全空)
+        uint16_t end_size = ringbuf->length - ringbuf->tail;  // 尾部剩余可用的空间
+        if (end_size > size) {
+            // 尾部可用空间足够 可以直接顺序写入
+            memcpy(ringbuf->buf + ringbuf->tail, data, size);
+            ringbuf->tail += size;
+        } else if (end_size == size) {
+            // 尾部可用空间刚好够 可以直接顺序写入 但尾位置移到 buf 开头
+            memcpy(ringbuf->buf + ringbuf->tail, data, size);
+            ringbuf->tail = 0;
+        } else {
+            // 尾部可用空间不足 需要分段写入
+            memcpy(ringbuf->buf + ringbuf->tail, data, end_size);
+            memcpy(ringbuf->buf, data + end_size, size - end_size);
+            ringbuf->tail = size - end_size;
+        }
+    }
+
+    // 更新句柄相关状态
+    ringbuf->used_size += size;
+    ringbuf->free_size -= size;
+
+#if Y_RINGBUF_USE_MUTEX_LOCK == 1
+    // 解锁
+    _y_ringbuf_unlock(ringbuf);
+#endif
+
+    //printf("ringbuf write %d byte succeed", size);
+    return true;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/// @brief 写入数据到 ringbuf
+/// @param [in] ringbuf  ringbuf 句柄指针
+/// @param [in] data     待写入数据指针
+/// @param [in] size     待写入数据大小
+/// @retval true  成功
+/// @retval false 失败
+bool y_ringbuf_write(RINGBUF_st *ringbuf, uint8_t *data, uint16_t size) {
+
+    // 断言
+    if (ringbuf == NULL) {
+    	printf("ringbuf is NULL");
+        return false;
+    }
+    if (data == NULL && size != 0) {
+        printf("data pointer is NULL ,but size != 0 is %d", size);
+        return false;
+    }
+
+#if Y_RINGBUF_USE_MUTEX_LOCK == 1
+    // 上锁
+    _y_ringbuf_lock(ringbuf);
+#endif
+
+    // 判断空闲空间是否满足写入要求
+    if (ringbuf->free_size < size) {
+        printf("ringbuf free size %d ,but need write size %d", ringbuf->free_size, size);
+
+#if Y_RINGBUF_USE_MUTEX_LOCK == 1
+        // 解锁
+        _y_ringbuf_unlock(ringbuf);
+#endif
+
+        return false;
+    }
+
+    // 判断头尾位置 确定写入方式
+    if (ringbuf->tail < ringbuf->head) {
+
+        // 尾在头前面 可以直接顺序写入
+        memcpy(ringbuf->buf + ringbuf->tail, data, size);
+        ringbuf->tail += size;
+
+    } else {
+
+        // 尾在头后面 或 在同一位置(全空)
+        uint16_t end_size = ringbuf->length - ringbuf->tail;  // 尾部剩余可用的空间
+        if (end_size > size) {
+            // 尾部可用空间足够 可以直接顺序写入
+            memcpy(ringbuf->buf + ringbuf->tail, data, size);
+            ringbuf->tail += size;
+        } else if (end_size == size) {
+            // 尾部可用空间刚好够 可以直接顺序写入 但尾位置移到 buf 开头
+            memcpy(ringbuf->buf + ringbuf->tail, data, size);
+            ringbuf->tail = 0;
+        } else {
+            // 尾部可用空间不足 需要分段写入
+            memcpy(ringbuf->buf + ringbuf->tail, data, end_size);
+            memcpy(ringbuf->buf, data + end_size, size - end_size);
+            ringbuf->tail = size - end_size;
+        }
+    }
+
+    // 更新句柄相关状态
+    ringbuf->used_size += size;
+    ringbuf->free_size -= size;
+
+#if Y_RINGBUF_USE_MUTEX_LOCK == 1
+    // 解锁
+    _y_ringbuf_unlock(ringbuf);
+#endif
+
+    //printf("ringbuf write %d byte succeed", size);
+    return true;
+}
+
+/// @brief 删除 ringbuf 中指定长度的数据
+/// @param [in] ringbuf  ringbuf 句柄指针
+/// @param [in] size     要删除的数据长度
+/// @retval true  成功
+/// @retval false 失败
+bool y_ringbuf_delete_data(RINGBUF_st *ringbuf, uint16_t size) {
+
+    // 断言
+    if (ringbuf == NULL) {
+        printf("ringbuf is NULL");
+        return false;
+    }
+
+    // 判断是否有足够的数据可删
+    if (ringbuf->used_size < size) {
+        printf("not enough data, need delete %d data,but only used size %d", size, ringbuf->used_size);
+        return false;
+    }
+
+#if Y_RINGBUF_USE_MUTEX_LOCK == 1
+    // 上锁
+    _y_ringbuf_lock(ringbuf);
+#endif
+
+    // 判断头尾位置 确定删除方式
+    if (ringbuf->tail <= ringbuf->head) {
+
+        // 尾在头前面 或 在同一位置(全满)
+        uint16_t end_size = ringbuf->length - ringbuf->head;  // 尾部删除的空间
+        if (end_size > size) {
+            // 尾部数据足够 可以直接删除
+            ringbuf->head += size;
+        } else if (end_size == size) {
+            // 尾部数据刚好 可以直接删除 但头位置移到 buf 开头
+            ringbuf->head = 0;
+        } else {
+            // 尾部数据不够 需要分段删除
+            ringbuf->head = size - end_size;
+        }
+
+    } else {
+
+        // 尾在头后面 可以直接删除
+        ringbuf->head += size;
+    }
+
+    // 更新句柄相关状态
+    ringbuf->used_size -= size;
+    ringbuf->free_size += size;
+
+#if Y_RINGBUF_USE_MUTEX_LOCK == 1
+    // 解锁
+    _y_ringbuf_unlock(ringbuf);
+#endif
+
+    printf("ringbuf delete %d byte succeed", size);
+    return true;
+}
+
+/// @brief 判断 ringbuf 是否为满
+/// @param [in] ringbuf ringbuf 句柄指针
+/// @retval true  成功
+/// @retval false 失败
+bool y_ringbuf_is_full(RINGBUF_st *ringbuf) {
+
+    // 断言
+    if (ringbuf == NULL) {
+        printf("ringbuf is NULL");
+        return false;
+    }
+
+    if (ringbuf->free_size == 0) {
+        return true;
+    } else {
+        return false;
+    }
+}
+
+/// @brief 判断 ringbuf 是否为空
+/// @param [in] ringbuf ringbuf 句柄指针
+/// @retval true  成功
+/// @retval false 失败
+bool y_ringbuf_is_empty(RINGBUF_st *ringbuf) {
+
+    // 断言
+    if (ringbuf == NULL) {
+        printf("ringbuf is NULL");
+        return false;
+    }
+
+    if (ringbuf->used_size == 0) {
+        return true;
+    } else {
+        return false;
+    }
+}
+
+/// @brief 重置 ringbuf
+/// @param [in] ringbuf ringbuf 句柄指针
+/// @retval true  成功
+/// @retval false 失败
+bool y_ringbuf_reset(RINGBUF_st *ringbuf) {
+
+    // 断言
+    if (ringbuf == NULL) {
+        printf("ringbuf is NULL, ringbuf reset fail");
+        return false;
+    }
+
+#if Y_RINGBUF_USE_MUTEX_LOCK == 1
+    // 上锁
+    _y_ringbuf_lock(ringbuf);
+#endif
+
+    // 句柄状态初始化
+    ringbuf->head      = 0;                // ringbuf 头所在位置
+    ringbuf->tail      = 0;                // ringbuf 尾所在位置
+    ringbuf->used_size = 0;                // ringbuf 已使用大小
+    ringbuf->free_size = ringbuf->length;  // ringbuf 空闲大小
+
+#if Y_RINGBUF_USE_MUTEX_LOCK == 1
+    // 解锁
+    _y_ringbuf_unlock(ringbuf);
+#endif
+
+    printf("ringbuf %p reset succeed", ringbuf);
+    return true;
+}
+
+/// @brief 销毁一个 ringbuf
+/// @param [in] ringbuf  ringbuf 句柄指针
+/// @retval true  成功
+/// @retval false 失败
+bool y_ringbuf_destroy(RINGBUF_st *ringbuf) {
+
+    // 断言
+    if (ringbuf == NULL) {
+    	printf("ringbuf is NULL, no need to delete");
+        return false;
+    }
+
+#if Y_RINGBUF_USE_MUTEX_LOCK == 1
+    // 销毁互斥锁
+    if (_y_ringbuf_destroy_mutex_lock(ringbuf) == false) {
+        printf("ringbuf destroy mutex lock error, destroy ringbuf fail");
+        return false;
+    }
+#endif
+
+    // 释放内存空间
+    heap_caps_free(ringbuf->buf);
+    heap_caps_free(ringbuf);
+
+    printf("ringbuf %p destroy succeed", ringbuf);
+    ringbuf = NULL;
+
+    return true;
+}
+
+/// @brief 创建一个 ringbuf
+/// @param [in]  length  ringbuf 长度 (max 65535)
+/// @return ringbuf 句柄指针
+RINGBUF_st *y_ringbuf_create(uint16_t length) {
+
+    // 断言
+    if (length == 0) {
+        printf("ringbuf length must > 0, ringbuf create fail");
+        return NULL;
+    }
+
+    // 创建 ringbuf 句柄
+    RINGBUF_st *tmp_ringbuf = (RINGBUF_st *) heap_caps_malloc(sizeof(RINGBUF_st),MALLOC_CAP_8BIT|MALLOC_CAP_SPIRAM);  // 开辟 结构体句柄 所需要的空间
+    if (tmp_ringbuf == NULL) {
+        printf("heap_caps_malloc RINGBUF_st error");
+        return NULL;
+    } else {
+        memset(tmp_ringbuf, 0, sizeof(RINGBUF_st));  // 清零
+    }
+
+    tmp_ringbuf->buf = (uint8_t *) heap_caps_malloc(length,MALLOC_CAP_8BIT|MALLOC_CAP_SPIRAM);  // 开辟 ringbuf 所需要的空间
+    if (tmp_ringbuf->buf == NULL) {
+        printf("heap_caps_malloc ringbuf->buf error");
+        heap_caps_free(tmp_ringbuf);
+        return NULL;
+    } else {
+        memset(tmp_ringbuf->buf, 0, length);  // 清零
+    }
+
+    // 初始化 ringbuf 句柄
+    tmp_ringbuf->length    = length;
+    tmp_ringbuf->free_size = length;
+
+#if Y_RINGBUF_USE_MUTEX_LOCK == 1
+    // 创建互斥锁
+    if (_y_ringbuf_create_mutex_lock(tmp_ringbuf) == false) {
+        heap_caps_free(tmp_ringbuf->buf);
+        heap_caps_free(tmp_ringbuf);
+        tmp_ringbuf = NULL;
+        printf("ringbuf create mutex lock error, create ringbuf fail");
+        return NULL;
+    }
+#endif
+
+    printf("ringbuf %p create succeed\n", tmp_ringbuf);
+    return tmp_ringbuf;
+}

+ 17 - 0
components/USER_SPIFFS/CMakeLists.txt

@@ -0,0 +1,17 @@
+idf_component_register(SRCS "SPIFFS.c"
+                    INCLUDE_DIRS "include"  
+                    #"../../main/"
+                    REQUIRES    "spiffs"  "c_linked_list")
+
+
+
+#                     # 添加生成的文件系统
+# spiffs_create_partition_image(partitions.csv spiffs FLASH_IN_PROJECT)
+
+#set(EXTRA_COMPONENT_DIRS /path/to/your/project/components)  # 设置 components 文件夹路径
+#set(EXTRA_COMPONENT_DIRS "../components/USER_SPIFFS")
+
+
+ # 添加生成的文件系统
+ spiffs_create_partition_image(storage spiffs FLASH_IN_PROJECT)
+

+ 252 - 0
components/USER_SPIFFS/SPIFFS.c

@@ -0,0 +1,252 @@
+#include <stdio.h>
+#include "SPIFFS.h"
+
+#include <string.h>
+#include <sys/unistd.h>
+#include <sys/stat.h>
+#include "esp_err.h"
+#include "esp_log.h"
+#include "esp_spiffs.h"
+
+static const char *TAG = "user_spiffs";
+
+#include "esp_attr.h"
+
+RTC_FAST_ATTR  Machine_info_t   Machine_info;
+RTC_FAST_ATTR Node *Send_list = NULL;  //发送数据链表
+
+
+
+
+
+void spiffs_init(void)
+{
+    esp_vfs_spiffs_conf_t conf = {
+      .base_path = "/spiffs",
+      .partition_label = NULL,
+      .max_files = 5,
+      .format_if_mount_failed = true
+    };
+
+    // Use settings defined above to initialize and mount SPIFFS filesystem.
+    // Note: esp_vfs_spiffs_register is an all-in-one convenience function.
+    esp_err_t ret = esp_vfs_spiffs_register(&conf);
+
+    if (ret != ESP_OK) {
+        if (ret == ESP_FAIL) {
+            ESP_LOGE(TAG, "Failed to mount or format filesystem");
+        } else if (ret == ESP_ERR_NOT_FOUND) {
+            ESP_LOGE(TAG, "Failed to find SPIFFS partition");
+        } else {
+            ESP_LOGE(TAG, "Failed to initialize SPIFFS (%s)", esp_err_to_name(ret));
+        }
+        return;
+    }
+
+
+    size_t total = 0, used = 0;
+    ret = esp_spiffs_info(conf.partition_label, &total, &used);
+    if (ret != ESP_OK) {
+        ESP_LOGE(TAG, "Failed to get SPIFFS partition information (%s). Formatting...", esp_err_to_name(ret));
+        esp_spiffs_format(conf.partition_label);
+        return;
+    } else {
+        ESP_LOGI(TAG, "Partition size: total: %d, used: %d", total, used);
+    }
+
+
+    // Check consistency of reported partiton size info.
+    if (used > total) {
+        ESP_LOGW(TAG, "Number of used bytes cannot be larger than total. Performing SPIFFS_check().");
+        ret = esp_spiffs_check(conf.partition_label);
+        // Could be also used to mend broken files, to clean unreferenced pages, etc.
+        // More info at https://github.com/pellepl/spiffs/wiki/FAQ#powerlosses-contd-when-should-i-run-spiffs_check
+        if (ret != ESP_OK) {
+            ESP_LOGE(TAG, "SPIFFS_check() failed (%s)", esp_err_to_name(ret));
+            return;
+        } else {
+            ESP_LOGI(TAG, "SPIFFS_check() successful");
+        }
+    }
+}
+
+
+void spiffs_write(Machine_info_t* info)
+{
+    ESP_LOGI(TAG, "File written");
+    FILE* f = fopen("/spiffs/yc_data.bin", "w");
+    if (f == NULL) {
+        ESP_LOGE(TAG, "Failed to open file for writing");
+        return;
+    }
+    fwrite(info,sizeof(Machine_info_t),1,f);
+    fclose(f);
+}
+
+
+
+void spiffs_read(Machine_info_t* info)
+{
+    ESP_LOGI(TAG, "Reading file");
+    FILE* f = fopen("/spiffs/yc_data.bin", "r");
+    if (f == NULL) {
+        ESP_LOGE(TAG, "Failed to open file for reading");
+        return;
+    }
+    fread(info,sizeof(Machine_info_t),1,f);
+    fclose(f);
+}
+
+
+
+
+
+
+void left_spiffs_write(uint8_t *buffer,unsigned int size)
+{
+    // ESP_LOGI(TAG, "left File written");
+    FILE* f = fopen("/spiffs/left_display.bin", "w");
+    if (f == NULL) {
+        printf("Failed to open file for writing");
+        return;
+    }
+
+    size_t bytes_written = 0;
+    size_t bytes_to_write = size;
+
+    while (bytes_written < size) {
+        size_t write_result = fwrite(buffer + bytes_written, 1, bytes_to_write, f);
+
+        if (write_result < 0) {
+           printf("Error writing to file: left_display.bin\r\n");
+            fclose(f);
+            return;
+        }
+
+        bytes_written += write_result;
+        bytes_to_write -= write_result;
+    }
+
+
+
+    fclose(f);
+}
+
+
+
+void left_spiffs_read(uint8_t *buffer, unsigned int  size)
+{
+    ESP_LOGI(TAG, "Reading left file");
+    FILE* f = fopen("/spiffs/left_display.bin", "r");
+    if (f == NULL) {
+        printf("Failed to open file for reading");
+        return;
+    }
+
+    size_t bytes_read = 0;
+    size_t bytes_to_read = size;
+
+    while (bytes_read < size) {
+        size_t read_result = fread(buffer + bytes_read, 1, bytes_to_read, f);
+
+        if (read_result < 0) {
+            printf( "Error reading from file: left_display.bin\r\n");
+            fclose(f);
+            return;
+        }
+
+        bytes_read += read_result;
+        bytes_to_read -= read_result;
+
+        if (read_result == 0) {
+            // 文件已经读取完毕
+            break;
+        }
+    }
+    fclose(f);
+}
+
+
+
+
+
+
+void right_spiffs_write(uint8_t *buffer,unsigned int  size)
+{
+    ESP_LOGI(TAG, "right File written");
+    FILE* f = fopen("/spiffs/right_display.bin", "w");
+    if (f == NULL) {
+        printf("Failed to open file for writing\r\n");
+        return;
+    }
+
+    size_t bytes_written = 0;
+    size_t bytes_to_write = size;
+
+    while (bytes_written < size) {
+        size_t write_result = fwrite(buffer + bytes_written, 1, bytes_to_write, f);
+
+        if (write_result < 0) {
+           printf("Error writing to file: left_display.bin\r\n");
+            fclose(f);
+            return;
+        }
+
+        bytes_written += write_result;
+        bytes_to_write -= write_result;
+    }
+    
+
+    fclose(f);
+}
+
+
+
+void right_spiffs_read(uint8_t *buffer,unsigned int  size)
+{
+    ESP_LOGI(TAG, "Reading right file");
+    FILE* f = fopen("/spiffs/right_display.bin", "r");
+    if (f == NULL) {
+        printf("Failed to open file for reading");
+        return;
+    }
+    size_t bytes_read = 0;
+    size_t bytes_to_read = size;
+
+    while (bytes_read < size) {
+        size_t read_result = fread(buffer + bytes_read, 1, bytes_to_read, f);
+
+        if (read_result < 0) {
+            printf( "Error reading from file: right_display.bin\r\n");
+            fclose(f);
+            return;
+        }
+
+        bytes_read += read_result;
+        bytes_to_read -= read_result;
+
+        if (read_result == 0) {
+            // 文件已经读取完毕
+            break;
+        }
+    }
+    fclose(f);
+}
+
+
+
+size_t spiffs_read_powerOn(Machine_info_t* info)
+{
+    size_t ret;
+    ESP_LOGI(TAG, "Reading powerOn file");
+    FILE* f = fopen("/spiffs/yc_data.bin", "r");
+    if (f == NULL) {
+        printf("Failed to open file for reading");
+        return 0;
+    }
+    ret = fread(info,sizeof(Machine_info_t),1,f);//返回的不一定是读取的字节数
+    fclose(f);
+    // ESP_LOGW(TAG,"ret  = %d",ret);
+    // ESP_LOG_BUFFER_HEX(TAG,info,sizeof(Machine_info_t));
+    return ret;
+}

+ 193 - 0
components/USER_SPIFFS/include/SPIFFS.h

@@ -0,0 +1,193 @@
+#ifndef _SPIFFS_H_
+#define _SPIFFS_H_
+
+#include "list.h"
+#include "stdbool.h"
+
+#define PERSON_MAX_NAME     10
+#define PERSON_NAME_MAX_LEN  10
+
+typedef enum
+{
+      Administrator                     = 0,               //管理员
+      product_person                    = 1,               //生产责任人
+      repair_person                     = 2,               //维修责任人
+      Maintenance_person                = 3,               //保养责任人
+      check_person                      = 4,               //巡检责任人
+
+}Person_type_t;
+
+typedef struct
+{
+    uint16_t   person_num;  //序号
+    uint8_t person_name[PERSON_NAME_MAX_LEN]; //人员名称
+}Person_Name_t;
+///////////////////////////////////////////
+typedef struct
+{
+    uint8_t    person_type;  //人员类型
+    Person_Name_t  person_name[PERSON_MAX_NAME];
+    char string_name[100];
+    bool Charge_close;//为真关闭责任人显示
+    uint8_t other_name[16];//右屏第二部分责任人显示
+}Person_t;
+
+
+
+
+typedef struct
+{
+    uint8_t button_info;  //当前按键状态
+
+    uint16_t Year;
+    uint8_t Month;
+    uint8_t Day;
+    uint8_t Hour;
+    uint8_t Minute;
+    uint8_t Second;
+    
+    uint32_t time_min;
+}Button_Time_t;
+
+
+typedef struct
+{
+    bool checkIn_close;//为真关闭打卡
+    uint32_t number;//只加不减,用于左屏图标数据
+    uint8_t real_number;//真实签到人数,用于右屏
+    uint8_t other_name[10];//四类打卡类型   限制四个字
+}CheckIn_Setting_t;
+
+typedef struct
+{    
+    //所有要保存的数据
+
+    char terminal_name[20];
+    char terminal_number[20];
+    char station_name[20];
+    char station_number[20];
+
+    char btn_operation[6];
+    char btn_breakDown_info[6];
+    char btn_upKeep_info[6];
+    char btn_shutDown_info[6];
+    char btn_safeKeep_info[6];
+    char btn_waitMaterials_info[6];
+    //保存按键信息以及是否按键显示显示
+    //0表示关闭,1表示开启
+    bool btn_dis_flag[6];
+
+    uint8_t lora_factory_channel;
+    uint8_t lora_new_channel;
+    uint8_t eflagID;         //本机组内编号
+    unsigned char cid[20];   //设备ID  CID flash中获取的ID
+    uint8_t paired;          //是否配网
+    uint8_t power_status;    //当前系统的状态  开机或者关机
+    // uint8_t is_setting;      //设置当前是否为设置模式
+
+
+    Node *Send_list;
+    uint16_t msg_id;        //添加消息发送设备ID 唯一性分配
+
+    Button_Time_t  last_button;         //上次按键状态
+    Button_Time_t  current_button;      //当前按键状态
+    uint32_t Duration_time;             //持续的时间
+
+    uint8_t left_max_Quick_refresh_time;     //最大快刷次数
+    uint8_t left_current_Quick_refresh_time; //当前已经快刷的次数  当前快刷的次数大于设置 慢刷一次
+
+    uint8_t right_max_Quick_refresh_time;     //最大快刷次数
+    uint8_t right_current_Quick_refresh_time; //当前已经快刷的次数  当前快刷的次数大于设置 慢刷一次
+
+
+
+    
+    int batt_precent;       //当前显示的电量
+    int last_batt_precent;  //记录上次显示的电量
+
+    uint16_t year;
+    uint8_t month;
+    uint8_t day;
+    uint8_t hour;
+    uint8_t min;
+    uint8_t sec;
+
+    uint8_t rssi;
+
+    uint8_t left_state;//左屏的模式  --> Machine_state_t
+    uint8_t left_display_mode;//cmd 0x07 
+    uint8_t right_display_mode;//cmd 0x08
+
+#if 0
+    uint8_t personnel_check_in[4];//存签到人数。
+    uint8_t person_in_charge_name[4][12];//责任人名称
+#else
+    CheckIn_Setting_t checkIn_set[4];//签到相关
+#endif
+
+    uint8_t mac_addr[6];
+    // uint8_t p_name[32];//未使用
+    uint8_t refresh_cycle;
+  
+    uint8_t announcement[120];  //公告
+    uint8_t systemMessage[120];  //系统消息
+    Person_t  person[5];//
+
+    uint8_t gateway_mac[6];  //网关的mac地址
+
+
+  
+
+    //保存右屏图表信息
+    uint32_t num_goodProducts[8];
+    uint32_t num_badProducts[8];
+
+    uint8_t scale_UR_int[8];
+    uint8_t scale_UR_dec[8];
+    uint8_t scale_YR_int[8];
+    uint8_t scale_YR_dec[8];
+
+    uint16_t num_manHour[8];
+    uint16_t num_people[8];
+
+    uint16_t num_production[8];
+    uint16_t num_repair[8];
+    uint16_t num_inspection[8];
+    uint16_t num_upkeep[8];
+
+    uint8_t wait_send_rssi_bat;
+
+    bool is_charge;      //判断是否充电
+    bool is_charge_full; //判断是否充满
+
+    char timestamp[20];   //当前时间戳
+    int time_offset;      //时区偏移
+
+}Machine_info_t;//所有要保存的数据
+
+extern Machine_info_t Machine_info;
+extern  Node *Send_list;  //发送数据链表
+
+
+void spiffs_init(void);
+void spiffs_write(Machine_info_t* info);
+void spiffs_read(Machine_info_t* info);
+
+
+
+
+void left_spiffs_write(uint8_t *buffer,unsigned int  size);
+void left_spiffs_read(uint8_t *buffer,unsigned int  size);
+
+
+void right_spiffs_write(uint8_t *buffer,unsigned int  size);
+
+void right_spiffs_read(uint8_t *buffer,unsigned int  size);
+
+
+size_t spiffs_read_powerOn(Machine_info_t* info);
+
+
+
+
+#endif/*_SPIFFS_H_*/

BIN
components/USER_SPIFFS/spiffs/left_display.bin


BIN
components/USER_SPIFFS/spiffs/right_display.bin


BIN
components/USER_SPIFFS/spiffs/yc_data.bin


+ 88 - 0
components/button/CHANGELOG.md

@@ -0,0 +1,88 @@
+# ChangeLog
+
+## v3.1.2 - 2023-10-24
+
+### bugfix
+
+* Fixed a bug where iot_button_delete feature crashes for custom button
+
+## v3.1.1 - 2023-10-18
+
+### bugfix
+
+* Fixed a bug where multiple callbacks feature crashes for BUTTON_MULTIPLE_CLICK
+
+## v3.1.0 - 2023-10-9
+
+### Enhancements:
+
+* Support matrix keypad
+
+## v3.0.1 - 2023-9-1
+
+### Enhancements:
+
+* Resolves bug for iot_button_unregister_event function returned error when reallocating with 0 byte.
+* Update Test cases to test iot_button_unregister_event_cb
+* Add api iot_button_stop & iot_button_resume for power save.
+
+## v3.0.0 - 2023-8-15
+
+### Enhancements:
+
+* Add support to register multiple callbacks for a button_event
+
+    * Update iot_button_unregister_cb, to unregister all the callbacks for that event
+    * Add iot_button_unregister_event to unregister specific callbacks of that event
+    * Add iot_button_count_event to return number of callbacks registered for the event.
+    * Update iot_button_count_cb, to return sum of number of registered callbacks.
+
+* Add support for Long press on specific time
+
+    * Add iot_button_register_event, which takes specific event_config_t data as input.
+    * Add BUTTON_LONG_PRESS_UP to trigger callback at the latest time of button release
+    * Update BUTTON_LONG_PRESS_START to trigger callback as the time passes for long_press.
+
+* Add support to trigger callback for specified number of clicks.
+
+## v2.5.6 - 2023-8-22
+
+### bugfix
+
+* Fixed a bug where the Serial trigger interval in button_long_press_hold event fires at an incorrect time
+
+## v2.5.5 - 2023-8-3
+
+* Add modify api which can change long_press_time and short_press_time
+
+## v2.5.4 - 2023-7-27
+
+### Enhancements:
+
+* Add test apps and ci auto test
+
+## v2.5.3 - 2023-7-26
+
+### Enhancements:
+
+* `repeat` defined in struct button_dev_t is reset to 0 after event `BUTTON_PRESS_REPEAT_DONE`
+
+## v2.5.2 - 2023-7-18
+
+### Enhancements:
+
+* Set "event" member to BUTTON_PRESS_REPEAT before calling the BUTTON_PRESS_REPEAT callback
+
+## v2.5.1 - 2023-3-14
+
+### Enhancements:
+
+* Update doc and code specification
+* Avoid overwriting callback by @franz-ms-muc in #252
+
+## v2.5.0 - 2023-2-1
+
+### Enhancements:
+
+* Support custom button 
+* Add BUTTON_PRESS_REPEAT_DONE event

+ 15 - 0
components/button/CMakeLists.txt

@@ -0,0 +1,15 @@
+set(PRIVREQ esp_timer)
+
+if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER_EQUAL "5.0")
+    list(APPEND REQ esp_adc)
+else() 
+    list(APPEND REQ esp_adc_cal)
+endif()
+
+idf_component_register(SRCS "button_adc.c" "button_gpio.c" "iot_button.c"  "button_matrix.c" "user_button.c"
+                        INCLUDE_DIRS include
+                        REQUIRES driver ${REQ}  USER_SPIFFS
+                        PRIV_REQUIRES ${PRIVREQ})
+
+#include(package_manager)
+#cu_pkg_define_version(${CMAKE_CURRENT_LIST_DIR})

+ 59 - 0
components/button/Kconfig

@@ -0,0 +1,59 @@
+menu "IoT Button"
+    
+    config BUTTON_PERIOD_TIME_MS
+        int "BUTTON PERIOD TIME (MS)"
+        range 2 20
+        default 5
+        help
+            "Button scan interval"
+
+    config BUTTON_DEBOUNCE_TICKS
+        int "BUTTON DEBOUNCE TICKS"
+        range 1 8
+        default 2
+        help
+            "One CONFIG_BUTTON_DEBOUNCE_TICKS equal to CONFIG_BUTTON_PERIOD_TIME_MS"
+
+    config BUTTON_SHORT_PRESS_TIME_MS
+        int "BUTTON SHORT PRESS TIME (MS)"
+        range 50 800
+        default 180
+
+    config BUTTON_LONG_PRESS_TIME_MS
+        int "BUTTON LONG PRESS TIME (MS)"
+        range 500 5000
+        default 1500
+
+    config BUTTON_LONG_PRESS_TOLERANCE_MS
+        int "BUTTON LONG PRESS TOLERANCE TIME (MS)"
+        default 20
+
+    config BUTTON_SERIAL_TIME_MS
+        int "BUTTON SERIAL TIME (MS)"
+        range 2 1000
+        default 20
+        help
+            "Serial trigger interval"
+
+    config ADC_BUTTON_MAX_CHANNEL
+        int "ADC BUTTON MAX CHANNEL"
+        range 1 5
+        default 3
+        help
+            "Maximum number of channels for ADC buttons"
+
+    config ADC_BUTTON_MAX_BUTTON_PER_CHANNEL
+        int "ADC BUTTON MAX BUTTON PER CHANNEL"
+        range 1 10
+        default 8
+        help
+            "Maximum number of buttons per channel"
+
+    config ADC_BUTTON_SAMPLE_TIMES
+        int "ADC BUTTON SAMPLE TIMES"
+        range 1 4
+        default 1
+        help
+            "Number of samples per scan"
+
+endmenu

+ 33 - 0
components/button/README.md

@@ -0,0 +1,33 @@
+[![Component Registry](https://components.espressif.com/components/espressif/button/badge.svg)](https://components.espressif.com/components/espressif/button)
+
+# Component: Button
+[Online documentation](https://docs.espressif.com/projects/esp-iot-solution/en/latest/input_device/button.html)
+
+After creating a new button object by calling function `button_create()`, the button object can create press events, every press event can have its own callback.
+
+List of supported events:
+ * Button pressed
+ * Button released
+ * Button pressed repeat
+ * Button press repeat done
+ * Button single click
+ * Button double click
+ * Button multiple click
+ * Button long press start
+ * Button long press hold
+ * Button long press up
+
+![](https://dl.espressif.com/button_v2/button.svg)
+
+There are three ways this driver can handle buttons:
+1. Buttons connected to standard digital GPIO
+2. Multiple buttons connected to single ADC channel
+3. Custom button connect to any driver
+
+## Add component to your project
+
+Please use the component manager command `add-dependency` to add the `button` to your project's dependency, during the `CMake` step the component will be downloaded automatically
+
+```
+idf.py add-dependency "espressif/button=*"
+```

+ 341 - 0
components/button/button_adc.c

@@ -0,0 +1,341 @@
+/* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <string.h>
+#include <inttypes.h>
+#include "esp_log.h"
+#include "esp_timer.h"
+#include "esp_idf_version.h"
+#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
+#include "soc/soc_caps.h"
+#include "esp_adc/adc_oneshot.h"
+#include "esp_adc/adc_cali.h"
+#include "esp_adc/adc_cali_scheme.h"
+#else
+#include "driver/gpio.h"
+#include "driver/adc.h"
+#include "esp_adc_cal.h"
+#endif
+#include "button_adc.h"
+#include "hal/adc_hal.h"
+
+static const char *TAG = "adc button";
+
+#define ADC_BTN_CHECK(a, str, ret_val)                          \
+    if (!(a))                                                     \
+    {                                                             \
+       printf("%s(%d): %s", __FUNCTION__, __LINE__, str); \
+        return (ret_val);                                         \
+    }
+
+#define DEFAULT_VREF    1100
+#define NO_OF_SAMPLES   CONFIG_ADC_BUTTON_SAMPLE_TIMES     //Multisampling
+
+#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
+#define ADC_BUTTON_WIDTH        SOC_ADC_RTC_MAX_BITWIDTH
+#define ADC1_BUTTON_CHANNEL_MAX SOC_ADC_MAX_CHANNEL_NUM
+#define ADC_BUTTON_ATTEN        ADC_ATTEN_DB_11
+#else
+#define ADC_BUTTON_WIDTH        ADC_WIDTH_MAX-1
+#define ADC1_BUTTON_CHANNEL_MAX ADC1_CHANNEL_MAX
+#define ADC_BUTTON_ATTEN        ADC_ATTEN_DB_11
+#endif
+#define ADC_BUTTON_ADC_UNIT     ADC_UNIT_1
+#define ADC_BUTTON_MAX_CHANNEL  CONFIG_ADC_BUTTON_MAX_CHANNEL
+#define ADC_BUTTON_MAX_BUTTON   CONFIG_ADC_BUTTON_MAX_BUTTON_PER_CHANNEL
+
+typedef struct {
+    uint16_t min;
+    uint16_t max;
+} button_data_t;
+
+typedef struct {
+    uint8_t channel;
+    uint8_t is_init;
+    button_data_t btns[ADC_BUTTON_MAX_BUTTON];  /* all button on the channel */
+    uint64_t last_time;  /* the last time of adc sample */
+} btn_adc_channel_t;
+
+typedef struct {
+    bool is_configured;
+#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
+    adc_cali_handle_t adc1_cali_handle;
+    adc_oneshot_unit_handle_t adc1_handle;
+#else
+    esp_adc_cal_characteristics_t adc_chars;
+#endif
+    btn_adc_channel_t ch[ADC_BUTTON_MAX_CHANNEL];
+    uint8_t ch_num;
+} adc_button_t;
+
+static adc_button_t g_button = {0};
+
+static int find_unused_channel(void)
+{
+    for (size_t i = 0; i < ADC_BUTTON_MAX_CHANNEL; i++) {
+        if (0 == g_button.ch[i].is_init) {
+            return i;
+        }
+    }
+    return -1;
+}
+
+static int find_channel(uint8_t channel)
+{
+    for (size_t i = 0; i < ADC_BUTTON_MAX_CHANNEL; i++) {
+        if (channel == g_button.ch[i].channel) {
+            return i;
+        }
+    }
+    return -1;
+}
+
+#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
+static esp_err_t adc_calibration_init(adc_unit_t unit, adc_atten_t atten, adc_cali_handle_t *out_handle)
+{
+    adc_cali_handle_t handle = NULL;
+    esp_err_t ret = ESP_FAIL;
+    bool calibrated = false;
+
+#if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
+    if (!calibrated) {
+        ESP_LOGI(TAG, "calibration scheme version is %s", "Curve Fitting");
+        adc_cali_curve_fitting_config_t cali_config = {
+            .unit_id = unit,
+            .atten = atten,
+            .bitwidth = ADC_BUTTON_WIDTH,
+        };
+        ret = adc_cali_create_scheme_curve_fitting(&cali_config, &handle);
+        if (ret == ESP_OK) {
+            calibrated = true;
+        }
+    }
+#endif
+
+#if ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED
+    if (!calibrated) {
+        ESP_LOGI(TAG, "calibration scheme version is %s", "Line Fitting");
+        adc_cali_line_fitting_config_t cali_config = {
+            .unit_id = unit,
+            .atten = atten,
+            .bitwidth = ADC_BUTTON_WIDTH,
+        };
+        ret = adc_cali_create_scheme_line_fitting(&cali_config, &handle);
+        if (ret == ESP_OK) {
+            calibrated = true;
+        }
+    }
+#endif
+
+    *out_handle = handle;
+    if (ret == ESP_OK) {
+        ESP_LOGI(TAG, "Calibration Success");
+    } else if (ret == ESP_ERR_NOT_SUPPORTED || !calibrated) {
+        ESP_LOGW(TAG, "eFuse not burnt, skip software calibration");
+    } else {
+        ESP_LOGE(TAG, "Invalid arg or no memory");
+    }
+
+    return calibrated?ESP_OK:ESP_FAIL;
+}
+#endif
+
+esp_err_t button_adc_init(const button_adc_config_t *config)
+{
+    ADC_BTN_CHECK(NULL != config, "Pointer of config is invalid", ESP_ERR_INVALID_ARG);
+    ADC_BTN_CHECK(config->adc_channel < ADC1_BUTTON_CHANNEL_MAX, "channel out of range", ESP_ERR_NOT_SUPPORTED);
+    ADC_BTN_CHECK(config->button_index < ADC_BUTTON_MAX_BUTTON, "button_index out of range", ESP_ERR_NOT_SUPPORTED);
+    ADC_BTN_CHECK(config->max > 0, "key max voltage invalid", ESP_ERR_INVALID_ARG);
+
+    int ch_index = find_channel(config->adc_channel);
+    if (ch_index >= 0) { /**< the channel has been initialized */
+        ADC_BTN_CHECK(g_button.ch[ch_index].btns[config->button_index].max == 0, "The button_index has been used", ESP_ERR_INVALID_STATE);
+    } else { /**< this is a new channel */
+        int unused_ch_index = find_unused_channel();
+        ADC_BTN_CHECK(unused_ch_index >= 0, "exceed max channel number, can't create a new channel", ESP_ERR_INVALID_STATE);
+        ch_index = unused_ch_index;
+    }
+
+    /** initialize adc */
+    if (0 == g_button.is_configured) {
+#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
+        esp_err_t ret;
+        if (NULL == config->adc_handle) {
+            //ADC1 Init
+            adc_oneshot_unit_init_cfg_t init_config = {
+                .unit_id = ADC_UNIT_1,
+            };
+            ret = adc_oneshot_new_unit(&init_config, &g_button.adc1_handle);
+            ADC_BTN_CHECK(ret == ESP_OK, "adc oneshot new unit fail!", ESP_FAIL);
+        } else {
+            g_button.adc1_handle = *config->adc_handle ;
+            ESP_LOGI(TAG, "ADC1 has been initialized");
+        }
+#else
+        //Configure ADC
+        adc1_config_width(ADC_BUTTON_WIDTH);
+        //Characterize ADC
+        esp_adc_cal_value_t val_type = esp_adc_cal_characterize(ADC_BUTTON_ADC_UNIT, ADC_BUTTON_ATTEN, ADC_BUTTON_WIDTH, DEFAULT_VREF, &g_button.adc_chars);
+        if (val_type == ESP_ADC_CAL_VAL_EFUSE_TP) {
+            ESP_LOGI(TAG, "Characterized using Two Point Value");
+        } else if (val_type == ESP_ADC_CAL_VAL_EFUSE_VREF) {
+            ESP_LOGI(TAG, "Characterized using eFuse Vref");
+        } else {
+            ESP_LOGI(TAG, "Characterized using Default Vref");
+        }
+#endif
+        g_button.is_configured = 1;
+    }
+
+    /** initialize adc channel */
+    if (0 == g_button.ch[ch_index].is_init) {
+#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
+        //ADC1 Config
+        adc_oneshot_chan_cfg_t oneshot_config = {
+            .bitwidth = ADC_BUTTON_WIDTH,
+            .atten = ADC_BUTTON_ATTEN,
+        };
+        esp_err_t ret = adc_oneshot_config_channel(g_button.adc1_handle, config->adc_channel, &oneshot_config);
+        ADC_BTN_CHECK(ret == ESP_OK, "adc oneshot config channel fail!", ESP_FAIL);
+        //-------------ADC1 Calibration Init---------------//
+        ret = adc_calibration_init(ADC_BUTTON_ADC_UNIT, ADC_BUTTON_ATTEN, &g_button.adc1_cali_handle);
+        ADC_BTN_CHECK(ret == ESP_OK, "ADC1 Calibration Init False", 0);
+#else
+        adc1_config_channel_atten(config->adc_channel, ADC_BUTTON_ATTEN);
+#endif
+        g_button.ch[ch_index].channel = config->adc_channel;
+        g_button.ch[ch_index].is_init = 1;
+        g_button.ch[ch_index].last_time = 0;
+    }
+    g_button.ch[ch_index].btns[config->button_index].max = config->max;
+    g_button.ch[ch_index].btns[config->button_index].min = config->min;
+    g_button.ch_num++;
+
+    return ESP_OK;
+}
+
+esp_err_t button_adc_deinit(uint8_t channel, int button_index)
+{
+    ADC_BTN_CHECK(channel < ADC1_BUTTON_CHANNEL_MAX, "channel out of range", ESP_ERR_INVALID_ARG);
+    ADC_BTN_CHECK(button_index < ADC_BUTTON_MAX_BUTTON, "button_index out of range", ESP_ERR_INVALID_ARG);
+
+    int ch_index = find_channel(channel);
+    ADC_BTN_CHECK(ch_index >= 0, "can't find the channel", ESP_ERR_INVALID_ARG);
+
+    g_button.ch[ch_index].btns[button_index].max = 0;
+    g_button.ch[ch_index].btns[button_index].min = 0;
+
+    /** check button usage on the channel*/
+    uint8_t unused_button = 0;
+    for (size_t i = 0; i < ADC_BUTTON_MAX_BUTTON; i++) {
+        if (0 == g_button.ch[ch_index].btns[i].max) {
+            unused_button++;
+        }
+    }
+    if (unused_button == ADC_BUTTON_MAX_BUTTON && g_button.ch[ch_index].is_init) {  /**< if all button is unused, deinit the channel */
+        g_button.ch[ch_index].is_init = 0;
+        g_button.ch[ch_index].channel = ADC1_BUTTON_CHANNEL_MAX;
+        ESP_LOGD(TAG, "all button is unused on channel%d, deinit the channel", g_button.ch[ch_index].channel);
+    }
+
+    /** check channel usage on the adc*/
+    uint8_t unused_ch = 0;
+    for (size_t i = 0; i < ADC_BUTTON_MAX_CHANNEL; i++) {
+        if (0 == g_button.ch[i].is_init) {
+            unused_ch++;
+        }
+    }
+    if (unused_ch == ADC_BUTTON_MAX_CHANNEL && g_button.is_configured) { /**< if all channel is unused, deinit the adc */
+        g_button.is_configured = false;
+        memset(&g_button, 0, sizeof(adc_button_t));
+        ESP_LOGD(TAG, "all channel is unused, , deinit adc");
+    }
+#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
+    esp_err_t ret = adc_oneshot_del_unit(g_button.adc1_handle);
+    ADC_BTN_CHECK(ret == ESP_OK, "adc oneshot deinit fail", ESP_FAIL);
+#endif
+    return ESP_OK;
+}
+
+int i = 0;
+static uint32_t get_adc_volatge(uint8_t channel)
+{
+    uint32_t adc_reading = 0;
+#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
+    int adc_raw = 0;
+    for (int i = 0; i < NO_OF_SAMPLES; i++) {
+        //adc_ll_set_power_manage(ADC_POWER_BY_FSM);
+        adc_oneshot_read(g_button.adc1_handle, channel, &adc_raw);
+        //adc_ll_set_power_manage(ADC_POWER_SW_OFF);
+        adc_reading += adc_raw;
+    }
+    adc_reading /= NO_OF_SAMPLES;
+    //Convert adc_reading to voltage in mV
+    int voltage = 0;
+    adc_cali_raw_to_voltage(g_button.adc1_cali_handle, adc_reading, &voltage);
+    // ESP_LOGV(TAG, "Raw: %"PRIu32"\tVoltage: %dmV", adc_reading, voltage);
+    // printf("Raw: %"PRIu32"\tVoltage: %"PRIu32"mV\r\n", adc_reading, voltage);
+    // i++;
+    // if(i>100)
+    // {
+    //     i = 0;
+    //     printf("Raw: %lu Voltage: %d mV\r\n", adc_reading, voltage);
+    // }
+#else
+    //Multisampling
+    for (int i = 0; i < NO_OF_SAMPLES; i++) {
+        adc_reading += adc1_get_raw(channel);
+    }
+    adc_reading /= NO_OF_SAMPLES;
+    //Convert adc_reading to voltage in mV
+    uint32_t voltage = esp_adc_cal_raw_to_voltage(adc_reading, &g_button.adc_chars);
+    ESP_LOGV(TAG, "Raw: %"PRIu32"\tVoltage: %"PRIu32"mV", adc_reading, voltage);
+    i++;
+    if(i>10000)
+    {
+        i = 0;
+        printf("Raw: %lu Voltage: %d mV\r\n", adc_reading, voltage);
+    }
+ 
+#endif
+    return voltage;
+}
+
+uint8_t button_adc_get_key_level(void *button_index)
+{
+    static uint16_t vol = 0;
+    uint32_t ch = ADC_BUTTON_SPLIT_CHANNEL(button_index);
+    uint32_t index = ADC_BUTTON_SPLIT_INDEX(button_index);
+    ADC_BTN_CHECK(ch < ADC1_BUTTON_CHANNEL_MAX, "channel out of range", 0);
+    ADC_BTN_CHECK(index < ADC_BUTTON_MAX_BUTTON, "button_index out of range", 0);
+    int ch_index = find_channel(ch);
+    ADC_BTN_CHECK(ch_index >= 0, "The button_index is not init", 0);
+
+    /** It starts only when the elapsed time is more than 1ms */
+    if ((esp_timer_get_time() - g_button.ch[ch_index].last_time) > 1000) {
+        vol = get_adc_volatge(ch);
+    // i++;
+    // if(i>100)
+    // {
+    //     i = 0;
+    //     printf("adc vol = %d ch = %ld,index = %ld,ch_index = %d\r\n",vol,ch,index,ch_index);
+    // }
+    //printf("adc vol = %d\r\n",vol);
+        g_button.ch[ch_index].last_time = esp_timer_get_time();
+    }
+
+    //  printf("adc vol = %d ch = %ld,index = %ld,ch_index = %d\r\n",vol,ch,index,ch_index);
+    // printf("adc vol = %d ch = %ld,index = %ld,ch_index = %d\r\n",vol,ch,index,ch_index);
+    // printf("max = %d\r\n",g_button.ch[ch_index].btns[index].max);
+    // printf("min = %d\r\n",g_button.ch[ch_index].btns[index].min);
+
+
+
+    if (vol <= g_button.ch[ch_index].btns[index].max &&
+            vol >= g_button.ch[ch_index].btns[index].min) {
+        return 1;
+    }
+    return 0;
+}

+ 48 - 0
components/button/button_gpio.c

@@ -0,0 +1,48 @@
+/* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "esp_log.h"
+#include "driver/gpio.h"
+#include "button_gpio.h"
+
+static const char *TAG = "gpio button";
+
+#define GPIO_BTN_CHECK(a, str, ret_val)                          \
+    if (!(a))                                                     \
+    {                                                             \
+        ESP_LOGE(TAG, "%s(%d): %s", __FUNCTION__, __LINE__, str); \
+        return (ret_val);                                         \
+    }
+
+esp_err_t button_gpio_init(const button_gpio_config_t *config)
+{
+    GPIO_BTN_CHECK(NULL != config, "Pointer of config is invalid", ESP_ERR_INVALID_ARG);
+    GPIO_BTN_CHECK(GPIO_IS_VALID_GPIO(config->gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG);
+
+    gpio_config_t gpio_conf;
+    gpio_conf.intr_type = GPIO_INTR_DISABLE;
+    gpio_conf.mode = GPIO_MODE_INPUT;
+    gpio_conf.pin_bit_mask = (1ULL << config->gpio_num);
+    if (config->active_level) {
+        gpio_conf.pull_down_en = GPIO_PULLDOWN_ENABLE;
+        gpio_conf.pull_up_en = GPIO_PULLUP_DISABLE;
+    } else {
+        gpio_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
+        gpio_conf.pull_up_en = GPIO_PULLUP_ENABLE;
+    }
+    gpio_config(&gpio_conf);
+
+    return ESP_OK;
+}
+
+esp_err_t button_gpio_deinit(int gpio_num)
+{
+    return gpio_reset_pin(gpio_num);;
+}
+
+uint8_t button_gpio_get_key_level(void *gpio_num)
+{
+    return (uint8_t)gpio_get_level((uint32_t)gpio_num);
+}

+ 59 - 0
components/button/button_matrix.c

@@ -0,0 +1,59 @@
+/*
+ * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "esp_log.h"
+#include "driver/gpio.h"
+#include "button_matrix.h"
+
+static const char *TAG = "matrix button";
+
+#define MATRIX_BTN_CHECK(a, str, ret_val)                          \
+    if (!(a))                                                     \
+    {                                                             \
+        ESP_LOGE(TAG, "%s(%d): %s", __FUNCTION__, __LINE__, str); \
+        return (ret_val);                                         \
+    }
+
+esp_err_t button_matrix_init(const button_matrix_config_t *config)
+{
+    MATRIX_BTN_CHECK(NULL != config, "Pointer of config is invalid", ESP_ERR_INVALID_ARG);
+    MATRIX_BTN_CHECK(GPIO_IS_VALID_GPIO(config->row_gpio_num), "row GPIO number error", ESP_ERR_INVALID_ARG);
+    MATRIX_BTN_CHECK(GPIO_IS_VALID_GPIO(config->col_gpio_num), "col GPIO number error", ESP_ERR_INVALID_ARG);
+
+    // set row gpio as output
+    gpio_config_t gpio_conf = {0};
+    gpio_conf.intr_type = GPIO_INTR_DISABLE;
+    gpio_conf.mode = GPIO_MODE_OUTPUT;
+    gpio_conf.pull_down_en = GPIO_PULLDOWN_ENABLE;
+    gpio_conf.pin_bit_mask = (1ULL << config->row_gpio_num);
+    gpio_config(&gpio_conf);
+
+    // set col gpio as input
+    gpio_conf.mode = GPIO_MODE_INPUT;
+    gpio_conf.pin_bit_mask = (1ULL << config->col_gpio_num);
+    gpio_config(&gpio_conf);
+
+    return ESP_OK;
+}
+
+esp_err_t button_matrix_deinit(int row_gpio_num, int col_gpio_num)
+{
+    //Reset an gpio to default state (select gpio function, enable pullup and disable input and output).
+    gpio_reset_pin(row_gpio_num);
+    gpio_reset_pin(col_gpio_num);
+    return ESP_OK;
+}
+
+uint8_t button_matrix_get_key_level(void *hardware_data)
+{
+    uint32_t row = MATRIX_BUTTON_SPLIT_ROW(hardware_data);
+    uint32_t col = MATRIX_BUTTON_SPLIT_COL(hardware_data);
+    gpio_set_level(row, 1);
+    uint8_t level = gpio_get_level(col);
+    gpio_set_level(row, 0);
+
+    return level;
+}

+ 76 - 0
components/button/include/button_adc.h

@@ -0,0 +1,76 @@
+/* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#pragma once
+
+#include "esp_idf_version.h"
+#include "driver/gpio.h"
+#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
+#include "esp_adc/adc_oneshot.h"
+#else
+#include "driver/adc.h"
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ADC_BUTTON_COMBINE(channel, index) ((channel)<<8 | (index))
+#define ADC_BUTTON_SPLIT_INDEX(data) ((uint32_t)(data)&0xff)
+#define ADC_BUTTON_SPLIT_CHANNEL(data) (((uint32_t)(data) >> 8) & 0xff)
+
+/**
+ * @brief adc button configuration
+ * 
+ */
+typedef struct {
+    uint8_t adc_channel;                             /**< Channel of ADC */
+    uint8_t button_index;                            /**< button index on the channel */
+    uint16_t min;                                    /**< min voltage in mv corresponding to the button */
+    uint16_t max;                                    /**< max voltage in mv corresponding to the button */
+#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
+    adc_oneshot_unit_handle_t *adc_handle;           /**< handle of adc unit, if NULL will create new one internal, else will use the handle */
+#endif
+} button_adc_config_t;
+
+/**
+ * @brief Initialize gpio button
+ * 
+ * @param config pointer of configuration struct
+ * 
+ * @return
+ *      - ESP_OK on success
+ *      - ESP_ERR_INVALID_ARG   Arguments is NULL.
+ *      - ESP_ERR_NOT_SUPPORTED Arguments out of range.
+ *      - ESP_ERR_INVALID_STATE State is error.
+ */
+esp_err_t button_adc_init(const button_adc_config_t *config);
+
+/**
+ * @brief Deinitialize gpio button
+ * 
+ * @param channel ADC channel
+ * @param button_index Button index on the channel
+ * 
+ * @return
+ *      - ESP_OK on success
+ *      - ESP_ERR_INVALID_ARG   Arguments is invalid.
+ */
+esp_err_t button_adc_deinit(uint8_t channel, int button_index);
+
+/**
+ * @brief Get the adc button level
+ * 
+ * @param button_index It is compressed by ADC channel and button index, use the macro ADC_BUTTON_COMBINE to generate. It will be treated as a uint32_t variable.
+ * 
+ * @return 
+ *      - 0 Not pressed
+ *      - 1 Pressed
+ */
+uint8_t button_adc_get_key_level(void *button_index);
+
+#ifdef __cplusplus
+}
+#endif

+ 54 - 0
components/button/include/button_gpio.h

@@ -0,0 +1,54 @@
+/* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#pragma once
+
+#include "driver/gpio.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief gpio button configuration
+ * 
+ */
+typedef struct {
+    int32_t gpio_num;              /**< num of gpio */
+    uint8_t active_level;          /**< gpio level when press down */
+} button_gpio_config_t;
+
+/**
+ * @brief Initialize gpio button
+ * 
+ * @param config pointer of configuration struct
+ * 
+ * @return
+ *      - ESP_OK on success
+ *      - ESP_ERR_INVALID_ARG   Arguments is NULL.
+ */
+esp_err_t button_gpio_init(const button_gpio_config_t *config);
+
+/**
+ * @brief Deinitialize gpio button
+ * 
+ * @param gpio_num gpio number of button
+ * 
+ * @return Always return ESP_OK
+ */
+esp_err_t button_gpio_deinit(int gpio_num);
+
+/**
+ * @brief Get current level on button gpio
+ * 
+ * @param gpio_num gpio number of button, it will be treated as a uint32_t variable.
+ * 
+ * @return Level on gpio
+ */
+uint8_t button_gpio_get_key_level(void *gpio_num);
+
+#ifdef __cplusplus
+}
+#endif

+ 80 - 0
components/button/include/button_matrix.h

@@ -0,0 +1,80 @@
+/*
+ * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define MATRIX_BUTTON_COMBINE(row_gpio, col_gpio) ((row_gpio)<<8 | (col_gpio))
+#define MATRIX_BUTTON_SPLIT_COL(data) ((uint32_t)(data)&0xff)
+#define MATRIX_BUTTON_SPLIT_ROW(data) (((uint32_t)(data) >> 8) & 0xff)
+
+/**
+ * @brief Button matrix key configuration.
+ *        Just need to configure the GPIO associated with this GPIO in the matrix keyboard.
+ * 
+ *        Matrix Keyboard Layout (3x3):
+ *        ----------------------------------------
+ *        |  Button 1  |  Button 2  |  Button 3  |
+ *        |  (R1-C1)   |  (R1-C2)   |  (R1-C3)   |
+ *        |--------------------------------------|
+ *        |  Button 4  |  Button 5  |  Button 6  |
+ *        |  (R2-C1)   |  (R2-C2)   |  (R2-C3)   |
+ *        |--------------------------------------|
+ *        |  Button 7  |  Button 8  |  Button 9  |
+ *        |  (R3-C1)   |  (R3-C2)   |  (R3-C3)   |
+ *        ----------------------------------------
+ * 
+ *        - Button matrix key is driven using row scanning.
+ *        - Buttons within the same column cannot be detected simultaneously,
+ *          but buttons within the same row can be detected without conflicts.
+ */
+typedef struct {
+    int32_t row_gpio_num;        /**< GPIO number associated with the row */
+    int32_t col_gpio_num;        /**< GPIO number associated with the column */
+} button_matrix_config_t;
+
+/**
+ * @brief Initialize a button matrix keyboard.
+ * 
+ * @param config Pointer to the button matrix key configuration.
+ * @return
+ *      - ESP_OK on success
+ *      - ESP_ERR_INVALID_ARG if the argument is NULL.
+ * 
+ * @note When initializing the button matrix keyboard, the row GPIO pins will be set as outputs,
+ *       and the column GPIO pins will be set as inputs, both with pull-down resistors enabled.
+ */
+esp_err_t button_matrix_init(const button_matrix_config_t *config);
+
+/**
+ * @brief Deinitialize a button in the matrix keyboard.
+ * 
+ * @param row_gpio_num GPIO number of the row where the button is located.
+ * @param col_gpio_num GPIO number of the column where the button is located.
+ * @return
+ *      - ESP_OK if the button is successfully deinitialized
+ * 
+ * @note When deinitializing a button, please exercise caution and avoid deinitializing a button individually, as it may affect the proper functioning of other buttons in the same row or column.
+ */
+esp_err_t button_matrix_deinit(int row_gpio_num, int col_gpio_num);
+
+/**
+ * @brief Get the key level from the button matrix hardware.
+ * 
+ * @param hardware_data Pointer to hardware-specific data containing information about row GPIO and column GPIO.
+ * @return uint8_t[out] The key level read from the hardware.
+ * 
+ * @note This function retrieves the key level from the button matrix hardware.
+ *       The `hardware_data` parameter should contain information about the row and column GPIO pins,
+ *       and you can access this information using the `MATRIX_BUTTON_SPLIT_COL` and `MATRIX_BUTTON_SPLIT_ROW` macros.
+ */
+uint8_t button_matrix_get_key_level(void *hardware_data);
+
+#ifdef __cplusplus
+}
+#endif

+ 291 - 0
components/button/include/iot_button.h

@@ -0,0 +1,291 @@
+/* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#pragma once
+
+#include "button_adc.h"
+#include "button_gpio.h"
+#include "button_matrix.h"
+#include "esp_err.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef void (* button_cb_t)(void *button_handle, void *usr_data);
+typedef void *button_handle_t;
+
+/**
+ * @brief Button events
+ *
+ */
+typedef enum {
+    BUTTON_PRESS_DOWN = 0,
+    BUTTON_PRESS_UP,
+    BUTTON_PRESS_REPEAT,
+    BUTTON_PRESS_REPEAT_DONE,
+    BUTTON_SINGLE_CLICK,
+    BUTTON_DOUBLE_CLICK,
+    BUTTON_MULTIPLE_CLICK,
+    BUTTON_LONG_PRESS_START,
+    BUTTON_LONG_PRESS_HOLD,
+    BUTTON_LONG_PRESS_UP,
+    BUTTON_EVENT_MAX,
+    BUTTON_NONE_PRESS,
+} button_event_t;
+
+/**
+ * @brief Button events data
+ *
+ */
+typedef union {
+    /**
+     * @brief Long press time event data
+     *
+     */
+    struct long_press_t {
+        uint16_t press_time;    /**< press time(ms) for the corresponding callback to trigger */
+    } long_press;               /**< long press struct, for event BUTTON_LONG_PRESS_START and BUTTON_LONG_PRESS_UP */
+
+    /**
+     * @brief Multiple clicks event data
+     *
+     */
+    struct multiple_clicks_t {
+        uint16_t clicks;        /**< number of clicks, to trigger the callback */
+    } multiple_clicks;          /**< multiple clicks struct, for event BUTTON_MULTIPLE_CLICK */
+}button_event_data_t;
+
+/**
+ * @brief Button events configuration
+ *
+ */
+typedef struct {
+    button_event_t event;               /**< button event type */
+    button_event_data_t event_data;     /**< event data corresponding to the event */
+} button_event_config_t;
+
+/**
+ * @brief Supported button type
+ *
+ */
+typedef enum {
+    BUTTON_TYPE_GPIO,
+    BUTTON_TYPE_ADC,
+    BUTTON_TYPE_MATRIX,
+    BUTTON_TYPE_CUSTOM
+} button_type_t;
+
+/**
+ * @brief Button parameter
+ * 
+ */
+typedef enum {
+    BUTTON_LONG_PRESS_TIME_MS = 0,
+    BUTTON_SHORT_PRESS_TIME_MS,
+    BUTTON_PARAM_MAX,
+} button_param_t;
+
+/**
+ * @brief custom button configuration
+ * 
+ */
+typedef struct {
+    uint8_t active_level;                                   /**< active level when press down */
+    esp_err_t (*button_custom_init)(void *param);           /**< user defined button init */
+    uint8_t (*button_custom_get_key_value)(void *param);    /**< user defined button get key value */
+    esp_err_t (*button_custom_deinit)(void *param);         /**< user defined button deinit */
+    void *priv;                                             /**< private data used for custom button, MUST be allocated dynamically and will be auto freed in iot_button_delete*/
+} button_custom_config_t;
+
+/**
+ * @brief Button configuration
+ *
+ */
+typedef struct {
+    button_type_t type;                               /**< button type, The corresponding button configuration must be filled */
+    uint16_t long_press_time;                         /**< Trigger time(ms) for long press, if 0 default to BUTTON_LONG_PRESS_TIME_MS */
+    uint16_t short_press_time;                        /**< Trigger time(ms) for short press, if 0 default to BUTTON_SHORT_PRESS_TIME_MS */
+    union {
+        button_gpio_config_t gpio_button_config;      /**< gpio button configuration */
+        button_adc_config_t adc_button_config;        /**< adc button configuration */
+        button_matrix_config_t matrix_button_config; /**< matrix key button configuration */
+        button_custom_config_t custom_button_config;  /**< custom button configuration */
+    }; /**< button configuration */
+} button_config_t;
+
+/**
+ * @brief Create a button
+ *
+ * @param config pointer of button configuration, must corresponding the button type
+ *
+ * @return A handle to the created button, or NULL in case of error.
+ */
+button_handle_t iot_button_create(const button_config_t *config);
+
+/**
+ * @brief Delete a button
+ *
+ * @param btn_handle A button handle to delete
+ *
+ * @return
+ *      - ESP_OK  Success
+ *      - ESP_FAIL Failure
+ */
+esp_err_t iot_button_delete(button_handle_t btn_handle);
+
+/**
+ * @brief Register the button event callback function.
+ *
+ * @param btn_handle A button handle to register
+ * @param event Button event
+ * @param cb Callback function.
+ * @param usr_data user data
+ *
+ * @return
+ *      - ESP_OK on success
+ *      - ESP_ERR_INVALID_ARG   Arguments is invalid.
+ *      - ESP_ERR_INVALID_STATE The Callback is already registered. No free Space for another Callback.
+ *      - ESP_ERR_NO_MEM        No more memory allocation for the event
+ */
+esp_err_t iot_button_register_cb(button_handle_t btn_handle, button_event_t event, button_cb_t cb, void *usr_data);
+
+/**
+ * @brief Register the button event callback function.
+ *
+ * @param btn_handle A button handle to register
+ * @param event_cfg Button event configuration
+ * @param cb Callback function.
+ * @param usr_data user data
+ *
+ * @return
+ *      - ESP_OK on success
+ *      - ESP_ERR_INVALID_ARG   Arguments is invalid.
+ *      - ESP_ERR_INVALID_STATE The Callback is already registered. No free Space for another Callback.
+ *      - ESP_ERR_NO_MEM        No more memory allocation for the event
+ */
+esp_err_t iot_button_register_event_cb(button_handle_t btn_handle, button_event_config_t event_cfg, button_cb_t cb, void *usr_data);
+
+/**
+ * @brief Unregister the button event callback function.
+ *        In case event_data is also passed it will unregister function for that particular event_data only.
+ *
+ * @param btn_handle A button handle to unregister
+ * @param event_cfg Button event
+ * @param cb callback to unregister
+ *
+ * @return
+ *      - ESP_OK on success
+ *      - ESP_ERR_INVALID_ARG   Arguments is invalid.
+ *      - ESP_ERR_INVALID_STATE The Callback was never registered with the event
+ */
+esp_err_t iot_button_unregister_event(button_handle_t btn_handle, button_event_config_t event_cfg, button_cb_t cb);
+
+/**
+ * @brief Unregister all the callbacks associated with the event.
+ *
+ * @param btn_handle A button handle to unregister
+ * @param event Button event
+ *
+ * @return
+ *      - ESP_OK on success
+ *      - ESP_ERR_INVALID_ARG   Arguments is invalid.
+ *      - ESP_ERR_INVALID_STATE No callbacks registered for the event
+ */
+esp_err_t iot_button_unregister_cb(button_handle_t btn_handle, button_event_t event);
+
+/**
+ * @brief counts total callbacks registered
+ *
+ * @param btn_handle A button handle to the button
+ *
+ * @return
+ *      - 0 if no callbacks registered, or 1 .. (BUTTON_EVENT_MAX-1) for the number of Registered Buttons.
+ *      - ESP_ERR_INVALID_ARG if btn_handle is invalid
+ */
+size_t iot_button_count_cb(button_handle_t btn_handle);
+
+/**
+ * @brief how many callbacks are registered for the event
+ *
+ * @param btn_handle A button handle to the button
+ *
+ * @param event Button event
+ *
+ * @return
+ *      - 0 if no callbacks registered, or 1 .. (BUTTON_EVENT_MAX-1) for the number of Registered Buttons.
+ *      - ESP_ERR_INVALID_ARG if btn_handle is invalid
+ */
+size_t iot_button_count_event(button_handle_t btn_handle, button_event_t event);
+
+/**
+ * @brief Get button event
+ *
+ * @param btn_handle Button handle
+ *
+ * @return Current button event. See button_event_t
+ */
+button_event_t iot_button_get_event(button_handle_t btn_handle);
+
+/**
+ * @brief Get button repeat times
+ *
+ * @param btn_handle Button handle
+ *
+ * @return button pressed times. For example, double-click return 2, triple-click return 3, etc.
+ */
+uint8_t iot_button_get_repeat(button_handle_t btn_handle);
+
+/**
+ * @brief Get button ticks time
+ *
+ * @param btn_handle Button handle
+ *
+ * @return Actual time from press down to up (ms).
+ */
+uint16_t iot_button_get_ticks_time(button_handle_t btn_handle);
+
+/**
+ * @brief Get button long press hold count
+ *
+ * @param btn_handle Button handle
+ *
+ * @return Count of trigger cb(BUTTON_LONG_PRESS_HOLD)
+ */
+uint16_t iot_button_get_long_press_hold_cnt(button_handle_t btn_handle);
+
+/**
+ * @brief Dynamically change the parameters of the iot button
+ * 
+ * @param btn_handle Button handle
+ * @param param Button parameter
+ * @param value new value
+ * @return
+ *      - ESP_OK on success
+ *      - ESP_ERR_INVALID_ARG   Arguments is invalid.
+ */
+esp_err_t iot_button_set_param(button_handle_t btn_handle, button_param_t param, void *value);
+
+/**
+ * @brief resume button timer, if button timer is stopped. Make sure iot_button_create() is called before calling this API.
+ *
+ * @return
+ *     - ESP_OK on success
+ *     - ESP_ERR_INVALID_STATE   timer state is invalid.
+ */
+esp_err_t iot_button_resume(void);
+
+/**
+ * @brief stop button timer, if button timer is running. Make sure iot_button_create() is called before calling this API.
+ *
+ * @return
+ *     - ESP_OK on success
+ *     - ESP_ERR_INVALID_STATE   timer state is invalid
+ */
+esp_err_t iot_button_stop(void);
+
+#ifdef __cplusplus
+}
+#endif

+ 142 - 0
components/button/include/user_button.h

@@ -0,0 +1,142 @@
+#ifndef _USER_BUTTON_H_
+#define _USER_BUTTON_H_
+
+#include "esp_adc/adc_oneshot.h"
+#include "esp_adc/adc_cali.h"
+#include "esp_adc/adc_cali_scheme.h"
+
+
+#define KEY_NUM  6
+
+#if 0  //以前没改过硬件的adc采集值
+#define BAOYANG_MIN_ADC 0
+#define BAOYANG_MAX_ADC 200
+
+
+#define FENGCUN_MIN_ADC 300
+#define FENGCUN_MAX_ADC 700
+
+
+#define GUZHUANG_MIN_ADC 800
+#define GUZHANG_MAX_ADC 1200
+
+
+#define DAILIAO_MIN_ADC 1300
+#define DAILIAO_MAX_ADC 1700
+
+
+#define TINGJI_MIN_ADC  1800
+#define TINGJI_MAX_ADC  2200
+
+
+#define YUNXING_MIN_ADC 2300
+#define YUNXING_MAX_ADC 3000
+#else
+
+
+// #define BAOYANG_MIN_ADC 230 
+// #define BAOYANG_MAX_ADC 300 
+
+
+// #define FENGCUN_MIN_ADC 700//500
+// #define FENGCUN_MAX_ADC 1000//600
+
+
+// #define GUZHUANG_MIN_ADC 500
+// #define GUZHANG_MAX_ADC  600
+
+
+// #define DAILIAO_MIN_ADC 350
+// #define DAILIAO_MAX_ADC 450
+
+
+// #define TINGJI_MIN_ADC  0//500
+// #define TINGJI_MAX_ADC  20//600
+
+
+// #define YUNXING_MIN_ADC 100
+// #define YUNXING_MAX_ADC 220
+
+
+
+#define BAOYANG_MIN_ADC  0 
+#define BAOYANG_MAX_ADC  20  
+
+
+#define FENGCUN_MIN_ADC 501
+#define FENGCUN_MAX_ADC 700
+
+
+#define GUZHUANG_MIN_ADC 701       
+#define GUZHANG_MAX_ADC  1000 
+
+
+#define DAILIAO_MIN_ADC  351
+#define DAILIAO_MAX_ADC  500
+
+
+#define TINGJI_MIN_ADC   20 
+#define TINGJI_MAX_ADC   220  
+
+
+#define YUNXING_MIN_ADC  221   
+#define YUNXING_MAX_ADC  350
+
+
+#endif
+
+typedef enum
+{   
+    
+    BAOYANG_KEY = 0,
+    FENGCUN_KEY,
+    GUZHUANG_KEY,
+    DAILIAO_KEY ,
+    TINGJI_KEY ,
+    YUNXING_KEY,
+    POWER_KEY,
+
+}KEY_t;
+
+//按键值
+typedef enum
+{
+    POWER_LONG_START_VALUE      = 0xF0,  //长按开始
+    POWER_ON_PRESS_VALUE        = 0xF1,  //短按
+    POWER_OFF_PRESS_VALUE       = 0xF2,  //
+    POWER_ON_INTO_SETTING_VALUE = 0xF3,  //进入设置模式
+    POWER_ON_INTO_OTA_VALUE     = 0xF4,  //进入OTA模式
+    POWER_ON_INTO_RESET_VALUE   = 0xF5,  //进入复位模式
+    
+    POWER_ON_INTO_STATUS_CHANGE_VALUE = 0xF6,//切换开关机
+    POWER_ON_INTO_DIS_RIGHT      = 0xF7,
+    
+}KEY_VALUE_t;
+
+
+#define POWER_KEY_LONG_PRSSS_TIME    3000//电源键长按开关机时常
+
+
+
+#define POWER_KEY_PRSSS_SETTING_MODE 2   //进入设置模式
+#define POWER_KEY_PRSSS_COUNT_RESET  4   //电源键多次按键恢复出厂
+
+
+//extern uint16_t adc_value[][2];
+
+
+
+void button_init(adc_oneshot_unit_handle_t adc_handle);
+void button_deinit(void);
+
+
+void power_button_init(adc_oneshot_unit_handle_t adc_handle);
+void power_button_deinit(void);
+
+int  find_key_value(int value);
+
+
+void send_button_key_queue(uint8_t value);
+
+
+#endif/*_USER_BUTTON_H_*/

+ 709 - 0
components/button/iot_button.c

@@ -0,0 +1,709 @@
+/* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "freertos/timers.h"
+#include "esp_log.h"
+#include "driver/gpio.h"
+#include "iot_button.h"
+#include "esp_timer.h"
+#include "sdkconfig.h"
+
+static const char *TAG = "button";
+static portMUX_TYPE s_button_lock = portMUX_INITIALIZER_UNLOCKED;
+#define BUTTON_ENTER_CRITICAL()           portENTER_CRITICAL(&s_button_lock)
+#define BUTTON_EXIT_CRITICAL()            portEXIT_CRITICAL(&s_button_lock)
+
+#define BTN_CHECK(a, str, ret_val)                                \
+    if (!(a)) {                                                   \
+        ESP_LOGE(TAG, "%s(%d): %s", __FUNCTION__, __LINE__, str); \
+        return (ret_val);                                         \
+    }
+
+/**
+ * @brief Structs to store callback info
+ *
+ */
+typedef struct {
+    button_cb_t cb;
+    void *usr_data;
+    button_event_data_t event_data;
+} button_cb_info_t;
+
+/**
+ * @brief Structs to record individual key parameters
+ *
+ */
+typedef struct Button {
+    uint16_t            ticks;
+    uint16_t            long_press_ticks;     /*! Trigger ticks for long press*/
+    uint16_t            short_press_ticks;    /*! Trigger ticks for repeat press*/
+    uint16_t            long_press_hold_cnt;  /*! Record long press hold count*/
+    uint16_t            long_press_ticks_default;
+    uint8_t             repeat;
+    uint8_t             state: 3;
+    uint8_t             debounce_cnt: 3;
+    uint8_t             active_level: 1;
+    uint8_t             button_level: 1;
+    button_event_t      event;
+    uint8_t             (*hal_button_Level)(void *hardware_data);
+    esp_err_t           (*hal_button_deinit)(void *hardware_data);
+    void                *hardware_data;
+    button_type_t       type;
+    button_cb_info_t    *cb_info[BUTTON_EVENT_MAX];
+    size_t              size[BUTTON_EVENT_MAX];
+    int                 count[2];
+    struct Button       *next;
+} button_dev_t;
+
+//button handle list head.
+static button_dev_t *g_head_handle = NULL;
+static esp_timer_handle_t g_button_timer_handle = NULL;
+static bool g_is_timer_running = false;
+
+#define TICKS_INTERVAL    CONFIG_BUTTON_PERIOD_TIME_MS
+#define DEBOUNCE_TICKS    CONFIG_BUTTON_DEBOUNCE_TICKS //MAX 8
+#define SHORT_TICKS       (CONFIG_BUTTON_SHORT_PRESS_TIME_MS /TICKS_INTERVAL)
+#define LONG_TICKS        (CONFIG_BUTTON_LONG_PRESS_TIME_MS /TICKS_INTERVAL)
+#define SERIAL_TICKS      (CONFIG_BUTTON_SERIAL_TIME_MS /TICKS_INTERVAL)
+#define TOLERANCE         CONFIG_BUTTON_LONG_PRESS_TOLERANCE_MS
+
+#define CALL_EVENT_CB(ev)                                                   \
+    if (btn->cb_info[ev]) {                                                 \
+        for (int i = 0; i < btn->size[ev]; i++) {                           \
+            btn->cb_info[ev][i].cb(btn, btn->cb_info[ev][i].usr_data);      \
+        }                                                                   \
+    }                                                                       \
+
+#define TIME_TO_TICKS(time, congfig_time)  (0 == (time))?congfig_time:(((time) / TICKS_INTERVAL))?((time) / TICKS_INTERVAL):1
+
+/**
+  * @brief  Button driver core function, driver state machine.
+  */
+static void button_handler(button_dev_t *btn)
+{
+    uint8_t read_gpio_level = btn->hal_button_Level(btn->hardware_data);
+
+    /** ticks counter working.. */
+    if ((btn->state) > 0) {
+        btn->ticks++;
+    }
+
+    /**< button debounce handle */
+    if (read_gpio_level != btn->button_level) {
+        if (++(btn->debounce_cnt) >= DEBOUNCE_TICKS) {
+            btn->button_level = read_gpio_level;
+            btn->debounce_cnt = 0;
+        }
+    } else {
+        btn->debounce_cnt = 0;
+    }
+
+    /** State machine */
+    switch (btn->state) {
+    case 0:
+        if (btn->button_level == btn->active_level) {
+            btn->event = (uint8_t)BUTTON_PRESS_DOWN;
+            CALL_EVENT_CB(BUTTON_PRESS_DOWN);
+            btn->ticks = 0;
+            btn->repeat = 1;
+            btn->state = 1;
+        } else {
+            btn->event = (uint8_t)BUTTON_NONE_PRESS;
+        }
+        break;
+
+    case 1:
+        if (btn->button_level != btn->active_level) {
+            btn->event = (uint8_t)BUTTON_PRESS_UP;
+            CALL_EVENT_CB(BUTTON_PRESS_UP);
+            btn->ticks = 0;
+            btn->state = 2;
+
+        } else if (btn->ticks > btn->long_press_ticks) {
+            btn->event = (uint8_t)BUTTON_LONG_PRESS_START;
+            btn->state = 4;
+            /** Calling callbacks for BUTTON_LONG_PRESS_START */
+            uint16_t ticks_time = iot_button_get_ticks_time(btn);
+            if (btn->cb_info[btn->event] && btn->count[0] == 0) {
+                if (abs(ticks_time - (btn->long_press_ticks * TICKS_INTERVAL)) <= TOLERANCE && btn->cb_info[btn->event][btn->count[0]].event_data.long_press.press_time == (btn->long_press_ticks * TICKS_INTERVAL)) {
+                    do {
+                        btn->cb_info[btn->event][btn->count[0]].cb(btn, btn->cb_info[btn->event][btn->count[0]].usr_data);
+                        btn->count[0]++;
+                        if (btn->count[0] >= btn->size[btn->event])
+                            break;
+                    } while (btn->cb_info[btn->event][btn->count[0]].event_data.long_press.press_time == btn->long_press_ticks * TICKS_INTERVAL);
+                }
+            }
+        }
+        break;
+
+    case 2:
+        if (btn->button_level == btn->active_level) {
+            btn->event = (uint8_t)BUTTON_PRESS_DOWN;
+            CALL_EVENT_CB(BUTTON_PRESS_DOWN);
+            btn->event = (uint8_t)BUTTON_PRESS_REPEAT;
+            btn->repeat++;
+            CALL_EVENT_CB(BUTTON_PRESS_REPEAT); // repeat hit
+            btn->ticks = 0;
+            btn->state = 3;
+        } else if (btn->ticks > btn->short_press_ticks) {
+            if (btn->repeat == 1) {
+                btn->event = (uint8_t)BUTTON_SINGLE_CLICK;
+                CALL_EVENT_CB(BUTTON_SINGLE_CLICK);
+            } else if (btn->repeat == 2) {
+                btn->event = (uint8_t)BUTTON_DOUBLE_CLICK;
+                CALL_EVENT_CB(BUTTON_DOUBLE_CLICK); // repeat hit
+            }
+
+            btn->event = (uint8_t)BUTTON_MULTIPLE_CLICK;
+
+            /** Calling the callbacks for MULTIPLE BUTTON CLICKS */
+            for (int i = 0; i < btn->size[btn->event]; i++) {
+                if (btn->repeat == btn->cb_info[btn->event][i].event_data.multiple_clicks.clicks) {
+                    do {
+                        btn->cb_info[btn->event][i].cb(btn, btn->cb_info[btn->event][i].usr_data);
+                        i++;
+                        if (i >= btn->size[btn->event])
+                            break;
+                    } while (btn->cb_info[btn->event][i].event_data.multiple_clicks.clicks == btn->repeat);
+                }
+            }
+
+            btn->event = (uint8_t)BUTTON_PRESS_REPEAT_DONE;
+            CALL_EVENT_CB(BUTTON_PRESS_REPEAT_DONE); // repeat hit
+            btn->repeat = 0;
+            btn->state = 0;
+        }
+        break;
+
+    case 3:
+        if (btn->button_level != btn->active_level) {
+            btn->event = (uint8_t)BUTTON_PRESS_UP;
+            CALL_EVENT_CB(BUTTON_PRESS_UP);
+            if (btn->ticks < SHORT_TICKS) {
+                btn->ticks = 0;
+                btn->state = 2; //repeat press
+            } else {
+                btn->state = 0;
+            }
+        }
+        break;
+
+    case 4:
+        if (btn->button_level == btn->active_level) {
+            //continue hold trigger
+            if (btn->ticks >= (btn->long_press_hold_cnt + 1) * SERIAL_TICKS + btn->long_press_ticks) {
+                btn->event = (uint8_t)BUTTON_LONG_PRESS_HOLD;
+                btn->long_press_hold_cnt++;
+                CALL_EVENT_CB(BUTTON_LONG_PRESS_HOLD);
+
+                /** Calling callbacks for BUTTON_LONG_PRESS_START based on press_time */
+                uint16_t ticks_time = iot_button_get_ticks_time(btn);
+                if (btn->cb_info[BUTTON_LONG_PRESS_START]) {
+                    button_cb_info_t *cb_info = btn->cb_info[BUTTON_LONG_PRESS_START];
+                    uint16_t time = cb_info[btn->count[0]].event_data.long_press.press_time;
+                    if (btn->long_press_ticks * TICKS_INTERVAL > time) {
+                        for (int i = btn->count[0] + 1; i < btn->size[BUTTON_LONG_PRESS_START]; i++) {
+                            time = cb_info[i].event_data.long_press.press_time;
+                            if (btn->long_press_ticks * TICKS_INTERVAL <= time) {
+                                btn->count[0] = i;
+                                break;
+                            }
+                        }
+                    }
+                    if (btn->count[0] < btn->size[BUTTON_LONG_PRESS_START] && abs(ticks_time - time) <= TOLERANCE) {
+                        do {
+                            cb_info[btn->count[0]].cb(btn, cb_info[btn->count[0]].usr_data);
+                            btn->count[0]++;
+                            if (btn->count[0] >= btn->size[BUTTON_LONG_PRESS_START])
+                                break;
+                        } while (time == cb_info[btn->count[0]].event_data.long_press.press_time);
+                    }
+                }
+
+                /** Updating counter for BUTTON_LONG_PRESS_UP press_time */
+                if (btn->cb_info[BUTTON_LONG_PRESS_UP]) {
+                    button_cb_info_t *cb_info = btn->cb_info[BUTTON_LONG_PRESS_UP];
+                    uint16_t time = cb_info[btn->count[1] + 1].event_data.long_press.press_time;
+                    if (btn->long_press_ticks * TICKS_INTERVAL > time) {
+                        for (int i = btn->count[1] + 1; i < btn->size[BUTTON_LONG_PRESS_UP]; i++) {
+                            time = cb_info[i].event_data.long_press.press_time;
+                            if (btn->long_press_ticks * TICKS_INTERVAL <= time) {
+                                btn->count[1] = i;
+                                break;
+                            }
+                        }
+                    }
+                    if(btn->count[1] + 1 < btn->size[BUTTON_LONG_PRESS_UP] && abs(ticks_time - time) <= TOLERANCE) {
+                        do {
+                            btn->count[1]++;
+                            if (btn->count[1] + 1 >= btn->size[BUTTON_LONG_PRESS_UP])
+                                break;
+                        } while (time == cb_info[btn->count[1] + 1].event_data.long_press.press_time);
+                    }
+                }
+            }
+        } else { //releasd
+
+            btn->event = BUTTON_LONG_PRESS_UP;
+
+            /** calling callbacks for BUTTON_LONG_PRESS_UP press_time */
+            if (btn->cb_info[btn->event] && btn->count[1] >= 0) {
+                button_cb_info_t *cb_info = btn->cb_info[btn->event];
+                do {
+                    cb_info[btn->count[1]].cb(btn, cb_info[btn->count[1]].usr_data);
+                    if (!btn->count[1])
+                        break;
+                    btn->count[1]--;
+                } while (cb_info[btn->count[1]].event_data.long_press.press_time == cb_info[btn->count[1] + 1].event_data.long_press.press_time);
+
+                /** Reset the counter */
+                btn->count[1] = -1;
+            }
+            /** Reset counter */
+            if (btn->cb_info[BUTTON_LONG_PRESS_START]) {
+                btn->count[0] = 0;
+            }
+
+            btn->event = (uint8_t)BUTTON_PRESS_UP;
+            CALL_EVENT_CB(BUTTON_PRESS_UP);
+            btn->state = 0; //reset
+            btn->long_press_hold_cnt = 0;
+        }
+        break;
+    }
+}
+
+static void button_cb(void *args)
+{
+    button_dev_t *target;
+    for (target = g_head_handle; target; target = target->next) {
+        button_handler(target);
+    }
+}
+
+static button_dev_t *button_create_com(uint8_t active_level, uint8_t (*hal_get_key_state)(void *hardware_data), void *hardware_data, uint16_t long_press_ticks, uint16_t short_press_ticks)
+{
+    BTN_CHECK(NULL != hal_get_key_state, "Function pointer is invalid", NULL);
+
+    button_dev_t *btn = (button_dev_t *) calloc(1, sizeof(button_dev_t));
+    BTN_CHECK(NULL != btn, "Button memory alloc failed", NULL);
+    btn->hardware_data = hardware_data;
+    btn->event = BUTTON_NONE_PRESS;
+    btn->active_level = active_level;
+    btn->hal_button_Level = hal_get_key_state;
+    btn->button_level = !active_level;
+    btn->long_press_ticks = long_press_ticks;
+    btn->long_press_ticks_default = btn->long_press_ticks;
+    btn->short_press_ticks = short_press_ticks;
+
+    /** Add handle to list */
+    btn->next = g_head_handle;
+    g_head_handle = btn;
+
+    if (false == g_is_timer_running) {
+        esp_timer_create_args_t button_timer;
+        button_timer.arg = NULL;
+        button_timer.callback = button_cb;
+        button_timer.dispatch_method = ESP_TIMER_TASK;
+        button_timer.name = "button_timer";
+        esp_timer_create(&button_timer, &g_button_timer_handle);
+        esp_timer_start_periodic(g_button_timer_handle, TICKS_INTERVAL * 1000U);
+        g_is_timer_running = true;
+    }
+
+    return btn;
+}
+
+static esp_err_t button_delete_com(button_dev_t *btn)
+{
+    BTN_CHECK(NULL != btn, "Pointer of handle is invalid", ESP_ERR_INVALID_ARG);
+
+    button_dev_t **curr;
+    for (curr = &g_head_handle; *curr; ) {
+        button_dev_t *entry = *curr;
+        if (entry == btn) {
+            *curr = entry->next;
+            free(entry);
+        } else {
+            curr = &entry->next;
+        }
+    }
+
+    /* count button number */
+    uint16_t number = 0;
+    button_dev_t *target = g_head_handle;
+    while (target) {
+        target = target->next;
+        number++;
+    }
+    ESP_LOGD(TAG, "remain btn number=%d", number);
+
+    if (0 == number && g_is_timer_running) { /**<  if all button is deleted, stop the timer */
+        esp_timer_stop(g_button_timer_handle);
+        esp_timer_delete(g_button_timer_handle);
+        g_is_timer_running = false;
+    }
+    return ESP_OK;
+}
+
+button_handle_t iot_button_create(const button_config_t *config)
+{
+    //ESP_LOGI(TAG, "IoT Button Version: %d.%d.%d", BUTTON_VER_MAJOR, BUTTON_VER_MINOR, BUTTON_VER_PATCH);
+    BTN_CHECK(config, "Invalid button config", NULL);
+
+    esp_err_t ret = ESP_OK;
+    button_dev_t *btn = NULL;
+    uint16_t long_press_time = 0;
+    uint16_t short_press_time = 0;
+    long_press_time = TIME_TO_TICKS(config->long_press_time, LONG_TICKS);
+    short_press_time = TIME_TO_TICKS(config->short_press_time, SHORT_TICKS);
+    switch (config->type) {
+    case BUTTON_TYPE_GPIO: {
+        const button_gpio_config_t *cfg = &(config->gpio_button_config);
+        ret = button_gpio_init(cfg);
+        BTN_CHECK(ESP_OK == ret, "gpio button init failed", NULL);
+        btn = button_create_com(cfg->active_level, button_gpio_get_key_level, (void *)cfg->gpio_num, long_press_time, short_press_time);
+    } break;
+    case BUTTON_TYPE_ADC: {
+        const button_adc_config_t *cfg = &(config->adc_button_config);
+        ret = button_adc_init(cfg);
+        if (ret != ESP_OK)
+        {
+           printf("adc button init failed\r\n");
+        }
+        
+        BTN_CHECK(ESP_OK == ret, "adc button init failed", NULL);
+        btn = button_create_com(1, button_adc_get_key_level, (void *)ADC_BUTTON_COMBINE(cfg->adc_channel, cfg->button_index), long_press_time, short_press_time);
+    } break;
+    case BUTTON_TYPE_MATRIX: {
+        const button_matrix_config_t *cfg = &(config->matrix_button_config);
+        ret = button_matrix_init(cfg);
+        BTN_CHECK(ESP_OK == ret, "matrix button init failed", NULL);
+        btn = button_create_com(1, button_matrix_get_key_level, (void *)MATRIX_BUTTON_COMBINE(cfg->row_gpio_num, cfg->col_gpio_num), long_press_time, short_press_time);
+    } break;
+    case BUTTON_TYPE_CUSTOM: {
+        if (config->custom_button_config.button_custom_init) {
+            ret = config->custom_button_config.button_custom_init(config->custom_button_config.priv);
+            BTN_CHECK(ESP_OK == ret, "custom button init failed", NULL);
+        }
+
+        btn = button_create_com(config->custom_button_config.active_level,
+                                config->custom_button_config.button_custom_get_key_value,
+                                config->custom_button_config.priv,
+                                long_press_time, short_press_time);
+        if (btn) {
+            btn->hal_button_deinit = config->custom_button_config.button_custom_deinit;
+        }
+    } break;
+
+    default:
+        ESP_LOGE(TAG, "Unsupported button type");
+        break;
+    }
+    BTN_CHECK(NULL != btn, "button create failed", NULL);
+    btn->type = config->type;
+    return (button_handle_t)btn;
+}
+
+esp_err_t iot_button_delete(button_handle_t btn_handle)
+{
+    esp_err_t ret = ESP_OK;
+    BTN_CHECK(NULL != btn_handle, "Pointer of handle is invalid", ESP_ERR_INVALID_ARG);
+    button_dev_t *btn = (button_dev_t *)btn_handle;
+    switch (btn->type) {
+    case BUTTON_TYPE_GPIO:
+        ret = button_gpio_deinit((int)(btn->hardware_data));
+        break;
+    case BUTTON_TYPE_ADC:
+        ret = button_adc_deinit(ADC_BUTTON_SPLIT_CHANNEL(btn->hardware_data), ADC_BUTTON_SPLIT_INDEX(btn->hardware_data));
+        break;
+    case BUTTON_TYPE_MATRIX:
+        ret = button_matrix_deinit(MATRIX_BUTTON_SPLIT_ROW(btn->hardware_data), MATRIX_BUTTON_SPLIT_COL(btn->hardware_data));
+        break;
+    case BUTTON_TYPE_CUSTOM:
+        if (btn->hal_button_deinit) {
+            ret = btn->hal_button_deinit(btn->hardware_data);
+        }
+
+        break;
+    default:
+        break;
+    }
+    BTN_CHECK(ESP_OK == ret, "button deinit failed", ESP_FAIL);
+    for (int i = 0; i < BUTTON_EVENT_MAX; i++) {
+        if (btn->cb_info[i]) {
+            free(btn->cb_info[i]);
+        }
+    }
+    button_delete_com(btn);
+    return ESP_OK;
+}
+
+esp_err_t iot_button_register_cb(button_handle_t btn_handle, button_event_t event, button_cb_t cb, void *usr_data)
+{
+    BTN_CHECK(NULL != btn_handle, "Pointer of handle is invalid", ESP_ERR_INVALID_ARG);
+    button_dev_t *btn = (button_dev_t *) btn_handle;
+    BTN_CHECK(event != BUTTON_MULTIPLE_CLICK, "event argument is invalid", ESP_ERR_INVALID_ARG);
+    button_event_config_t event_cfg = {
+        .event = event,
+    };
+
+    if ((event == BUTTON_LONG_PRESS_START || event == BUTTON_LONG_PRESS_UP) && !event_cfg.event_data.long_press.press_time) {
+        event_cfg.event_data.long_press.press_time = btn->long_press_ticks_default * TICKS_INTERVAL;
+    }
+
+    return iot_button_register_event_cb(btn_handle, event_cfg, cb, usr_data);
+}
+
+esp_err_t iot_button_register_event_cb(button_handle_t btn_handle, button_event_config_t event_cfg, button_cb_t cb, void *usr_data)
+{
+    BTN_CHECK(NULL != btn_handle, "Pointer of handle is invalid", ESP_ERR_INVALID_ARG);
+    button_dev_t *btn = (button_dev_t *) btn_handle;
+    button_event_t event = event_cfg.event;
+    BTN_CHECK(event < BUTTON_EVENT_MAX, "event is invalid", ESP_ERR_INVALID_ARG);
+    BTN_CHECK(!(event == BUTTON_LONG_PRESS_START || event == BUTTON_LONG_PRESS_UP) || event_cfg.event_data.long_press.press_time > btn->short_press_ticks * TICKS_INTERVAL, "event_data is invalid", ESP_ERR_INVALID_ARG);
+    BTN_CHECK(event != BUTTON_MULTIPLE_CLICK || event_cfg.event_data.multiple_clicks.clicks, "event_data is invalid", ESP_ERR_INVALID_ARG);
+
+    if (!btn->cb_info[event]) {
+        btn->cb_info[event] = calloc(1, sizeof(button_cb_info_t));
+        BTN_CHECK(NULL != btn->cb_info[event], "calloc cb_info failed", ESP_ERR_NO_MEM);
+        if (event == BUTTON_LONG_PRESS_START) {
+            btn->count[0] = 0;
+        } else if (event == BUTTON_LONG_PRESS_UP) {
+            btn->count[1] = -1;
+        }
+    }
+    else {
+        button_cb_info_t *p = realloc(btn->cb_info[event], sizeof(button_cb_info_t) * (btn->size[event] + 1));
+        BTN_CHECK(NULL != p, "realloc cb_info failed", ESP_ERR_NO_MEM);
+        btn->cb_info[event] = p;
+    }
+
+    btn->cb_info[event][btn->size[event]].cb = cb;
+    btn->cb_info[event][btn->size[event]].usr_data = usr_data;
+    btn->size[event]++;
+
+    /** Inserting the event_data in sorted manner */
+    if (event == BUTTON_LONG_PRESS_START || event == BUTTON_LONG_PRESS_UP) {
+        uint16_t press_time = event_cfg.event_data.long_press.press_time;
+        BTN_CHECK(press_time / TICKS_INTERVAL > btn->short_press_ticks, "press_time event_data is less than short_press_ticks", ESP_ERR_INVALID_ARG);
+        if (btn->size[event] >= 2) {
+            for (int i = btn->size[event] - 2; i >= 0; i--) {
+                if (btn->cb_info[event][i].event_data.long_press.press_time > press_time) {
+                    btn->cb_info[event][i + 1] = btn->cb_info[event][i];
+
+                    btn->cb_info[event][i].event_data.long_press.press_time = press_time;
+                    btn->cb_info[event][i].cb = cb;
+                    btn->cb_info[event][i].usr_data = usr_data;
+                } else {
+                    btn->cb_info[event][i + 1].event_data.long_press.press_time = press_time;
+                    btn->cb_info[event][i + 1].cb = cb;
+                    btn->cb_info[event][i + 1].usr_data = usr_data;
+                    break;
+                }
+            }
+        } else {
+            btn->cb_info[event][btn->size[event] - 1].event_data.long_press.press_time = press_time;
+        }
+
+        int32_t press_ticks = press_time / TICKS_INTERVAL;
+        if (btn->short_press_ticks < press_ticks && press_ticks < btn->long_press_ticks) {
+            iot_button_set_param(btn, BUTTON_LONG_PRESS_TIME_MS, (void*)(intptr_t)press_time);
+        }
+    }
+
+    if (event == BUTTON_MULTIPLE_CLICK) {
+        if (btn->size[event] >= 2) {
+            for (int i = btn->size[event] - 2; i >= 0; i--) {
+                if (btn->cb_info[event][i].event_data.multiple_clicks.clicks > event_cfg.event_data.multiple_clicks.clicks) {
+                    btn->cb_info[event][i + 1] = btn->cb_info[event][i];
+
+                    btn->cb_info[event][i].event_data.multiple_clicks.clicks = event_cfg.event_data.multiple_clicks.clicks;
+                    btn->cb_info[event][i].cb = cb;
+                    btn->cb_info[event][i].usr_data = usr_data;
+                } else {
+                    btn->cb_info[event][i + 1].event_data.multiple_clicks.clicks = event_cfg.event_data.multiple_clicks.clicks;
+                    btn->cb_info[event][i + 1].cb = cb;
+                    btn->cb_info[event][i + 1].usr_data = usr_data;
+                    break;
+                }
+            }
+        } else {
+            btn->cb_info[event][btn->size[event] - 1].event_data.multiple_clicks.clicks = event_cfg.event_data.multiple_clicks.clicks;
+        }
+    }
+
+    return ESP_OK;
+}
+
+esp_err_t iot_button_unregister_cb(button_handle_t btn_handle, button_event_t event)
+{
+    BTN_CHECK(NULL != btn_handle, "Pointer of handle is invalid", ESP_ERR_INVALID_ARG);
+    BTN_CHECK(event < BUTTON_EVENT_MAX, "event is invalid", ESP_ERR_INVALID_ARG);
+    button_dev_t *btn = (button_dev_t *) btn_handle;
+    BTN_CHECK(NULL != btn->cb_info[event], "No callbacks registered for the event", ESP_ERR_INVALID_STATE);
+
+    if (btn->cb_info[event]) {
+        free(btn->cb_info[event]);
+
+        /** Reset the counter */
+        if (event == BUTTON_LONG_PRESS_START) {
+            btn->count[0] = 0;
+        } else if (event == BUTTON_LONG_PRESS_UP) {
+            btn->count[1] = -1;
+        }
+
+    }
+
+    btn->cb_info[event] = NULL;
+    btn->size[event] = 0;
+    return ESP_OK;
+}
+
+esp_err_t iot_button_unregister_event(button_handle_t btn_handle, button_event_config_t event_cfg, button_cb_t cb)
+{
+    BTN_CHECK(NULL != btn_handle, "Pointer of handle is invalid", ESP_ERR_INVALID_ARG);
+    button_event_t event = event_cfg.event;
+    BTN_CHECK(event < BUTTON_EVENT_MAX, "event is invalid", ESP_ERR_INVALID_ARG);
+    BTN_CHECK(NULL != cb, "Pointer to function callback is invalid", ESP_ERR_INVALID_ARG);
+    button_dev_t *btn = (button_dev_t *) btn_handle;
+
+    int check = -1;
+
+    for (int i = 0; i < btn->size[event]; i++) {
+        if (cb == btn->cb_info[event][i].cb) {
+            if ((event == BUTTON_LONG_PRESS_START || event == BUTTON_LONG_PRESS_UP) && event_cfg.event_data.long_press.press_time) {
+                if (event_cfg.event_data.long_press.press_time != btn->cb_info[event][i].event_data.long_press.press_time) {
+                    continue;
+                }
+            }
+
+            if (event == BUTTON_MULTIPLE_CLICK && event_cfg.event_data.multiple_clicks.clicks) {
+                if (event_cfg.event_data.multiple_clicks.clicks != btn->cb_info[event][i].event_data.multiple_clicks.clicks) {
+                    continue;
+                }
+            }
+            check = i;
+            for (int j = i; j <= btn->size[event]-1; j++) {
+                btn->cb_info[event][j] = btn->cb_info[event][j + 1];
+            }
+
+            if (btn->size[event] != 1) {
+                button_cb_info_t *p = realloc(btn->cb_info[event], sizeof(button_cb_info_t) * (btn->size[event] - 1));
+                BTN_CHECK(NULL != p, "realloc cb_info failed", ESP_ERR_NO_MEM);
+                btn->cb_info[event] = p;
+                btn->size[event]--;
+            } else {
+                free(btn->cb_info[event]);
+                btn->cb_info[event] = NULL;
+                btn->size[event] = 0;
+            }
+            break;
+        }
+    }
+
+    BTN_CHECK(check != -1, "No such callback registered for the event", ESP_ERR_INVALID_STATE);
+
+    return ESP_OK;
+}
+
+size_t iot_button_count_cb(button_handle_t btn_handle)
+{
+    BTN_CHECK(NULL != btn_handle, "Pointer of handle is invalid", ESP_ERR_INVALID_ARG);
+    button_dev_t *btn = (button_dev_t *) btn_handle;
+    size_t ret = 0;
+    for (size_t i = 0; i < BUTTON_EVENT_MAX; i++) {
+        if (btn->cb_info[i]) {
+            ret+=btn->size[i];
+        }
+    }
+    return ret;
+}
+
+size_t iot_button_count_event(button_handle_t btn_handle, button_event_t event)
+{
+    BTN_CHECK(NULL != btn_handle, "Pointer of handle is invalid", ESP_ERR_INVALID_ARG);
+    button_dev_t *btn = (button_dev_t *) btn_handle;
+    return btn->size[event];
+}
+
+button_event_t iot_button_get_event(button_handle_t btn_handle)
+{
+    BTN_CHECK(NULL != btn_handle, "Pointer of handle is invalid", BUTTON_NONE_PRESS);
+    button_dev_t *btn = (button_dev_t *) btn_handle;
+    return btn->event;
+}
+
+uint8_t iot_button_get_repeat(button_handle_t btn_handle)
+{
+    BTN_CHECK(NULL != btn_handle, "Pointer of handle is invalid", 0);
+    button_dev_t *btn = (button_dev_t *) btn_handle;
+    return btn->repeat;
+}
+
+uint16_t iot_button_get_ticks_time(button_handle_t btn_handle)
+{
+    BTN_CHECK(NULL != btn_handle, "Pointer of handle is invalid", 0);
+    button_dev_t *btn = (button_dev_t *) btn_handle;
+    return (btn->ticks * TICKS_INTERVAL);
+}
+
+uint16_t iot_button_get_long_press_hold_cnt(button_handle_t btn_handle)
+{
+    BTN_CHECK(NULL != btn_handle, "Pointer of handle is invalid", 0);
+    button_dev_t *btn = (button_dev_t *) btn_handle;
+    return btn->long_press_hold_cnt;
+}
+
+esp_err_t iot_button_set_param(button_handle_t btn_handle, button_param_t param, void *value)
+{
+    BTN_CHECK(NULL != btn_handle, "Pointer of handle is invalid", ESP_ERR_INVALID_ARG);
+    button_dev_t *btn = (button_dev_t *) btn_handle;
+    BUTTON_ENTER_CRITICAL();
+    switch (param) {
+    case BUTTON_LONG_PRESS_TIME_MS:
+        btn->long_press_ticks = (int32_t)value / TICKS_INTERVAL;
+        break;
+    case BUTTON_SHORT_PRESS_TIME_MS:
+        btn->short_press_ticks = (int32_t)value / TICKS_INTERVAL;
+        break;
+    default:
+        break;
+    }
+    BUTTON_EXIT_CRITICAL();
+    return ESP_OK;
+}
+
+esp_err_t iot_button_resume(void)
+{
+    BTN_CHECK(g_button_timer_handle, "Button timer handle is invalid", ESP_ERR_INVALID_STATE);
+    if(g_is_timer_running){
+        return ESP_ERR_INVALID_STATE;
+    }
+
+    BTN_CHECK(!g_is_timer_running, "Button timer is already running", ESP_ERR_INVALID_STATE);
+
+    esp_err_t err = esp_timer_start_periodic(g_button_timer_handle, TICKS_INTERVAL * 1000U);
+    BTN_CHECK(ESP_OK == err, "Button timer start failed", ESP_FAIL);
+    g_is_timer_running = true;
+    return ESP_OK;
+}
+
+esp_err_t iot_button_stop(void)
+{
+    BTN_CHECK(g_button_timer_handle, "Button timer handle is invalid", ESP_ERR_INVALID_STATE);
+    BTN_CHECK(g_is_timer_running, "Button timer is not running", ESP_ERR_INVALID_STATE);
+
+    esp_err_t err = esp_timer_stop(g_button_timer_handle);
+    BTN_CHECK(ESP_OK == err, "Button timer stop failed", ESP_FAIL);
+    g_is_timer_running = false;
+    return ESP_OK;
+}

+ 202 - 0
components/button/license.txt

@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.

+ 9 - 0
components/button/test_apps/CMakeLists.txt

@@ -0,0 +1,9 @@
+
+# The following lines of boilerplate have to be in your project's CMakeLists
+# in this exact order for cmake to work correctly
+cmake_minimum_required(VERSION 3.5)
+
+set(EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/unit-test-app/components"
+                         "../../button")
+include($ENV{IDF_PATH}/tools/cmake/project.cmake)
+project(button_test)

+ 9 - 0
components/button/test_apps/main/CMakeLists.txt

@@ -0,0 +1,9 @@
+if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER_EQUAL "5.0")
+    list(APPEND PRIVREQ esp_adc)
+else() 
+    list(APPEND PRIVREQ esp_adc_cal)
+endif()
+
+idf_component_register(SRC_DIRS "."
+                       PRIV_INCLUDE_DIRS "."
+                       PRIV_REQUIRES unity test_utils button ${PRIVREQ})

+ 704 - 0
components/button/test_apps/main/button_test.c

@@ -0,0 +1,704 @@
+/* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "stdio.h"
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "freertos/queue.h"
+#include "freertos/timers.h"
+#include "freertos/semphr.h"
+#include "freertos/event_groups.h"
+#include "esp_idf_version.h"
+#include "esp_log.h"
+#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
+#include "esp_adc/adc_cali.h"
+#endif
+#include "unity.h"
+#include "iot_button.h"
+#include "sdkconfig.h"
+
+static const char *TAG = "BUTTON TEST";
+
+#define TEST_MEMORY_LEAK_THRESHOLD (-400)
+#define BUTTON_IO_NUM  0
+#define BUTTON_ACTIVE_LEVEL   0
+#define BUTTON_NUM 16
+
+#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
+#define ADC_BUTTON_WIDTH SOC_ADC_RTC_MAX_BITWIDTH
+#else
+#define ADC_BUTTON_WIDTH ADC_WIDTH_MAX - 1
+#endif
+
+static size_t before_free_8bit;
+static size_t before_free_32bit;
+static button_handle_t g_btns[BUTTON_NUM] = {0};
+
+static int get_btn_index(button_handle_t btn)
+{
+    for (size_t i = 0; i < BUTTON_NUM; i++) {
+        if (btn == g_btns[i]) {
+            return i;
+        }
+    }
+    return -1;
+}
+
+static void button_press_down_cb(void *arg, void *data)
+{
+    TEST_ASSERT_EQUAL_HEX(BUTTON_PRESS_DOWN, iot_button_get_event(arg));
+    ESP_LOGI(TAG, "BTN%d: BUTTON_PRESS_DOWN", get_btn_index((button_handle_t)arg));
+}
+
+static void button_press_up_cb(void *arg, void *data)
+{
+    TEST_ASSERT_EQUAL_HEX(BUTTON_PRESS_UP, iot_button_get_event(arg));
+    ESP_LOGI(TAG, "BTN%d: BUTTON_PRESS_UP[%d]", get_btn_index((button_handle_t)arg), iot_button_get_ticks_time((button_handle_t)arg));
+}
+
+static void button_press_repeat_cb(void *arg, void *data)
+{
+    ESP_LOGI(TAG, "BTN%d: BUTTON_PRESS_REPEAT[%d]", get_btn_index((button_handle_t)arg), iot_button_get_repeat((button_handle_t)arg));
+}
+
+static void button_single_click_cb(void *arg, void *data)
+{
+    TEST_ASSERT_EQUAL_HEX(BUTTON_SINGLE_CLICK, iot_button_get_event(arg));
+    ESP_LOGI(TAG, "BTN%d: BUTTON_SINGLE_CLICK", get_btn_index((button_handle_t)arg));
+}
+
+static void button_double_click_cb(void *arg, void *data)
+{
+    TEST_ASSERT_EQUAL_HEX(BUTTON_DOUBLE_CLICK, iot_button_get_event(arg));
+    ESP_LOGI(TAG, "BTN%d: BUTTON_DOUBLE_CLICK", get_btn_index((button_handle_t)arg));
+}
+
+static void button_long_press_start_cb(void *arg, void *data)
+{
+    TEST_ASSERT_EQUAL_HEX(BUTTON_LONG_PRESS_START, iot_button_get_event(arg));
+    ESP_LOGI(TAG, "BTN%d: BUTTON_LONG_PRESS_START", get_btn_index((button_handle_t)arg));
+}
+
+static void button_long_press_hold_cb(void *arg, void *data)
+{
+    TEST_ASSERT_EQUAL_HEX(BUTTON_LONG_PRESS_HOLD, iot_button_get_event(arg));
+    ESP_LOGI(TAG, "BTN%d: BUTTON_LONG_PRESS_HOLD[%d],count is [%d]", get_btn_index((button_handle_t)arg), iot_button_get_ticks_time((button_handle_t)arg), iot_button_get_long_press_hold_cnt((button_handle_t)arg));
+}
+
+static void button_press_repeat_done_cb(void *arg, void *data)
+{
+    TEST_ASSERT_EQUAL_HEX(BUTTON_PRESS_REPEAT_DONE, iot_button_get_event(arg));
+    ESP_LOGI(TAG, "BTN%d: BUTTON_PRESS_REPEAT_DONE[%d]", get_btn_index((button_handle_t)arg), iot_button_get_repeat((button_handle_t)arg));
+}
+
+static esp_err_t custom_button_gpio_init(void *param)
+{
+    button_gpio_config_t *cfg = (button_gpio_config_t *)param;
+
+    return button_gpio_init(cfg);
+}
+
+static uint8_t custom_button_gpio_get_key_value(void *param)
+{
+    button_gpio_config_t *cfg = (button_gpio_config_t *)param;
+
+    return button_gpio_get_key_level((void *)cfg->gpio_num);
+}
+
+static esp_err_t custom_button_gpio_deinit(void *param)
+{
+    button_gpio_config_t *cfg = (button_gpio_config_t *)param;
+
+    return button_gpio_deinit(cfg->gpio_num);
+}
+
+TEST_CASE("custom button test", "[button][iot]")
+{
+    button_gpio_config_t *gpio_cfg = calloc(1, sizeof(button_gpio_config_t));
+    gpio_cfg->active_level = 0;
+    gpio_cfg->gpio_num = 0;
+
+    button_config_t cfg = {
+        .type = BUTTON_TYPE_CUSTOM,
+        .long_press_time = CONFIG_BUTTON_LONG_PRESS_TIME_MS,
+        .short_press_time = CONFIG_BUTTON_SHORT_PRESS_TIME_MS,
+        .custom_button_config = {
+            .button_custom_init = custom_button_gpio_init,
+            .button_custom_deinit = custom_button_gpio_deinit,
+            .button_custom_get_key_value = custom_button_gpio_get_key_value,
+            .active_level = 0,
+            .priv = gpio_cfg,
+        },
+    };
+
+    g_btns[0] = iot_button_create(&cfg);
+    TEST_ASSERT_NOT_NULL(g_btns[0]);
+    iot_button_register_cb(g_btns[0], BUTTON_PRESS_DOWN, button_press_down_cb, NULL);
+    iot_button_register_cb(g_btns[0], BUTTON_PRESS_UP, button_press_up_cb, NULL);
+    iot_button_register_cb(g_btns[0], BUTTON_PRESS_REPEAT, button_press_repeat_cb, NULL);
+    iot_button_register_cb(g_btns[0], BUTTON_SINGLE_CLICK, button_single_click_cb, NULL);
+    iot_button_register_cb(g_btns[0], BUTTON_DOUBLE_CLICK, button_double_click_cb, NULL);
+    iot_button_register_cb(g_btns[0], BUTTON_LONG_PRESS_START, button_long_press_start_cb, NULL);
+    iot_button_register_cb(g_btns[0], BUTTON_LONG_PRESS_HOLD, button_long_press_hold_cb, NULL);
+    iot_button_register_cb(g_btns[0], BUTTON_PRESS_REPEAT_DONE, button_press_repeat_done_cb, NULL);
+
+    while (1) {
+        vTaskDelay(pdMS_TO_TICKS(1000));
+    }
+
+    iot_button_delete(g_btns[0]);
+}
+
+TEST_CASE("gpio button test", "[button][iot]")
+{
+    button_config_t cfg = {
+        .type = BUTTON_TYPE_GPIO,
+        .long_press_time = CONFIG_BUTTON_LONG_PRESS_TIME_MS,
+        .short_press_time = CONFIG_BUTTON_SHORT_PRESS_TIME_MS,
+        .gpio_button_config = {
+            .gpio_num = 0,
+            .active_level = 0,
+        },
+    };
+    g_btns[0] = iot_button_create(&cfg);
+    TEST_ASSERT_NOT_NULL(g_btns[0]);
+    iot_button_register_cb(g_btns[0], BUTTON_PRESS_DOWN, button_press_down_cb, NULL);
+    iot_button_register_cb(g_btns[0], BUTTON_PRESS_UP, button_press_up_cb, NULL);
+    iot_button_register_cb(g_btns[0], BUTTON_PRESS_REPEAT, button_press_repeat_cb, NULL);
+    iot_button_register_cb(g_btns[0], BUTTON_SINGLE_CLICK, button_single_click_cb, NULL);
+    iot_button_register_cb(g_btns[0], BUTTON_DOUBLE_CLICK, button_double_click_cb, NULL);
+    iot_button_register_cb(g_btns[0], BUTTON_LONG_PRESS_START, button_long_press_start_cb, NULL);
+    iot_button_register_cb(g_btns[0], BUTTON_LONG_PRESS_HOLD, button_long_press_hold_cb, NULL);
+    iot_button_register_cb(g_btns[0], BUTTON_PRESS_REPEAT_DONE, button_press_repeat_done_cb, NULL);
+
+    while (1) {
+        vTaskDelay(pdMS_TO_TICKS(1000));
+    }
+
+    iot_button_delete(g_btns[0]);
+}
+
+TEST_CASE("gpio button test power save", "[button][iot][power save]")
+{
+    button_config_t cfg = {
+        .type = BUTTON_TYPE_GPIO,
+        .long_press_time = CONFIG_BUTTON_LONG_PRESS_TIME_MS,
+        .short_press_time = CONFIG_BUTTON_SHORT_PRESS_TIME_MS,
+        .gpio_button_config = {
+            .gpio_num = 0,
+            .active_level = 0,
+        },
+    };
+    g_btns[0] = iot_button_create(&cfg);
+    TEST_ASSERT_NOT_NULL(g_btns[0]);
+    iot_button_register_cb(g_btns[0], BUTTON_PRESS_DOWN, button_press_down_cb, NULL);
+    iot_button_register_cb(g_btns[0], BUTTON_PRESS_UP, button_press_up_cb, NULL);
+    iot_button_register_cb(g_btns[0], BUTTON_PRESS_REPEAT, button_press_repeat_cb, NULL);
+    iot_button_register_cb(g_btns[0], BUTTON_SINGLE_CLICK, button_single_click_cb, NULL);
+    iot_button_register_cb(g_btns[0], BUTTON_DOUBLE_CLICK, button_double_click_cb, NULL);
+    iot_button_register_cb(g_btns[0], BUTTON_LONG_PRESS_START, button_long_press_start_cb, NULL);
+    iot_button_register_cb(g_btns[0], BUTTON_LONG_PRESS_HOLD, button_long_press_hold_cb, NULL);
+    iot_button_register_cb(g_btns[0], BUTTON_PRESS_REPEAT_DONE, button_press_repeat_done_cb, NULL);
+
+    TEST_ASSERT_EQUAL(ESP_OK, iot_button_stop());
+    vTaskDelay(pdMS_TO_TICKS(1000));
+    TEST_ASSERT_EQUAL(ESP_OK, iot_button_resume());
+
+    while (1)
+    {
+        vTaskDelay(pdMS_TO_TICKS(1000));
+    }
+
+    iot_button_delete(g_btns[0]);
+}
+
+TEST_CASE("matrix keyboard button test","[button][matrix key]")
+{
+    int32_t row_gpio[4] = {4,5,6,7};
+    int32_t col_gpio[4] = {3,8,16,15};
+    button_config_t cfg = {
+        .type = BUTTON_TYPE_MATRIX,
+        .long_press_time = CONFIG_BUTTON_LONG_PRESS_TIME_MS,
+        .short_press_time = CONFIG_BUTTON_SHORT_PRESS_TIME_MS,
+        .matrix_button_config = {
+            .row_gpio_num = 0,
+            .col_gpio_num = 0,
+        }
+    };
+
+    for (int i=0;i<4;i++){
+        cfg.matrix_button_config.row_gpio_num = row_gpio[i];
+        for (int j=0;j<4;j++) {
+            cfg.matrix_button_config.col_gpio_num = col_gpio[j];
+            g_btns[i*4+j] = iot_button_create(&cfg);
+            TEST_ASSERT_NOT_NULL(g_btns[i*4+j]);
+            iot_button_register_cb(g_btns[i*4+j], BUTTON_PRESS_DOWN, button_press_down_cb, NULL);
+            iot_button_register_cb(g_btns[i*4+j], BUTTON_PRESS_UP, button_press_up_cb, NULL);
+            iot_button_register_cb(g_btns[i*4+j], BUTTON_PRESS_REPEAT, button_press_repeat_cb, NULL);
+            iot_button_register_cb(g_btns[i*4+j], BUTTON_SINGLE_CLICK, button_single_click_cb, NULL);
+            iot_button_register_cb(g_btns[i*4+j], BUTTON_DOUBLE_CLICK, button_double_click_cb, NULL);
+            iot_button_register_cb(g_btns[i*4+j], BUTTON_LONG_PRESS_START, button_long_press_start_cb, NULL);
+            iot_button_register_cb(g_btns[i*4+j], BUTTON_LONG_PRESS_HOLD, button_long_press_hold_cb, NULL);
+            iot_button_register_cb(g_btns[i*4+j], BUTTON_PRESS_REPEAT_DONE, button_press_repeat_done_cb, NULL);
+        }
+    }
+
+    while (1)
+    {
+        vTaskDelay(pdMS_TO_TICKS(1000));
+    }
+
+    for (int i=0;i<4;i++){
+        for (int j=0;j<4;j++) {
+            iot_button_delete(g_btns[i*4+j]);
+        }
+    }
+}
+
+#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0)
+TEST_CASE("adc button test", "[button][iot]")
+{
+    /** ESP32-S3-Korvo board */
+    const uint16_t vol[6] = {380, 820, 1180, 1570, 1980, 2410};
+    button_config_t cfg = {0};
+    cfg.type = BUTTON_TYPE_ADC;
+    cfg.long_press_time = CONFIG_BUTTON_LONG_PRESS_TIME_MS;
+    cfg.short_press_time = CONFIG_BUTTON_SHORT_PRESS_TIME_MS;
+    for (size_t i = 0; i < 6; i++) {
+        cfg.adc_button_config.adc_channel = 7,
+        cfg.adc_button_config.button_index = i;
+        if (i == 0) {
+            cfg.adc_button_config.min = (0 + vol[i]) / 2;
+        } else {
+            cfg.adc_button_config.min = (vol[i - 1] + vol[i]) / 2;
+        }
+
+        if (i == 5) {
+            cfg.adc_button_config.max = (vol[i] + 3000) / 2;
+        } else {
+            cfg.adc_button_config.max = (vol[i] + vol[i + 1]) / 2;
+        }
+
+        g_btns[i] = iot_button_create(&cfg);
+        TEST_ASSERT_NOT_NULL(g_btns[i]);
+        iot_button_register_cb(g_btns[i], BUTTON_PRESS_DOWN, button_press_down_cb, NULL);
+        iot_button_register_cb(g_btns[i], BUTTON_PRESS_UP, button_press_up_cb, NULL);
+        iot_button_register_cb(g_btns[i], BUTTON_PRESS_REPEAT, button_press_repeat_cb, NULL);
+        iot_button_register_cb(g_btns[i], BUTTON_SINGLE_CLICK, button_single_click_cb, NULL);
+        iot_button_register_cb(g_btns[i], BUTTON_DOUBLE_CLICK, button_double_click_cb, NULL);
+        iot_button_register_cb(g_btns[i], BUTTON_LONG_PRESS_START, button_long_press_start_cb, NULL);
+        iot_button_register_cb(g_btns[i], BUTTON_LONG_PRESS_HOLD, button_long_press_hold_cb, NULL);
+        iot_button_register_cb(g_btns[i], BUTTON_PRESS_REPEAT_DONE, button_press_repeat_done_cb, NULL);
+    }
+
+    while (1) {
+        vTaskDelay(pdMS_TO_TICKS(1000));
+    }
+    for (size_t i = 0; i < 6; i++) {
+        iot_button_delete(g_btns[i]);
+    }
+}
+#else
+static esp_err_t adc_calibration_init(adc_unit_t unit, adc_atten_t atten, adc_cali_handle_t *out_handle)
+{
+    adc_cali_handle_t handle = NULL;
+    esp_err_t ret = ESP_FAIL;
+    bool calibrated = false;
+
+#if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
+    if (!calibrated) {
+        ESP_LOGI(TAG, "calibration scheme version is %s", "Curve Fitting");
+        adc_cali_curve_fitting_config_t cali_config = {
+            .unit_id = unit,
+            .atten = atten,
+            .bitwidth = ADC_BUTTON_WIDTH,
+        };
+        ret = adc_cali_create_scheme_curve_fitting(&cali_config, &handle);
+        if (ret == ESP_OK) {
+            calibrated = true;
+        }
+    }
+#endif
+
+#if ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED
+    if (!calibrated) {
+        ESP_LOGI(TAG, "calibration scheme version is %s", "Line Fitting");
+        adc_cali_line_fitting_config_t cali_config = {
+            .unit_id = unit,
+            .atten = atten,
+            .bitwidth = ADC_BUTTON_WIDTH,
+        };
+        ret = adc_cali_create_scheme_line_fitting(&cali_config, &handle);
+        if (ret == ESP_OK) {
+            calibrated = true;
+        }
+    }
+#endif
+
+    *out_handle = handle;
+    if (ret == ESP_OK) {
+        ESP_LOGI(TAG, "Calibration Success");
+    } else if (ret == ESP_ERR_NOT_SUPPORTED || !calibrated) {
+        ESP_LOGW(TAG, "eFuse not burnt, skip software calibration");
+    } else {
+        ESP_LOGE(TAG, "Invalid arg or no memory");
+    }
+
+    return calibrated ? ESP_OK : ESP_FAIL;
+}
+
+TEST_CASE("adc button idf5 drive test", "[button][iot]")
+{
+    adc_oneshot_unit_handle_t adc1_handle;
+    adc_cali_handle_t adc1_cali_handle;
+    adc_oneshot_unit_init_cfg_t init_config = {
+        .unit_id = ADC_UNIT_1,
+    };
+    esp_err_t ret = adc_oneshot_new_unit(&init_config, &adc1_handle);
+    TEST_ASSERT_TRUE(ret == ESP_OK);
+    adc_calibration_init(ADC_UNIT_1, ADC_ATTEN_DB_11, &adc1_cali_handle);
+
+    /** ESP32-S3-Korvo board */
+    const uint16_t vol[6] = {380, 820, 1180, 1570, 1980, 2410};
+    button_config_t cfg = {0};
+    cfg.type = BUTTON_TYPE_ADC;
+    cfg.long_press_time = CONFIG_BUTTON_LONG_PRESS_TIME_MS;
+    cfg.short_press_time = CONFIG_BUTTON_SHORT_PRESS_TIME_MS;
+    for (size_t i = 0; i < 6; i++) {
+        cfg.adc_button_config.adc_handle = &adc1_handle;
+        cfg.adc_button_config.adc_channel = 7,
+        cfg.adc_button_config.button_index = i;
+        if (i == 0) {
+            cfg.adc_button_config.min = (0 + vol[i]) / 2;
+        } else {
+            cfg.adc_button_config.min = (vol[i - 1] + vol[i]) / 2;
+        }
+
+        if (i == 5) {
+            cfg.adc_button_config.max = (vol[i] + 3000) / 2;
+        } else {
+            cfg.adc_button_config.max = (vol[i] + vol[i + 1]) / 2;
+        }
+
+        g_btns[i] = iot_button_create(&cfg);
+        TEST_ASSERT_NOT_NULL(g_btns[i]);
+        iot_button_register_cb(g_btns[i], BUTTON_PRESS_DOWN, button_press_down_cb, NULL);
+        iot_button_register_cb(g_btns[i], BUTTON_PRESS_UP, button_press_up_cb, NULL);
+        iot_button_register_cb(g_btns[i], BUTTON_PRESS_REPEAT, button_press_repeat_cb, NULL);
+        iot_button_register_cb(g_btns[i], BUTTON_SINGLE_CLICK, button_single_click_cb, NULL);
+        iot_button_register_cb(g_btns[i], BUTTON_DOUBLE_CLICK, button_double_click_cb, NULL);
+        iot_button_register_cb(g_btns[i], BUTTON_LONG_PRESS_START, button_long_press_start_cb, NULL);
+        iot_button_register_cb(g_btns[i], BUTTON_LONG_PRESS_HOLD, button_long_press_hold_cb, NULL);
+        iot_button_register_cb(g_btns[i], BUTTON_PRESS_REPEAT_DONE, button_press_repeat_done_cb, NULL);
+    }
+
+    while (1) {
+        vTaskDelay(pdMS_TO_TICKS(1000));
+    }
+    for (size_t i = 0; i < 6; i++) {
+        iot_button_delete(g_btns[i]);
+    }
+}
+#endif
+
+#define GPIO_OUTPUT_IO_45 45
+static EventGroupHandle_t g_check = NULL;
+static SemaphoreHandle_t g_auto_check_pass = NULL;
+static const char* button_event_str[BUTTON_EVENT_MAX] = {
+    "BUTTON_PRESS_DOWN",
+    "BUTTON_PRESS_UP",
+    "BUTTON_PRESS_REPEAT",
+    "BUTTON_PRESS_REPEAT_DONE",
+    "BUTTON_SINGLE_CLICK",
+    "BUTTON_DOUBLE_CLICK",
+    "BUTTON_MULTIPLE_CLICK",
+    "BUTTON_LONG_PRESS_START",
+    "BUTTON_LONG_PRESS_HOLD",
+    "BUTTON_LONG_PRESS_UP"
+};
+
+static button_event_t state = BUTTON_PRESS_DOWN;
+
+static void button_auto_press_test_task(void *arg)
+{
+    // test BUTTON_PRESS_DOWN
+    xEventGroupWaitBits(g_check, BIT(0) | BIT(1), pdTRUE, pdTRUE, portMAX_DELAY);
+    gpio_set_level(GPIO_OUTPUT_IO_45, 0);
+    vTaskDelay(pdMS_TO_TICKS(100));
+
+    // // test BUTTON_PRESS_UP
+    xEventGroupWaitBits(g_check, BIT(0) | BIT(1), pdTRUE, pdTRUE, portMAX_DELAY);
+    gpio_set_level(GPIO_OUTPUT_IO_45, 1);
+    vTaskDelay(pdMS_TO_TICKS(200));
+
+    // test BUTTON_PRESS_REPEAT
+    xEventGroupWaitBits(g_check, BIT(0) | BIT(1), pdTRUE, pdTRUE, portMAX_DELAY);
+    gpio_set_level(GPIO_OUTPUT_IO_45, 0);
+    vTaskDelay(pdMS_TO_TICKS(100));
+    gpio_set_level(GPIO_OUTPUT_IO_45, 1);
+    vTaskDelay(pdMS_TO_TICKS(100));
+    gpio_set_level(GPIO_OUTPUT_IO_45, 0);
+    vTaskDelay(pdMS_TO_TICKS(100));
+
+    // test BUTTON_PRESS_REPEAT_DONE
+    xEventGroupWaitBits(g_check, BIT(0) | BIT(1), pdTRUE, pdTRUE, portMAX_DELAY);
+    gpio_set_level(GPIO_OUTPUT_IO_45, 1);
+    vTaskDelay(pdMS_TO_TICKS(200));
+
+    // test BUTTON_SINGLE_CLICK
+    xEventGroupWaitBits(g_check, BIT(0) | BIT(1), pdTRUE, pdTRUE, portMAX_DELAY);
+    gpio_set_level(GPIO_OUTPUT_IO_45, 0);
+    vTaskDelay(pdMS_TO_TICKS(100));
+    gpio_set_level(GPIO_OUTPUT_IO_45, 1);
+    vTaskDelay(pdMS_TO_TICKS(200));
+
+    // test BUTTON_DOUBLE_CLICK
+    xEventGroupWaitBits(g_check, BIT(0) | BIT(1), pdTRUE, pdTRUE, portMAX_DELAY);
+    gpio_set_level(GPIO_OUTPUT_IO_45, 0);
+    vTaskDelay(pdMS_TO_TICKS(100));
+    gpio_set_level(GPIO_OUTPUT_IO_45, 1);
+    vTaskDelay(pdMS_TO_TICKS(100));
+    gpio_set_level(GPIO_OUTPUT_IO_45, 0);
+    vTaskDelay(pdMS_TO_TICKS(100));
+    gpio_set_level(GPIO_OUTPUT_IO_45, 1);
+    vTaskDelay(pdMS_TO_TICKS(200));
+
+    // test BUTTON_MULTIPLE_CLICK
+    xEventGroupWaitBits(g_check, BIT(0) | BIT(1), pdTRUE, pdTRUE, portMAX_DELAY);
+    for (int i = 0; i < 4; i++) {
+        gpio_set_level(GPIO_OUTPUT_IO_45, 0);
+        vTaskDelay(pdMS_TO_TICKS(100));
+        gpio_set_level(GPIO_OUTPUT_IO_45, 1);
+        vTaskDelay(pdMS_TO_TICKS(100));
+    }
+    vTaskDelay(pdMS_TO_TICKS(100));
+
+    // test BUTTON_LONG_PRESS_START
+    xEventGroupWaitBits(g_check, BIT(0) | BIT(1), pdTRUE, pdTRUE, portMAX_DELAY);
+    gpio_set_level(GPIO_OUTPUT_IO_45, 0);
+    vTaskDelay(pdMS_TO_TICKS(1600));
+
+    // test BUTTON_LONG_PRESS_HOLD and BUTTON_LONG_PRESS_UP
+    xEventGroupWaitBits(g_check, BIT(0) | BIT(1), pdTRUE, pdTRUE, portMAX_DELAY);
+    gpio_set_level(GPIO_OUTPUT_IO_45, 1);
+
+    ESP_LOGI(TAG, "Auto Press Success!");
+    vTaskDelete(NULL);
+}
+static void button_auto_check_cb_1(void *arg, void *data)
+{
+    if (iot_button_get_event(g_btns[0]) == state) {
+        xEventGroupSetBits(g_check, BIT(1));
+    }
+}
+static void button_auto_check_cb(void *arg, void *data)
+{
+    if (iot_button_get_event(g_btns[0]) == state) {
+        ESP_LOGI(TAG, "Auto check: button event %s pass", button_event_str[state]);
+        xEventGroupSetBits(g_check, BIT(0));
+        if (++state >= BUTTON_EVENT_MAX) {
+            xSemaphoreGive(g_auto_check_pass);
+            return;
+        }
+    }
+}
+
+TEST_CASE("gpio button auto-test", "[button][iot][auto]")
+{
+    state = BUTTON_PRESS_DOWN;
+    g_check = xEventGroupCreate();
+    g_auto_check_pass = xSemaphoreCreateBinary();
+    xEventGroupSetBits(g_check, BIT(0) | BIT(1));
+    button_config_t cfg = {
+        .type = BUTTON_TYPE_GPIO,
+        .long_press_time = CONFIG_BUTTON_LONG_PRESS_TIME_MS,
+        .short_press_time = CONFIG_BUTTON_SHORT_PRESS_TIME_MS,
+        .gpio_button_config = {
+            .gpio_num = 0,
+            .active_level = 0,
+        },
+    };
+    g_btns[0] = iot_button_create(&cfg);
+    TEST_ASSERT_NOT_NULL(g_btns[0]);
+
+    /* register iot_button callback for all the button_event */
+    for (uint8_t i = 0; i < BUTTON_EVENT_MAX; i++) {
+        if (i == BUTTON_MULTIPLE_CLICK) {
+            button_event_config_t btn_cfg;
+            btn_cfg.event = i;
+            btn_cfg.event_data.multiple_clicks.clicks = 4;
+            iot_button_register_event_cb(g_btns[0], btn_cfg, button_auto_check_cb_1, NULL);
+            iot_button_register_event_cb(g_btns[0], btn_cfg, button_auto_check_cb, NULL);
+        } else {
+            iot_button_register_cb(g_btns[0], i, button_auto_check_cb_1, NULL);
+            iot_button_register_cb(g_btns[0], i, button_auto_check_cb, NULL);
+        }
+    }
+
+    TEST_ASSERT_EQUAL(ESP_OK, iot_button_set_param(g_btns[0], BUTTON_LONG_PRESS_TIME_MS, (void *)1500));
+
+    gpio_config_t io_conf = {
+        .intr_type = GPIO_INTR_DISABLE,
+        .mode = GPIO_MODE_OUTPUT,
+        .pin_bit_mask = (1ULL << GPIO_OUTPUT_IO_45),
+        .pull_down_en = 0,
+        .pull_up_en = 0,
+    };
+    gpio_config(&io_conf);
+    gpio_set_level(GPIO_OUTPUT_IO_45, 1);
+
+    xTaskCreate(button_auto_press_test_task, "button_auto_press_test_task", 1024 * 4, NULL, 10, NULL);
+
+    TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreTake(g_auto_check_pass, pdMS_TO_TICKS(6000)));
+
+    for (uint8_t i = 0; i < BUTTON_EVENT_MAX; i++) {
+        button_event_config_t btn_cfg;
+        btn_cfg.event = i;
+        if (i == BUTTON_MULTIPLE_CLICK) {
+            btn_cfg.event_data.multiple_clicks.clicks = 4;
+        } else if ( i == BUTTON_LONG_PRESS_UP || i == BUTTON_LONG_PRESS_START) {
+            btn_cfg.event_data.long_press.press_time = 1500;
+        }
+        iot_button_unregister_event(g_btns[0], btn_cfg, button_auto_check_cb);
+        iot_button_unregister_event(g_btns[0], btn_cfg, button_auto_check_cb_1);
+    }
+
+    TEST_ASSERT_EQUAL(ESP_OK,iot_button_delete(g_btns[0]));
+    vEventGroupDelete(g_check);
+    vSemaphoreDelete(g_auto_check_pass);
+    vTaskDelay(pdMS_TO_TICKS(100));
+}
+
+#define TOLERANCE CONFIG_BUTTON_LONG_PRESS_TOLERANCE_MS
+
+uint16_t long_press_time[5] = {2000, 2500, 3000, 3500, 4000};
+static SemaphoreHandle_t long_press_check = NULL;
+static SemaphoreHandle_t long_press_auto_check_pass = NULL;
+unsigned int status = 0;
+
+static void button_auto_long_press_test_task(void *arg)
+{
+    // Test for BUTTON_LONG_PRESS_START
+    for (int i = 0; i < 5; i++) {
+        xSemaphoreTake(long_press_check, portMAX_DELAY);
+        gpio_set_level(GPIO_OUTPUT_IO_45, 0);
+        status = (BUTTON_LONG_PRESS_START << 16) | long_press_time[i];
+        if (i > 0)
+            vTaskDelay(pdMS_TO_TICKS(long_press_time[i] - long_press_time[i - 1]));
+        else
+            vTaskDelay(pdMS_TO_TICKS(long_press_time[i]));
+    }
+    vTaskDelay(pdMS_TO_TICKS(100));
+    gpio_set_level(GPIO_OUTPUT_IO_45, 1);
+    xSemaphoreGive(long_press_auto_check_pass);
+    vTaskDelay(pdMS_TO_TICKS(100));
+    // Test for BUTTON_LONG_PRESS_UP
+    for (int i = 0; i < 5; i++) {
+        xSemaphoreTake(long_press_check, portMAX_DELAY);
+        status = (BUTTON_LONG_PRESS_UP << 16) | long_press_time[i];
+        gpio_set_level(GPIO_OUTPUT_IO_45, 0);
+        vTaskDelay(pdMS_TO_TICKS(long_press_time[i] + 10));
+        gpio_set_level(GPIO_OUTPUT_IO_45, 1);
+    }
+
+    ESP_LOGI(TAG, "Auto Long Press Success!");
+    vTaskDelete(NULL);
+}
+
+static void button_long_press_auto_check_cb(void *arg, void *data)
+{
+    uint32_t value = (uint32_t)data;
+    uint16_t event = (0xffff0000 & value) >> 16;
+    uint16_t time = 0xffff & value;
+    uint16_t ticks_time = iot_button_get_ticks_time(g_btns[0]);
+    if (status == value && abs(ticks_time - time) <= TOLERANCE) {
+        ESP_LOGI(TAG, "Auto check: button event: %s and time: %d pass", button_event_str[event], time);
+
+        if (event == BUTTON_LONG_PRESS_UP && time == long_press_time[4]) {
+            xSemaphoreGive(long_press_auto_check_pass);
+        }
+
+        xSemaphoreGive(long_press_check);
+    }
+}
+
+TEST_CASE("gpio button long_press auto-test", "[button][long_press][auto]")
+{
+    ESP_LOGI(TAG, "Starting the test");
+    long_press_check = xSemaphoreCreateBinary();
+    long_press_auto_check_pass = xSemaphoreCreateBinary();
+    xSemaphoreGive(long_press_check);
+    button_config_t cfg = {
+        .type = BUTTON_TYPE_GPIO,
+        .long_press_time = CONFIG_BUTTON_LONG_PRESS_TIME_MS,
+        .short_press_time = CONFIG_BUTTON_SHORT_PRESS_TIME_MS,
+        .gpio_button_config = {
+            .gpio_num = 0,
+            .active_level = 0,
+        },
+    };
+    g_btns[0] = iot_button_create(&cfg);
+    TEST_ASSERT_NOT_NULL(g_btns[0]);
+
+    button_event_config_t btn_cfg;
+    btn_cfg.event = BUTTON_LONG_PRESS_START;
+    for (int i = 0; i < 5; i++) {
+        btn_cfg.event_data.long_press.press_time = long_press_time[i];
+        uint32_t data = (btn_cfg.event << 16) | long_press_time[i];
+        iot_button_register_event_cb(g_btns[0], btn_cfg, button_long_press_auto_check_cb, (void*)data);
+    }
+
+    gpio_config_t io_conf = {
+        .intr_type = GPIO_INTR_DISABLE,
+        .mode = GPIO_MODE_OUTPUT,
+        .pin_bit_mask = (1ULL << GPIO_OUTPUT_IO_45),
+        .pull_down_en = 0,
+        .pull_up_en = 0,
+    };
+    gpio_config(&io_conf);
+    gpio_set_level(GPIO_OUTPUT_IO_45, 1);
+    xTaskCreate(button_auto_long_press_test_task, "button_auto_long_press_test_task", 1024 * 4, NULL, 10, NULL);
+
+    xSemaphoreTake(long_press_auto_check_pass, portMAX_DELAY);
+    iot_button_unregister_cb(g_btns[0], BUTTON_LONG_PRESS_START);
+    btn_cfg.event = BUTTON_LONG_PRESS_UP;
+    for (int i = 0; i < 5; i++) {
+        btn_cfg.event_data.long_press.press_time = long_press_time[i];
+        uint32_t data = (btn_cfg.event << 16) | long_press_time[i];
+        iot_button_register_event_cb(g_btns[0], btn_cfg, button_long_press_auto_check_cb, (void*)data);
+    }
+    TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreTake(long_press_auto_check_pass, pdMS_TO_TICKS(17000)));
+    TEST_ASSERT_EQUAL(ESP_OK,iot_button_delete(g_btns[0]));
+    vSemaphoreDelete(long_press_check);
+    vSemaphoreDelete(long_press_auto_check_pass);
+    vTaskDelay(pdMS_TO_TICKS(100));
+}
+
+static void check_leak(size_t before_free, size_t after_free, const char *type)
+{
+    ssize_t delta = after_free - before_free;
+    printf("MALLOC_CAP_%s: Before %u bytes free, After %u bytes free (delta %d)\n", type, before_free, after_free, delta);
+    TEST_ASSERT_MESSAGE(delta >= TEST_MEMORY_LEAK_THRESHOLD, "memory leak");
+}
+
+void setUp(void)
+{
+    before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
+    before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
+}
+
+void tearDown(void)
+{
+    size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
+    size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
+    check_leak(before_free_8bit, after_free_8bit, "8BIT");
+    check_leak(before_free_32bit, after_free_32bit, "32BIT");
+}
+
+void app_main(void)
+{
+    printf("USB STREAM TEST \n");
+    unity_run_menu();
+}

+ 5 - 0
components/button/test_apps/main/component.mk

@@ -0,0 +1,5 @@
+#
+#Component Makefile
+#
+
+COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive

+ 23 - 0
components/button/test_apps/pytest_button.py

@@ -0,0 +1,23 @@
+# SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
+# SPDX-License-Identifier: Apache-2.0
+
+'''
+Steps to run these cases:
+- Build
+  - . ${IDF_PATH}/export.sh
+  - pip install idf_build_apps
+  - python tools/build_apps.py components/button/test_apps -t esp32s3
+- Test
+  - pip install -r tools/requirements/requirement.pytest.txt
+  - pytest components/button/test_apps --target esp32s3
+'''
+
+import pytest
+from pytest_embedded import Dut
+
+@pytest.mark.target('esp32s3')
+@pytest.mark.env('button')
+def test_usb_stream(dut: Dut)-> None:
+    dut.expect_exact('Press ENTER to see the list of tests.')
+    dut.write('[auto]')
+    dut.expect_unity_test_output(timeout = 60)

+ 9 - 0
components/button/test_apps/sdkconfig.defaults

@@ -0,0 +1,9 @@
+# For IDF 5.0
+CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y
+CONFIG_FREERTOS_HZ=1000
+CONFIG_ESP_TASK_WDT_EN=n
+
+# For IDF4.4
+CONFIG_ESP32S2_DEFAULT_CPU_FREQ_240=y
+CONFIG_ESP32S3_DEFAULT_CPU_FREQ_240=y
+CONFIG_ESP_TASK_WDT=n

+ 605 - 0
components/button/user_button.c

@@ -0,0 +1,605 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include "user_button.h"
+#include "iot_button.h"
+#include "esp_log.h"
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "freertos/queue.h"
+#include "freertos/semphr.h"
+#include "freertos/timers.h"
+
+
+#include "../EPD//include/EPD.h"
+
+
+static const char *LOG_TAG = "user button";
+
+
+
+extern QueueHandle_t button_Data_queue;
+extern SemaphoreHandle_t button_semaphore;
+
+
+static button_handle_t user_button_handle[KEY_NUM] = {0};
+
+static button_handle_t user_powerbutton_handle[3] = {0};
+TimerHandle_t btn_timer_handle;
+bool is_setting = false;
+bool is_reset_net = false;//配网
+
+
+uint16_t adc_value[][2]=
+{
+    {BAOYANG_MIN_ADC,BAOYANG_MAX_ADC},
+    {FENGCUN_MIN_ADC,FENGCUN_MAX_ADC},
+    {GUZHUANG_MIN_ADC,GUZHANG_MAX_ADC},
+    {DAILIAO_MIN_ADC,DAILIAO_MAX_ADC},
+    {TINGJI_MIN_ADC,TINGJI_MAX_ADC},
+    {YUNXING_MIN_ADC,YUNXING_MAX_ADC},
+};
+
+
+
+
+
+
+uint16_t power_adc_value[][2]=
+{
+    {0,100},
+    {1000,1900},
+    {2000,3063},
+};
+
+
+int  find_key_value(int value)
+{
+    int key = 0xff;
+
+    for(int i  =0 ;i<6;i++)
+    {
+        if( 
+            (adc_value[i][1]>=value)&&
+            (adc_value[i][0]<=value)
+          )
+          {
+
+            key = i;
+            printf("key vaule %d\r\n",i);
+             
+               break;
+          }
+
+    }
+
+
+      return key;
+
+}
+
+
+
+
+
+
+static void user_button_single_click_cb(void *arg,void *usr_data);
+
+static void power_long_press_start_cb(void *arg,void *usr_data);
+static void power_long_press_hold_cb(void *arg,void *usr_data);
+static void power_long_press_up_cb(void *arg,void *usr_data);
+
+
+static void power_single_press_cb(void *arg,void *usr_data);
+
+
+static void user_button_single_UP_cb(void *arg,void *usr_data);
+
+
+
+static void power_button_multi_press_cb(void *arg,void *usr_data);
+
+
+
+static void power_left_single_press_cb(void *arg,void *usr_data)
+{
+    printf("%s\r\n",__FUNCTION__);
+}
+
+static void power_right_single_press_cb(void *arg,void *usr_data)
+{
+    printf("%s\r\n",__FUNCTION__);
+}
+
+
+int button_idx = 0;
+
+
+
+int power_button_idx = 0;
+
+int power_button_mult_time = 0;
+
+void btn_timer_Callback(TimerHandle_t xTimer)
+{
+    ESP_LOGW(LOG_TAG, "btn_timer_Callback");
+    if(is_setting)
+    {
+        is_setting = false; 
+    }
+    else if (is_reset_net)
+    {
+        is_reset_net = false; 
+    }
+    
+
+    is_setting = 0;
+    //xTimerStop(btn_timer_handle,0);
+}
+
+void button_init(adc_oneshot_unit_handle_t adc_handle)
+{
+    int i;
+    printf("button_init\n");
+
+    #if 1
+	button_config_t adc_btn_cfg = {
+	    .type = BUTTON_TYPE_ADC,
+	    .long_press_time = CONFIG_BUTTON_LONG_PRESS_TIME_MS,
+	    .short_press_time = CONFIG_BUTTON_SHORT_PRESS_TIME_MS,
+	    .adc_button_config = {
+	        .adc_channel = 7,
+            .adc_handle = &adc_handle,
+	    },
+	};
+    //左边6个按键初始化
+    for(i = 0;i < KEY_NUM ;i++)
+    {
+        adc_btn_cfg.adc_button_config.button_index = i;
+        adc_btn_cfg.adc_button_config.min = adc_value[i][0] ;
+        adc_btn_cfg.adc_button_config.max = adc_value[i][1] ;
+        user_button_handle[i] = iot_button_create(&adc_btn_cfg);
+        if(NULL == user_button_handle[i]) {
+            printf( "Button create failed");
+        }
+        button_idx = i;
+        int ret = iot_button_register_cb( user_button_handle[i], BUTTON_PRESS_DOWN, user_button_single_click_cb,button_idx);
+                  //iot_button_register_cb( user_powerbutton_handle[i], BUTTON_LONG_PRESS_START, power_long_press_cb,button_idx);
+        //iot_button_register_cb( user_button_handle[i], BUTTON_PRESS_UP, user_button_single_click_cb,button_idx);
+    }
+
+    #endif
+
+}
+void button_deinit(void)
+{
+    printf("button_deinit\n");
+    for(int i=0;i<KEY_NUM;i++)
+    {
+        iot_button_delete(user_button_handle[i]);
+    }
+    //iot_button_delete(user_powerbutton_handle[0]);
+}
+
+
+void power_button_init(adc_oneshot_unit_handle_t adc_handle)
+{
+    btn_timer_handle = xTimerCreate(
+    "btn_timer_handle",             
+    pdMS_TO_TICKS(10*1000),    
+    pdFALSE,                
+    0,                     
+    btn_timer_Callback         
+    );
+
+ int i = 0;
+
+
+
+#if 0
+    //右边3个按键  目前只是用一个按键 POWER
+    button_config_t power_btn_cfg = {
+	    .type = BUTTON_TYPE_GPIO,
+	    .long_press_time = CONFIG_BUTTON_LONG_PRESS_TIME_MS,
+	    .short_press_time = CONFIG_BUTTON_SHORT_PRESS_TIME_MS,
+	    .adc_button_config = {
+	        .adc_channel = 3,
+            .adc_handle = &adc_handle,
+	    },
+	};
+
+    for(int i = 0;i < 1 ;i++)
+    {
+        power_btn_cfg.adc_button_config.button_index = i;
+        power_btn_cfg.adc_button_config.min = power_adc_value[i][0] ;
+        power_btn_cfg.adc_button_config.max = power_adc_value[i][1] ;
+        user_powerbutton_handle[i] = iot_button_create(&power_btn_cfg);
+        if(NULL == user_powerbutton_handle[i]) {
+            printf( "Button create failed");
+        }
+        button_idx = i;
+        
+        iot_button_register_cb( user_powerbutton_handle[i], BUTTON_PRESS_DOWN, power_single_press_cb,button_idx);
+        #if 0
+        iot_button_register_cb( user_powerbutton_handle[i], BUTTON_LONG_PRESS_START, power_long_press_cb,button_idx);
+        #else
+        iot_button_register_cb( user_powerbutton_handle[i], BUTTON_LONG_PRESS_START, power_long_press_start_cb,button_idx);
+        iot_button_register_cb( user_powerbutton_handle[i], BUTTON_LONG_PRESS_HOLD, power_long_press_hold_cb,button_idx);
+        iot_button_register_cb( user_powerbutton_handle[i], BUTTON_LONG_PRESS_UP, power_long_press_up_cb,button_idx);
+        #endif
+
+
+
+
+
+
+        //注册按键多次按键回调
+        button_event_config_t btn_cfg;
+        btn_cfg.event = BUTTON_MULTIPLE_CLICK;
+        // btn_cfg.event_data.multiple_clicks.clicks = POWER_KEY_PRSSS_COUNT_OTA;
+        // power_button_mult_time = btn_cfg.event_data.multiple_clicks.clicks ;
+        // iot_button_register_event_cb(user_powerbutton_handle[i], btn_cfg, power_button_multi_press_cb, power_button_mult_time);
+        btn_cfg.event_data.multiple_clicks.clicks = POWER_KEY_PRSSS_COUNT_RESET;
+        power_button_mult_time = btn_cfg.event_data.multiple_clicks.clicks ;
+        iot_button_register_event_cb(user_powerbutton_handle[i], btn_cfg, power_button_multi_press_cb, power_button_mult_time);
+
+        btn_cfg.event_data.multiple_clicks.clicks = POWER_KEY_PRSSS_SETTING_MODE;
+        power_button_mult_time = btn_cfg.event_data.multiple_clicks.clicks ;
+        iot_button_register_event_cb(user_powerbutton_handle[i], btn_cfg, power_button_multi_press_cb, power_button_mult_time);            
+
+
+        // //注册按键长按按键回调
+        // button_event_config_t btn_long_press_cfg;
+        // btn_cfg.event = BUTTON_LONG_PRESS_START;
+    }
+#else
+
+
+
+  button_config_t power_btn_cfg = {
+            .type = BUTTON_TYPE_GPIO,
+            .long_press_time = CONFIG_BUTTON_LONG_PRESS_TIME_MS,
+            .short_press_time = CONFIG_BUTTON_SHORT_PRESS_TIME_MS,
+            .gpio_button_config = {
+                .gpio_num = 4,
+                .active_level = 0,
+            },
+        };
+
+
+
+
+
+
+        for(int i = 0;i < 1 ;i++)
+    {
+       //power_btn_cfg.adc_button_config.button_index = i;
+       //power_btn_cfg.adc_button_config.min = power_adc_value[i][0] ;
+       //power_btn_cfg.adc_button_config.max = power_adc_value[i][1] ;
+
+
+        user_powerbutton_handle[i] = iot_button_create(&power_btn_cfg);
+        if(NULL == user_powerbutton_handle[i]) {
+            printf( "Button create failed");
+        }
+        button_idx = i;
+        
+        iot_button_register_cb( user_powerbutton_handle[i], BUTTON_PRESS_DOWN, power_single_press_cb,button_idx);
+        #if 0
+        iot_button_register_cb( user_powerbutton_handle[i], BUTTON_LONG_PRESS_START, power_long_press_cb,button_idx);
+        #else
+        iot_button_register_cb( user_powerbutton_handle[i], BUTTON_LONG_PRESS_START, power_long_press_start_cb,button_idx);
+        iot_button_register_cb( user_powerbutton_handle[i], BUTTON_LONG_PRESS_HOLD, power_long_press_hold_cb,button_idx);
+        iot_button_register_cb( user_powerbutton_handle[i], BUTTON_LONG_PRESS_UP, power_long_press_up_cb,button_idx);
+        #endif
+
+
+
+
+
+
+        //注册按键多次按键回调
+        button_event_config_t btn_cfg;
+        btn_cfg.event = BUTTON_MULTIPLE_CLICK;
+        // btn_cfg.event_data.multiple_clicks.clicks = POWER_KEY_PRSSS_COUNT_OTA;
+        // power_button_mult_time = btn_cfg.event_data.multiple_clicks.clicks ;
+        // iot_button_register_event_cb(user_powerbutton_handle[i], btn_cfg, power_button_multi_press_cb, power_button_mult_time);
+        btn_cfg.event_data.multiple_clicks.clicks = POWER_KEY_PRSSS_COUNT_RESET;
+        power_button_mult_time = btn_cfg.event_data.multiple_clicks.clicks ;
+        iot_button_register_event_cb(user_powerbutton_handle[i], btn_cfg, power_button_multi_press_cb, power_button_mult_time);
+
+        btn_cfg.event_data.multiple_clicks.clicks = POWER_KEY_PRSSS_SETTING_MODE;
+        power_button_mult_time = btn_cfg.event_data.multiple_clicks.clicks ;
+        iot_button_register_event_cb(user_powerbutton_handle[i], btn_cfg, power_button_multi_press_cb, power_button_mult_time);            
+
+
+        // //注册按键长按按键回调
+        // button_event_config_t btn_long_press_cfg;
+        // btn_cfg.event = BUTTON_LONG_PRESS_START;
+    }
+
+
+
+
+
+#endif
+
+
+
+
+}
+
+
+
+
+
+void power_button_deinit(void)
+{
+     for(int i = 0;i < 3;i++)
+     iot_button_delete(user_powerbutton_handle[i]);
+}
+
+
+uint8_t button_info = 0;
+
+#include "SPIFFS.h"
+#if 0
+static void power_long_press_cb(void *arg,void *usr_data)
+{
+      switch ((int)usr_data)
+      {
+        case 0:
+        printf("power key long press start\r\n");
+        button_info = POWER_ON_INTO_STATUS_CHANGE_VALUE;
+            if(xQueueSend(button_Data_queue,&button_info,0) != true)
+            {
+                ESP_LOGE(LOG_TAG,"queue send is fail");
+                return;
+            }
+        break;
+        case 1:
+        printf("power left key long press\r\n");
+        break;
+      }
+}
+iot_button_get_long_press_hold_cnt((button_handle_t)arg)
+#else
+static void power_long_press_start_cb(void *arg,void *usr_data)
+{
+    // printf("power_long_press_start_cb\n");
+}
+static void power_long_press_hold_cb(void *arg,void *usr_data)
+{
+    //设置的长按开始时间后开始计算hold_cnt  长按开始时间设置为1s,cnt计算时间间隔(SERIAL_TICKS)也设置为1s
+    uint16_t hold_cnt = iot_button_get_long_press_hold_cnt((button_handle_t)arg);
+    switch ((int)hold_cnt)
+    {
+    case 0:
+    {
+         ESP_LOGE(LOG_TAG,"case 0:");
+         button_info = POWER_ON_INTO_DIS_RIGHT;
+        if(xQueueSend(button_Data_queue,&button_info,0) != true)
+            {
+                ESP_LOGE(LOG_TAG,"queue send is fail");
+                return;
+            }
+    }
+    case 2://设置模式,长按3s进入
+        if(is_setting && is_reset_net)
+        {
+            ESP_LOGE(LOG_TAG,"err:OTA 和 重新配网 同时");
+            return;
+        }
+        if(is_setting)
+        {
+            button_info = POWER_ON_INTO_OTA_VALUE;
+            if(xQueueSend(button_Data_queue,&button_info,0) != true)
+            {
+                ESP_LOGE(LOG_TAG,"queue send is fail");
+                return;
+            }
+        }
+
+        if(is_reset_net)
+        {
+            button_info = POWER_ON_INTO_RESET_VALUE;
+            if(xQueueSend(button_Data_queue,&button_info,0) != true)
+            {
+                ESP_LOGE(LOG_TAG,"queue send is fail");
+                return;
+            }
+        }
+    break;
+    case 4://5s开关机
+        button_info = POWER_ON_INTO_STATUS_CHANGE_VALUE;
+        if(xQueueSend(button_Data_queue,&button_info,0) != true)
+        {
+            ESP_LOGE(LOG_TAG,"queue send is fail");
+            return;
+        }
+    break;
+    default:
+        //printf(" BUTTON_LONG_PRESS_HOLD[%d],count is [%d]\n", iot_button_get_ticks_time((button_handle_t)arg), hold_cnt);
+    break;
+    }
+}
+static void power_long_press_up_cb(void *arg,void *usr_data)
+{
+    // printf("power_long_press_up_cb\n");
+}
+#endif
+
+
+
+#include "esp_sleep.h"
+
+static void power_single_press_cb(void *arg,void *usr_data)
+{
+
+      switch ((int)usr_data)
+      {
+          case 0:
+          printf("power sigle press\r\n");
+
+                  if(Machine_info.power_status == 1)  //开机
+                {
+
+                        button_info = POWER_ON_PRESS_VALUE;
+
+                        if(xQueueSend(button_Data_queue,&button_info,0) != true)
+                    {
+                        ESP_LOGE(LOG_TAG,"queue send is fail");
+                        return;
+                    }
+
+                    
+                }else 
+                {
+                    button_info = POWER_OFF_PRESS_VALUE;  //关机状态下单击按键
+                if(xQueueSend(button_Data_queue,&button_info,0) != true)
+                    {
+                        ESP_LOGE(LOG_TAG,"queue send is fail");
+                        return;
+                    }
+                }
+   
+          break;
+
+
+
+
+          case 1:
+          printf("power left single press\r\n");
+
+          break;
+
+          case 2:
+          printf("power right single press\r\n");
+
+          break;
+      }
+}
+
+
+static void user_button_single_click_cb(void *arg,void *usr_data)
+{
+    is_setting = false;
+    is_reset_net = false;
+    switch ((int)usr_data)
+    {
+    case BAOYANG_KEY:
+            button_info = 6;
+            ESP_LOGD(LOG_TAG,"bao yang");
+        break;
+    case FENGCUN_KEY:
+            button_info = 5;
+            ESP_LOGD(LOG_TAG,"feng cun");
+        break;
+    case GUZHUANG_KEY:
+            button_info = 4;
+            ESP_LOGD(LOG_TAG,"gu zhang");
+        break;
+    case DAILIAO_KEY:
+            button_info = 3;
+            ESP_LOGD(LOG_TAG,"dai liao");
+        break;
+    case TINGJI_KEY:
+            button_info = 2;
+            ESP_LOGD(LOG_TAG,"ting ji");
+        break;
+    case YUNXING_KEY:
+            button_info = 1;
+            ESP_LOGD(LOG_TAG,"yun xing");
+        break;
+    default:
+        break;
+    }
+
+#if 0
+    if(xSemaphoreTake(button_semaphore, 0) != true)
+    {  
+        ESP_LOGE(LOG_TAG,"button mutex  is fail,not send lora queue");
+        return;
+    }
+
+    xSemaphoreGive(button_semaphore);
+#endif   
+
+    if(Machine_info.power_status == 0)  //关机
+    {
+        printf("power off not refresh\r\n");
+        return;
+    }
+
+        if(left_refresh_timer_isActive() == true)
+        {
+            return;
+        }
+    
+    if(xQueueSend(button_Data_queue,&button_info,0) != true)
+    {
+        ESP_LOGE(LOG_TAG,"queue send is fail");
+        return;
+    }
+
+    
+}
+
+
+
+static void power_button_multi_press_cb(void *arg,void *usr_data)
+{
+    if(Machine_info.power_status == 0)  //关机
+    {
+        printf("关机状态\r\n");
+        return;
+    }
+    switch ((int)usr_data)
+    {
+        case POWER_KEY_PRSSS_SETTING_MODE:
+        if(!is_setting && !is_reset_net) //当前非配置模式
+        {
+            is_setting = true;
+            xTimerStart(btn_timer_handle, 0);
+            printf("短按2次\r\n");
+        }
+        break;
+        case  POWER_KEY_PRSSS_COUNT_RESET:
+            if(!is_setting && !is_reset_net)
+            {
+                is_reset_net = true;
+                xTimerStart(btn_timer_handle, 0);
+                printf("短按4次\r\n");
+            }
+        break;
+    }
+}
+
+
+
+
+
+
+static void user_button_single_UP_cb(void *arg,void *usr_data)
+{
+
+
+    
+}
+
+void send_button_key_queue(uint8_t value)
+{
+
+         button_info = value;
+         if(xQueueSend(button_Data_queue,&button_info,0) != true)
+        {
+            ESP_LOGE(LOG_TAG,"queue send is fail");
+            return;
+        }
+}
+
+
+
+
+
+
+

+ 3 - 0
components/c_linked_list/CMakeLists.txt

@@ -0,0 +1,3 @@
+idf_component_register(SRCS "list.c"
+                    INCLUDE_DIRS "."
+                    REQUIRES)

+ 186 - 0
components/c_linked_list/README.md

@@ -0,0 +1,186 @@
+# C Linked List library
+A simple library written in C for managing linked lists.
+
+To understand well the operation of the data structure it is convenient to consolidate the notions concerning pointers in C.
+## Table of Contents
+
+- [Linked list in a nutshell](#linked-list-in-a-nutshell)
+- [Definition of the struct](#definition-of-the-struct)
+- [Inserting operations](#inserting-operations)
+	* [Create a new node](#create-a-new-node)
+	* [Pre-Insertion](#pre-insertion)
+	* [Post-Insertion](#post-insertion)
+	* [Sorted insertion](#sorted-insertion)
+- [Managing operations](#managing-operations)
+	* [Linear search](#linear-search)
+	* [Merge of two sorted lists](#merge-of-two-sorted-lists)
+	* [Splitting a list](#splitting-a-list)
+	* [Print list](#print-list)
+	* [Counting nodes](#counting-nodes)
+- [Deleting operations](#deleting-operations)
+	* [Deleting a node](#deleting-a-node)
+	* [Erasing the list](#erasing-the-list)
+- [Sorting the list](#sorting-the-list)
+
+## Linked list in a nutshell
+It's a dynamic data structure with a linear collection of items. It consists of a sequence of nodes, each containing arbitrary data fields and one reference pointing to the next node. A linked list is a self-referring data type, as it contains a pointer to another data of the same type.
+
+## Definition of the struct
+```c
+typedef struct node {
+	int n;
+	/* float b;
+	 * char c;
+	 * ... etc.
+	 */
+	struct node *next;
+}Node;
+```
+Node struct contains the information of the single node. In this example there is a dummy variable of integers but it can be shaped according to your needs. The `struct node *next` variable points to his next item (which is NULL if it is the last node in the list).
+
+## Inserting operations
+
+### Create a new node
+`Node *newNode(int x)`
+
+Creates physically a new node. It is not used directly into main block, but only from other functions, like inserting ones.
+
+**Parameter:**
+- `int x`: value to insert in the node
+
+![newNode](https://github.com/Leyxargon/c-linked-list/blob/master/figures/newNode.png "newNode()")
+
+### Pre-Insertion
+`Node *preInsert(Node *top, int x)`
+
+Inserts a new item at the top of the list. If the list is not empty then the new node will be the new top item, which will points to the list.
+
+**Parameters:**
+- `Node *top`: pointer to the first element of the list
+- `int x`: value to insert in the node
+
+![preInsert](https://github.com/Leyxargon/c-linked-list/blob/master/figures/preInsert.png "preInsert()")
+
+### Post-Insertion
+`Node *postInsert(Node *top, int x)`
+
+Inserts a new item at the end of the list. If the list is not empty then the new node will be the last one, pointed by the previous last node.
+
+**Parameters:**
+- `Node *top`: pointer to the first element of the list
+- `int x`: value to insert in the node
+
+![postInsert](https://github.com/Leyxargon/c-linked-list/blob/master/figures/postInsert.png "postInsert()")
+
+### Sorted insertion
+`Node *orderInsert(Node *top, int x)`
+
+Inserts a new item in the list following an order, according to a key field. For example if you want to have an increasingly ordered list, this function is the solution.
+This insertion function cannot be combined with Pre and Post insertions, the result is the loss of meaning of the eventual order applied by the sorted insertion.
+
+**Parameters:**
+- `Node *top`: pointer to the first element of the list
+- `int x`: value to insert in the node
+
+![orderInsert](https://github.com/Leyxargon/c-linked-list/blob/master/figures/orderInsert.png "orderInsert()")
+
+## Managing operations
+
+### Linear search
+`Node *findNode(Node *top, int k)`
+
+Finds the node that contains the k value. It scans all the list while the item is not found, or the list is ended. If the node with k value is not in the list, it will return NULL.
+
+**Parameters:**
+- `Node *top`: pointer to the first element of the list
+- `int k`: value to find in the list
+
+### Merge of two sorted lists
+`Node* Merge(Node *top1, Node *top2)`
+
+Returns a single linked list made by merge of two given sorted linked lists.
+This function has ad undefined behavior if used with unsorted linked lists.
+
+### Splitting a list
+`void Split(Node *top, Node **front, Node **back)`
+
+Splits a given list in two lists.
+
+### Print list
+`void printList(Node *top)`
+
+Prints all the items in the list.
+
+**Parameter:**
+- `Node *top`: pointer to the first element of the list
+
+### Counting nodes
+`int countNodes(Node *top)`
+
+Returns the number of the nodes in the list.
+
+**Parameter:**
+- `Node *top`: pointer to the first element of the list
+
+## Deleting operations
+
+### Deleting a node
+`Node *deleteNode(Node *top, int k)`
+
+This function applies the linear search to remove the element that contains the k value.
+
+**Parameters:**
+- `Node *top`: pointer to the first element of the list
+- `int k`: value to delete in the list
+
+![deleteNode](https://github.com/Leyxargon/c-linked-list/blob/master/figures/deleteNode.png "deleteNode()")
+
+### Erasing the list
+`Node *deleteList(Node *top)`
+
+**Parameter:**
+- `Node *top`: pointer to the first element of the list
+
+Destroy the list deallocating every nodes. At the end it will return a NULL pointer.
+
+## Sorting the list
+`void MergeSort(Node **top)`
+
+Sort a non-empty list. This algorithm works with pointers, so there is not the variables swapping, but simply redefines the order of the pointers that make up the list.
+
+**Parameter:**
+- `Node **top`: address of the pointer to the first element of the list
+
+![MergeSort](https://github.com/Leyxargon/c-linked-list/blob/master/figures/MergeSort.png "MergeSort()")
+
+## Example of usage
+```c
+#include ...
+
+int main(...) {
+	Node *list = NULL;
+	
+	list = postInsert(list, 7);
+	list = postInsert(list, 18);
+	list = postInsert(list, 1);
+	list = preInsert(list, 20);
+	
+	printList(list);
+	
+	if (findNode(list, 2) != NULL)
+		printf("Found 2 in the list\n");
+	else
+		printf("Not found 2 in the list\n");
+		
+	printf("Sorted list:\n");
+	MergeSort(&list);
+	printList(list);
+		
+	if ((list = deleteList(list)) == NULL)
+		printf("List erased correctly\n");
+	else
+		printf("Error\n");
+		
+	return 0;
+}
+```

BIN
components/c_linked_list/figures/MergeSort.png


BIN
components/c_linked_list/figures/deleteNode.png


BIN
components/c_linked_list/figures/newNode.png


BIN
components/c_linked_list/figures/orderInsert.png


BIN
components/c_linked_list/figures/postInsert.png


BIN
components/c_linked_list/figures/preInsert.png


+ 331 - 0
components/c_linked_list/list.c

@@ -0,0 +1,331 @@
+#include "list.h"
+#include "string.h"
+#include <stdint.h>
+#include<string.h>
+#include <stdlib.h>
+#include<stdbool.h>
+#include "esp_heap_caps.h"
+
+#if 1
+Node *newNode(int x,int cmd,char *data,int len) 
+#else
+Node *newNode(int x) 
+#endif
+{
+	
+
+	#if 0
+	Node *pnt = (Node *) malloc (sizeof(Node)); /* allocates physical memory for the node */
+	#else
+	Node *pnt = (Node *) heap_caps_malloc(sizeof(Node),MALLOC_CAP_RTCRAM); /* allocates physical memory for the node */
+	#endif
+	
+	pnt -> n = x;  
+	                             /* inserts the information received as input in the field(s) in the list */
+	/* entering additional information if necessary...
+	 * pnt -> b = y;
+	 * pnt -> c = z;
+	 * ... and so on
+	 */
+
+	pnt -> cmd = cmd;
+	#if 0
+	pnt -> data = malloc(len);
+	#else
+	pnt -> data = heap_caps_malloc(len,MALLOC_CAP_RTCRAM);
+	#endif
+
+	#include "string.h"
+	memcpy(pnt -> data,data,len);
+
+	pnt -> len = len;
+	pnt -> next = NULL;                         /* initialize the pointer */
+	/* an insert function will take care of properly setting the next variable */
+	return pnt;
+}
+
+//cmd,data,len
+
+Node *preInsert(Node *top, int x,int cmd,char *data,int len) {
+	Node *tmp = newNode(x,cmd,data,len);						/* create a node with input information */
+	tmp -> next = top;							/* top corresponds to the first element of the list BEFORE this operation,
+												 * which provides to put "tmp" at the top of the list,
+												 * thus becoming the new "top" */
+	return tmp;									/* returns the new first element of the list */
+}
+
+Node *orderInsert(Node *top, int x,int cmd,char *data,int len) {
+	if (top == NULL || top -> n > x) {
+		/* if top == NULL is true then the element will be positioned at the bottom of the list,
+		 * if top -> n > x then the element will be positioned in the middle of the list.
+		 * OBSERVATION: the first condition is fundamental to the function of the function
+		 * while the second one can be modeled according to the needs, in this case the condition
+		 * top -> n > x imposes an increasing order of the elements of the list, but it could well
+		 * be top -> n < x if you wanted to insert elements in descending order. */
+		Node *tmp = newNode(x,cmd,data,len);						/* creates a node with the information entered as input */
+		tmp -> next = top;							/* points to the next node in the list */
+		top = tmp;									/* inserts the node in the list */
+		return top;
+	}
+	else
+		top -> next = orderInsert(top -> next, x,cmd,data,len);	/* "scrolls" the list, examining the next node */
+	return top;
+}
+
+Node *postInsert(Node *top, int x,int cmd,char *data,int len) {
+	if (top == NULL)
+		return newNode(x,cmd,data,len);							/* inserts the item at the bottom of the list */
+	else
+		top -> next = postInsert(top -> next, x,cmd,data,len);	/* if it is not at the bottom, move on */
+	return top;
+}
+
+Node *findNode(Node *top, int k) {
+	if (top == NULL || top -> n == k)
+		return top;							/* returns the found node (NULL if it was not found) */
+	else
+		return findNode(top -> next, k);	/* scrolls the list to search for the item */
+}
+
+Node *deleteNode(Node *top, int k) {
+	Node *tmp;					/* temporary node */
+	if (top != NULL) {
+		if (top -> n == k) {	/* if the element was found, it is deleted */
+
+
+			top -> n = 0;
+			top -> cmd= 0;
+			memset(top ->data,0x00,top -> len);
+			top -> len = 0;
+
+			// 释放一个已分配的msgid_num
+		    deallocateMsgIdNum(top->n); 
+			tmp = top -> next;	/* set the temporary node to the next element (in order not to "lose" the list) */
+			heap_caps_free(top->data);//释放data
+			heap_caps_free(top);			/* frees the physical memory associated with the node to be deleted */
+			top = tmp;			/* set the pointer to the next node */
+		}
+		else
+			top -> next = deleteNode(top -> next, k);	/* scrolls the list in order to search the item to be deleted */
+	}
+	return top;
+}
+
+
+
+Node *deleteNode_head(Node *top) {
+	Node *tmp;					/* temporary node */
+	if (top != NULL) {
+		 if (1)
+		 {	/* if the element was found, it is deleted */
+			// 释放一个已分配的msgid_num
+		    deallocateMsgIdNum(top->n); 
+			tmp = top -> next;	/* set the temporary node to the next element (in order not to "lose" the list) */
+			//free(top->data);//释放data
+			free(top);			/* frees the physical memory associated with the node to be deleted */
+			top = tmp;			/* set the pointer to the next node */
+		}
+		else
+			top -> next = deleteNode_head(top -> next);	/* scrolls the list in order to search the item to be deleted */
+	}
+	return top;
+}
+
+
+
+
+
+Node *deleteList(Node *top) {
+	if (top != NULL) {				/* if not reached end of the list... */
+		deleteList(top -> next);	/* ...move on */
+		//free(top->data);
+		free(top);					/* delete the node */
+		return NULL;
+	}
+	//else
+		return NULL;
+}
+
+
+
+
+// 函数用于复制链表
+ Node* copyList( Node* originalList) {
+    if (originalList == NULL) {
+        return NULL;
+    }
+
+     Node* current = originalList; // 用于遍历原链表
+     Node* newList = NULL;         // 新链表的头节点
+     Node* tail = NULL;            // 新链表的尾节点
+
+    // 遍历原链表
+    while (current != NULL) {
+        // 创建新节点
+         Node* newNode = (Node*)malloc(sizeof(Node));
+
+		newNode->n = current->n; 
+		newNode->cmd = current->cmd; 
+		newNode->len = current->len; 
+        newNode->data = malloc(current->len);
+		memcpy(newNode->data,current->data,current->len);
+	
+        newNode->next = NULL;
+
+        // 如果新链表为空,设置新链表的头节点
+        if (newList == NULL) {
+            newList = newNode;
+            tail = newNode;
+        } else {
+            // 否则,将新节点连接到新链表的尾部
+            tail->next = newNode;
+            tail = newNode;
+        }
+
+        current = current->next;
+    }
+
+    return newList; // 返回新链表的头节点
+}
+
+
+void printList(Node *top) {
+	if (top != NULL) {
+		printf("n=%d ", top -> n);
+		printf(" data=%s", top -> data);
+		printf("  len=%d\r\n", top -> len);
+		printList(top -> next);
+	}
+	else
+		printf("NULL\n");
+}
+
+void MergeSort(Node **top) {
+	Node *tmp = *top, *a, *b;
+
+    if (tmp != NULL && tmp -> next != NULL) {
+		Split(tmp, &a, &b);				/* (divide) split head into "a" and "b" sublists */
+	  
+		/* (conquer) sort the sublists */
+		MergeSort(&a); 
+		MergeSort(&b); 
+	  
+		*top = Merge(a, b);				/* (combine) merge the two sorted lists together */
+	}
+} 
+
+Node* Merge(Node *top1, Node *top2) {
+    if (top1 == NULL) 
+		return top2; 
+    else
+		if (top2 == NULL) 
+			return top1;   
+
+    Node* pnt = NULL;
+    /* pick either top1 or top2, and merge them */
+    if (top1 -> n <= top2 -> n) { 
+		pnt = top1;
+		pnt -> next = Merge(top1 -> next, top2); 
+    } 
+    else {
+        pnt = top2; 
+        pnt -> next = Merge(top1, top2 -> next); 
+    }
+    return pnt; 
+} 
+
+void Split(Node *top, Node **front, Node **back) {
+	Node* fast = top -> next;
+	Node* slow = top;
+
+    /* fast pointer advances two nodes, slow pointer advances one node */
+	while (fast != NULL) {
+		fast = fast -> next;		/* "fast" moves on first time */
+		if (fast != NULL) { 
+			slow = slow -> next;	/* "slow" moves on first time */
+			fast = fast -> next;	/* "fast" moves on second time */
+        }
+    }
+	
+    /* "slow" is before the middle in the list, so split it in two at that point */
+	*front = top;
+	*back = slow -> next;
+	slow -> next = NULL;			/* end of the input list */
+}
+
+int countNodes(Node *top) {
+    if (top == NULL)
+        return 0;
+    else
+        return 1 + countNodes(top -> next);
+}
+
+int countClockNodes_byCMD(Node *top,char type_cmd) 
+{   
+    if (top == NULL)
+        return 0;
+    else
+    {
+        if(top->cmd == type_cmd)
+            return 1 + countClockNodes_byCMD(top->next,type_cmd);
+        else
+            return countClockNodes_byCMD(top->next,type_cmd);
+    }   
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+bool msgidNumAllocated[MAX_MSG_ID_NUM] = {false};
+
+
+// 分配一个可用的msgid_num
+int allocateMsgIdNum() {
+    for (int i = 1; i <= MAX_MSG_ID_NUM; i++) {
+        if (!msgidNumAllocated[i - 1]) {
+            msgidNumAllocated[i - 1] = true; // 标记为已分配
+            return i;
+        }
+    }
+    return -1; // 没有可用的msgid_num
+}
+
+// 释放一个已分配的msgid_num
+void deallocateMsgIdNum(int idNum) {
+    if (idNum >= 1 && idNum <= MAX_MSG_ID_NUM) {
+        msgidNumAllocated[idNum - 1] = false; // 标记为未分配
+    }
+}
+
+
+
+void wait_Send_Data_Insert(int msgid,uint8_t *data,int len)
+{
+
+}
+
+
+
+
+void wait_Send_Data_Delete(int msgid)
+{
+    
+}
+
+

+ 84 - 0
components/c_linked_list/list.h

@@ -0,0 +1,84 @@
+#include <stdio.h>
+#include <stdlib.h>
+#ifndef __LIST_H__
+#define __LIST_H__
+
+typedef struct node {
+	int n;              /* data field(s) */
+	char * data;  //数据
+	char   cmd;   //发送的命令
+	int len;      //发送的长度
+	/* float b;
+	 * char c;
+	 * ... etc.
+	 */
+	struct node *next;  /* pointer to next element */
+}Node;
+
+#if 0
+Node *newNode(int ); /* physically creates a new node */
+#else
+Node *newNode(int x,int cmd,char *data,int len);
+#endif
+/* N.B. this function is called by other functions because does not take care
+ * of inserting the Node in the list, but delegates this operation to other
+ * functions, such as *Insert functions */
+
+Node *preInsert(Node *, int,int,char *,int); /* inserts a new item at the top of the list */
+
+Node *orderInsert(Node *, int ,int,char *,int); /* inserts a new element in order, according to a key field */
+
+Node *postInsert(Node *, int ,int,char *,int); /* inserts a new item at the end of the list */
+
+Node *findNode(Node *, int ); /* find a node in the list */
+
+Node *deleteNode(Node *, int ); /* deletes a node corresponding to the inserted key */
+
+Node *deleteList(Node *); /* deletes a list */
+
+void printList(Node *); /* prints all the nodes in the list */
+
+void MergeSort(Node **); /* sorting algorithm */
+
+Node *Merge(Node *, Node *); /* merges two sorted linked lists */
+
+void Split(Node *, Node **, Node **); /* split the nodes of the list into two sublists */
+
+int countNodes(Node *); /* returns the number of nodes in the list */
+
+
+
+ Node* copyList( Node* originalList);
+
+ int countClockNodes_byCMD(Node *top,char type_cmd);
+
+
+Node *deleteNode_head(Node *top);
+
+
+
+
+
+
+
+
+
+#define MAX_MSG_ID_NUM 1000
+
+
+int allocateMsgIdNum();
+void deallocateMsgIdNum(int idNum);
+
+
+#if 0
+void wait_Send_Data_Insert(int msgid,uint8_t *data,int len);
+
+#endif
+
+void wait_Send_Data_Delete(int msgid);
+
+
+
+
+
+#endif

+ 1 - 0
components/esp_ble_ota/.component_hash

@@ -0,0 +1 @@
+0a15e3b070eecfae2b99e9dfc901b11c14ba67bac6e74057a7b9358a946cdc61

+ 60 - 0
components/esp_ble_ota/CHANGELOG.md

@@ -0,0 +1,60 @@
+# ChangeLog
+
+## v0.1.0 - 2023-01-04
+
+### Feature:
+
+* BLE-OTA:
+  * The initial version.
+
+## v0.1.1 - 2023-01-17
+
+### Enhancements:
+* BLE-OTA:
+  * Deleted subscribe command support, no longer needed for protocomm.
+
+  * Added finish command support to indicated end of OTA.
+
+## v0.1.2 - 2023-01-18
+
+* Fixed warnings
+
+## v0.1.3 - 2023-02-09
+
+* BLE-OTA:
+  * Added support for IDF release v4.4
+  * Fixed github ble_ota link in idf_component.yml
+
+## v0.1.4 - 2023-02-13
+
+* BLE-OTA:
+  * Added support for bluedroid BLE 5.0 features.
+
+## v0.1.5 - 2023-02-15
+
+* BLE-OTA:
+  * Added support for pre-encrypted OTA.
+
+## v0.1.6 - 2023-02-17
+
+* BLE-OTA:
+  * Added support for nimble extended advertising.
+
+## v0.1.7 - 2023-03-09
+
+* BLE-OTA:
+  * Use cu_pkg_define_version to define the version of this component.
+
+## v0.1.8 - 2023-03-31
+
+* BLE-OTA:
+  * Fixed errors for release v4.4
+
+## v0.1.9 - 2023-10-16
+
+* BLE-OTA:
+  * Add support for ESP32H2
+
+## v0.1.10 - 2023-11-23
+
+* Fix possible cmake_utilities dependency issue

+ 29 - 0
components/esp_ble_ota/CMakeLists.txt

@@ -0,0 +1,29 @@
+set(srcs "")
+set(priv_include_dirs "")
+set(requires bt nvs_flash protocomm esp_netif protobuf-c)
+set(priv_requires json esp_timer)
+
+if(CONFIG_BT_NIMBLE_ENABLED)
+    list(APPEND srcs "src/nimble_ota.c")
+
+    if(CONFIG_OTA_WITH_PROTOCOMM)    
+        list(APPEND srcs
+            "src/scheme_ble.c"
+            "src/manager.c"
+            "src/handlers.c"
+            "src/decrypt_ota_file.c"
+            "proto-c/ota_send_file.pb-c.c")
+        list(APPEND priv_include_dirs proto-c "${IDF_PATH}/components/protocomm/proto-c")
+    endif()
+else()  
+    list(APPEND srcs "src/ble_ota.c")
+endif()
+
+idf_component_register(SRCS "${srcs}"
+                       INCLUDE_DIRS "include"
+                       PRIV_INCLUDE_DIRS "${priv_include_dirs}"
+                       REQUIRES "${requires}"
+                       PRIV_REQUIRES "${priv_requires}")
+
+# include(package_manager)
+# cu_pkg_define_version(${CMAKE_CURRENT_LIST_DIR})

+ 25 - 0
components/esp_ble_ota/Kconfig

@@ -0,0 +1,25 @@
+menu "OTA Manager"
+    choice OTA_TYPE
+        prompt "Type of OTA"
+        default OTA_WITHOUT_SECURITY
+
+        config OTA_WITHOUT_SECURITY
+            bool "Enable OTA without security"
+            help
+                This transfers file without any security using OTA
+
+        config OTA_WITH_PROTOCOMM
+            bool
+            prompt "Enable protocomm level security"
+            depends on BT_NIMBLE_ENABLED
+            help
+                Used to enable protocomm level security for file transfer using OTA
+
+        config PRE_ENC_OTA
+            bool
+            prompt "Enable pre encrypted OTA"
+            depends on BT_NIMBLE_ENABLED
+            help
+                Used to enable transferring pre encrypted file via OTA
+    endchoice
+endmenu

+ 15 - 0
components/esp_ble_ota/README.md

@@ -0,0 +1,15 @@
+# ESP BLE OTA Component description
+
+``esp ble ota`` is a firmware upgrade component for data sending and receiving based on customized BLE Services. The firmware to be upgraded will be subcontracted by the client and transmitted sequentially. After receiving data from the client, packet sequence and CRC check will be checked and ACK will be returned.
+
+## Example
+
+- [examples/bluetooth/ble_ota](https://github.com/espressif/esp-iot-solution/tree/master/examples/bluetooth/ble_ota): This example is based on the ble_ota component, upgrade firmware via BLE.
+
+You can create a project from this example by the following command:
+
+```
+idf.py create-project-from-example "espressif/ble_ota^0.1.8:ble_ota"
+```
+
+> Note: For the examples downloaded by using this command, you need to comment out the override_path line in the main/idf_component.yml.

+ 7 - 0
components/esp_ble_ota/README_CN.md

@@ -0,0 +1,7 @@
+# ESP BLE OTA 组件说明
+
+``esp ble ota`` 是基于自定义 ``BLE Services`` 进行数据收发的固件升级组件,待升级固件会被主机分包之后依次传输,从机接收到数据之后进行包序列以及 CRC 校验并回复 ACK。
+
+## 从机示例代码
+
+[ESP BLE OTA](https://github.com/espressif/esp-iot-solution/tree/master/examples/bluetooth/ble_ota)

+ 6 - 0
components/esp_ble_ota/examples/ble_ota/CMakeLists.txt

@@ -0,0 +1,6 @@
+# The following lines of boilerplate have to be in your project's
+# CMakeLists in this exact order for cmake to work correctly
+cmake_minimum_required(VERSION 3.5)
+
+include($ENV{IDF_PATH}/tools/cmake/project.cmake)
+project(ble_ota)

+ 104 - 0
components/esp_ble_ota/examples/ble_ota/README.md

@@ -0,0 +1,104 @@
+# ESP BLE OTA Demo description
+
+``ble ota demo`` is based on the ``ble ota component``, it receives firmware via BLE and writes it to flash, sector by sector, until the upgrade is complete.
+
+## 1. Services definition
+
+The component contains two services:
+
+- `DIS Service`: Displays software and hardware version information
+- `OTA Service`: It is used for OTA upgrade and contains 4 characteristics, as shown in the following table:
+
+|  Characteristics   | UUID  |  Prop   | description  |
+|  ----  | ----  |  ----  | ----  |
+|  RECV_FW_CHAR | 0x8020 | Write, notify  | Firmware received, send ACK |
+|  PROGRESS_BAR_CHAR  | 0x8021 | Read, notify  | Read the progress bar and report the progress bar |
+|  COMMAND_CHAR  | 0x8022 | Write, notify  | Send the command and ACK |
+|  CUSTOMER_CHAR  | 0x8023 | Write, notify  | User-defined data to send and receive |
+
+## 2. Data transmission
+
+### 2.1 Command package format
+
+|  unit   | Command_ID  |  PayLoad   | CRC16  |
+|  ----  | ----  |  ----  | ----  |
+|  Byte | Byte: 0 ~ 1 | Byte: 2 ~ 17  | Byte: 18 ~ 19 |
+
+Command_ID:
+
+- 0x0001: Start OTA, Payload bytes(2 to 5), indicates the length of the firmware. Other Payload is set to 0 by default. CRC16 calculates bytes(0 to 17).
+- 0x0002: Stop OTA, and the remaining Payload will be set to 0. CRC16 calculates bytes(0 to 17).
+- 0x0003: The Payload bytes(2 or 3) is the payload of the Command_ID for which the response will be sent. Payload bytes(4 to 5) is a response to the command. 0x0000 indicates accept, 0x0001 indicates reject. Other payloads are set to 0. CRC16 computes bytes(0 to 17).
+
+### 2.2 Firmware package format
+
+The format of the firmware package sent by the client is as follows:
+
+|  unit   | Sector_Index  |  Packet_Seq   | PayLoad  |
+|  ----  | ----  |  ----  | ----  |
+|  Byte | Byte: 0 ~ 1 | Byte: 2  | Byte: 3 ~ (MTU_size - 4) |
+
+- Sector_Index:Indicates the number of sectors, sector number increases from 0, cannot jump, must be send 4K data and then start transmit the next sector, otherwise it will immediately send the error ACK for request retransmission.
+- Packet_Seq:If Packet_Seq is 0xFF, it indicates that this is the last packet of the sector, and the last 2 bytes of Payload is the CRC16 value of 4K data for the entire sector, the remaining bytes will set to 0x0. Server will check the total length and CRC of the data from the client, reply the correct ACK, and then start receive the next sector of firmware data.
+
+The format of the reply packet is as follows:
+
+|  unit   | Sector_Index  |  ACK_Status   | CRC6  |
+|  ----  | ----  |  ----  | ----  |
+|  Byte | Byte: 0 ~ 1 | Byte: 2 ~ 3  | Byte: 18 ~ 19 |
+
+ACK_Status:
+
+- 0x0000: Success
+- 0x0001: CRC error
+- 0x0002: Sector_Index error, bytes(4 ~ 5) indicates the desired Sector_Index
+- 0x0003:Payload length error
+
+## 3.  Sample APP
+
+[APP](https://github.com/EspressifApps/esp-ble-ota-android/releases/tag/rc)
+
+## 4. Sample code
+
+[ESP BLE OTA](https://github.com/espressif/esp-iot-solution/tree/master/examples/bluetooth/ble_ota)
+
+### 4.1 OTA update ESP32-H2
+
+To construct the BLE OTA demo for the ESP32-H2 device, ensure you're using ESP-IDF version 5.0 or later. Currently, the ESP32-H2 doesn't support NimBLE OTA updates, so omit the instructions in section 5. Maintain the default configuration for the Bluetooth framework, where `Bluedroid - Dual-mode` is selected. To ensure compatibility with the sample app mentioned in section 3, deactivate BLE 5.0 features by navigating to:
+
+1. `Component config` > `Bluetooth` > `Bluedroid Options` and deselect `Enable BLE 5.0 features`.
+2. `Component config` > `Bluetooth` > `Controller Options` and deselect `Enable BLE 5 feature`.
+
+## 5. NimBLE OTA
+
+#### Configure the Project
+
+`idf.py menuconfig`
+
+- Component config → Bluetooth → Bluetooth → Host → NimBLE - BLE only
+
+Note: For maximum throughput, set maximum MTU using
+
+- Component config → Bluetooth → NimBLE Options → Preferred MTU size in octets as 517
+
+### 5.1 OTA with Protocomm
+
+#### Configure the Project
+
+`idf.py menuconfig`
+
+- Example Configuration → Type of OTA → Use protocomm layer for security
+- Component config → OTA Manager → Type of OTA → Enable protocomm level security
+
+### 5.2 Pre encrypted OTA
+
+- For using pre encrypted OTA, the OTA file needs to be encrypted using [private key](../ble_ota/rsa_key/private.pem)
+- For more info regarding encrypting file refer : [esp_encrypted_img](https://github.com/espressif/idf-extra-components/tree/master/esp_encrypted_img)
+
+#### Configure the Project
+
+`idf.py menuconfig`
+
+- If using ESP32S3 set Component config → Bluetooth → Bluetooth → NimBLE Options → NimBLE Host task stack size as 8192 
+- Example Configuration → Type of OTA → Use Pre-Encrypted OTA
+- Component config → OTA Manager → Type of OTA → Enable pre encrypted OTA

+ 64 - 0
components/esp_ble_ota/examples/ble_ota/README_CN.md

@@ -0,0 +1,64 @@
+# ESP BLE OTA 示例说明
+
+``ble ota demo`` 基于 ``ble ota component``,通过 BLE 接收待升级的固件,然后以扇区为单位,依次写入 flash,直到升级完成。
+
+## 1. Services 定义
+ 
+组件包含两个服务:
+
+  - `DIS Service`:展示软硬件版本信息
+
+  - `OTA Service`:用于 OTA 升级,包含 4 个 characteristics,如下表所示:
+
+|  特征值   | UUID  |  权限   | 说明  |
+|  ----  | ----  |  ----  | ----  |
+|  RECV_FW_CHAR | 0x8020 | Write, notify  | 固件接收,回复ACK |
+|  PROGRESS_BAR_CHAR  | 0x8021 | Read, notify  | 读取进度条,上报进度条 |
+|  COMMAND_CHAR  | 0x8022 | Write, notify  | 命令下发与ACK回复 |
+|  CUSTOMER_CHAR  | 0x8023 | Write, notify  | ⽤用户⾃自定义数据收发 |
+
+## 2. 数据传输
+
+### 2.1 命令包格式
+
+|  单位   | Command_ID  |  PayLoad   | CRC16  |
+|  ----  | ----  |  ----  | ----  |
+|  字节 | Byte: 0 ~ 1 | Byte: 2 ~ 17  | Byte: 18 ~ 19 |
+
+Command_ID:
+
+  - 0x0001: 开始 OTA,Payload bytes(2 ~ 5),用于指示固件的长度信息,其余 Payload 默认置 0,CRC16 计算的是 bytes(0 ~ 17)。
+  - 0x0002: 结束 OTA,剩余 Payload 全部置 0,CRC16 计算的是 bytes(0 ~ 17)。
+  - 0x0003: 命令包的回复,Payload bytes(2 ~ 3),填充准备回复的 Command_ID;Payload bytes(4 ~ 5) 是对该命令的响应,0x0000 表示接受,0x0001 表示拒绝;其余 Payload 全部置 0,CRC16 计算的是 bytes(0 ~ 17)。
+
+### 2.2 固件包格式
+
+主机发送的固件包格式如下:
+
+|  单位   | Sector_Index  |  Packet_Seq   | PayLoad  |
+|  ----  | ----  |  ----  | ----  |
+|  字节 | Byte: 0 ~ 1 | Byte: 2  | Byte: 3 ~ (MTU_size - 4) |
+
+  - Sector_Index:指示写⼊的是第几个扇区,扇区号从 0 开始递增,不能跳跃,必须发送完 4K 数据才能开始下一个扇区,否则会⽴立刻回复错误,请求重传。
+  - Packet_Seq:如果 Packet_Seq 是 0xFF,表示这是这个扇区的最后⼀包,Payload 有效载荷的最后 2字节 是整个扇区 4K 数据的 CRC16 的值, 剩余字节填充 0x0。从机会校验数据的总⻓长度和 CRC,并给主机回复正确的 ACK,然后开始发送下一个扇区数据。
+
+从机回复主机固件包的应答包格式如下:
+
+|  单位   | Sector_Index  |  ACK_Status   | CRC6  |
+|  ----  | ----  |  ----  | ----  |
+|  字节 | Byte: 0 ~ 1 | Byte: 2 ~ 3  | Byte: 18 ~ 19 |
+
+ACK_Status:
+
+  - 0x0000:成功
+  - 0x0001:CRC 错误
+  - 0x0002:Sector_Index 错误,bytes(4 ~ 5) 表示期望的 Sector_Index
+  - 0x0003:Payload length 错误
+
+## 3. 主机示例程序
+
+[APP](https://github.com/EspressifApps/esp-ble-ota-android/releases/tag/rc)
+
+## 4. 从机示例代码
+
+[ESP BLE OTA](https://github.com/espressif/esp-iot-solution/tree/master/examples/bluetooth/ble_ota)

+ 9 - 0
components/esp_ble_ota/examples/ble_ota/main/CMakeLists.txt

@@ -0,0 +1,9 @@
+idf_build_get_property(project_dir PROJECT_DIR)
+idf_component_register(SRCS "app_main.c"
+                       EMBED_TXTFILES ${project_dir}/rsa_key/private.pem)
+
+
+create_esp_enc_img(${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME}.bin
+    ${project_dir}/rsa_key/private.pem ${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME}_secure.bin app)
+
+target_compile_options(${COMPONENT_LIB} PRIVATE -Wno-unused-const-variable)

+ 70 - 0
components/esp_ble_ota/examples/ble_ota/main/Kconfig.projbuild

@@ -0,0 +1,70 @@
+menu "Example Configuration"
+
+    choice EXAMPLE_OTA_SECURITY_VERSION
+        bool "Protocomm security version"
+        default EXAMPLE_OTA_SECURITY_VERSION_2
+        help
+            OTA component offers 3 security versions.
+            The example offers a choice between security version 1 and 2.
+
+        config EXAMPLE_OTA_SECURITY_VERSION_1
+            bool "Security version 1"
+            select ESP_PROTOCOMM_SUPPORT_SECURITY_VERSION_1
+
+        config EXAMPLE_OTA_SECURITY_VERSION_2
+            bool "Security version 2"
+            select ESP_PROTOCOMM_SUPPORT_SECURITY_VERSION_2
+    endchoice
+
+    choice EXAMPLE_OTA_MODE
+        bool "Security version 2 mode"
+        depends on EXAMPLE_OTA_SECURITY_VERSION_2
+        default EXAMPLE_OTA_SEC2_DEV_MODE
+
+        config EXAMPLE_OTA_SEC2_DEV_MODE
+            bool "Security version 2 development mode"
+            depends on EXAMPLE_OTA_SECURITY_VERSION_2
+            help
+                This enables the development mode for
+                security version 2.
+                Please note that this mode is NOT recommended for production purpose.
+
+        config EXAMPLE_OTA_SEC2_PROD_MODE
+            bool "Security version 2 production mode"
+            depends on EXAMPLE_OTA_SECURITY_VERSION_2
+            help
+                This enables the production mode for
+                security version 2.
+    endchoice
+
+    choice EXAMPLE_OTA_TYPE
+        prompt "Type of OTA"
+        default EXAMPLE_OTA_WITHOUT_SECURITY
+
+        config EXAMPLE_OTA_WITHOUT_SECURITY
+            bool "Use OTA without security"
+            help
+                This transfers file without any security using OTA
+
+        config EXAMPLE_USE_PROTOCOMM
+            bool "Use protocomm layer for security"
+            depends on BT_NIMBLE_ENABLED
+            help
+                This enables protocomm level security to transfer file via OTA
+
+        config EXAMPLE_USE_PRE_ENC_OTA
+            bool "Use Pre-Encrypted OTA"
+            depends on BT_NIMBLE_ENABLED
+            help
+                This enables transferring pre-encrypted file via OTA
+    endchoice
+
+    config EXAMPLE_EXTENDED_ADV
+        bool
+        default y if SOC_ESP_NIMBLE_CONTROLLER
+        select BT_NIMBLE_EXT_ADV
+        prompt "Enable Extended Adv"
+        depends on BT_NIMBLE_ENABLED && !IDF_TARGET_ESP32
+        help
+            Use this option to enable extended advertising in the example
+endmenu

+ 365 - 0
components/esp_ble_ota/examples/ble_ota/main/app_main.c

@@ -0,0 +1,365 @@
+/*
+ * SPDX-FileCopyrightText: 2019-2023 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <string.h>
+
+#include "freertos/FreeRTOS.h"
+#include "freertos/ringbuf.h"
+
+#include "esp_log.h"
+#include "esp_ota_ops.h"
+#include "nvs_flash.h"
+#include "esp_bt.h"
+#include "ble_ota.h"
+
+#define OTA_RINGBUF_SIZE                    8192
+#define OTA_TASK_SIZE                       8192
+
+static const char *TAG = "ESP_BLE_OTA";
+
+#if CONFIG_EXAMPLE_USE_PRE_ENC_OTA
+extern const char rsa_private_pem_start[] asm("_binary_private_pem_start");
+extern const char rsa_private_pem_end[]   asm("_binary_private_pem_end");
+esp_decrypt_handle_t decrypt_handle;
+#endif
+
+#ifdef CONFIG_EXAMPLE_USE_PROTOCOMM
+#include "manager.h"
+#include "scheme_ble.h"
+#include "esp_netif.h"
+
+#if CONFIG_EXAMPLE_OTA_SEC2_DEV_MODE
+static const char sec2_salt[] = {
+    0x03, 0x6e, 0xe0, 0xc7, 0xbc, 0xb9, 0xed, 0xa8, 0x4c, 0x9e, 0xac, 0x97, 0xd9, 0x3d, 0xec, 0xf4
+};
+
+static const char sec2_verifier[] = {
+    0x7c, 0x7c, 0x85, 0x47, 0x65, 0x08, 0x94, 0x6d, 0xd6, 0x36, 0xaf, 0x37, 0xd7, 0xe8, 0x91, 0x43,
+    0x78, 0xcf, 0xfd, 0x61, 0x6c, 0x59, 0xd2, 0xf8, 0x39, 0x08, 0x12, 0x72, 0x38, 0xde, 0x9e, 0x24,
+    0xa4, 0x70, 0x26, 0x1c, 0xdf, 0xa9, 0x03, 0xc2, 0xb2, 0x70, 0xe7, 0xb1, 0x32, 0x24, 0xda, 0x11,
+    0x1d, 0x97, 0x18, 0xdc, 0x60, 0x72, 0x08, 0xcc, 0x9a, 0xc9, 0x0c, 0x48, 0x27, 0xe2, 0xae, 0x89,
+    0xaa, 0x16, 0x25, 0xb8, 0x04, 0xd2, 0x1a, 0x9b, 0x3a, 0x8f, 0x37, 0xf6, 0xe4, 0x3a, 0x71, 0x2e,
+    0xe1, 0x27, 0x86, 0x6e, 0xad, 0xce, 0x28, 0xff, 0x54, 0x46, 0x60, 0x1f, 0xb9, 0x96, 0x87, 0xdc,
+    0x57, 0x40, 0xa7, 0xd4, 0x6c, 0xc9, 0x77, 0x54, 0xdc, 0x16, 0x82, 0xf0, 0xed, 0x35, 0x6a, 0xc4,
+    0x70, 0xad, 0x3d, 0x90, 0xb5, 0x81, 0x94, 0x70, 0xd7, 0xbc, 0x65, 0xb2, 0xd5, 0x18, 0xe0, 0x2e,
+    0xc3, 0xa5, 0xf9, 0x68, 0xdd, 0x64, 0x7b, 0xb8, 0xb7, 0x3c, 0x9c, 0xfc, 0x00, 0xd8, 0x71, 0x7e,
+    0xb7, 0x9a, 0x7c, 0xb1, 0xb7, 0xc2, 0xc3, 0x18, 0x34, 0x29, 0x32, 0x43, 0x3e, 0x00, 0x99, 0xe9,
+    0x82, 0x94, 0xe3, 0xd8, 0x2a, 0xb0, 0x96, 0x29, 0xb7, 0xdf, 0x0e, 0x5f, 0x08, 0x33, 0x40, 0x76,
+    0x52, 0x91, 0x32, 0x00, 0x9f, 0x97, 0x2c, 0x89, 0x6c, 0x39, 0x1e, 0xc8, 0x28, 0x05, 0x44, 0x17,
+    0x3f, 0x68, 0x02, 0x8a, 0x9f, 0x44, 0x61, 0xd1, 0xf5, 0xa1, 0x7e, 0x5a, 0x70, 0xd2, 0xc7, 0x23,
+    0x81, 0xcb, 0x38, 0x68, 0xe4, 0x2c, 0x20, 0xbc, 0x40, 0x57, 0x76, 0x17, 0xbd, 0x08, 0xb8, 0x96,
+    0xbc, 0x26, 0xeb, 0x32, 0x46, 0x69, 0x35, 0x05, 0x8c, 0x15, 0x70, 0xd9, 0x1b, 0xe9, 0xbe, 0xcc,
+    0xa9, 0x38, 0xa6, 0x67, 0xf0, 0xad, 0x50, 0x13, 0x19, 0x72, 0x64, 0xbf, 0x52, 0xc2, 0x34, 0xe2,
+    0x1b, 0x11, 0x79, 0x74, 0x72, 0xbd, 0x34, 0x5b, 0xb1, 0xe2, 0xfd, 0x66, 0x73, 0xfe, 0x71, 0x64,
+    0x74, 0xd0, 0x4e, 0xbc, 0x51, 0x24, 0x19, 0x40, 0x87, 0x0e, 0x92, 0x40, 0xe6, 0x21, 0xe7, 0x2d,
+    0x4e, 0x37, 0x76, 0x2f, 0x2e, 0xe2, 0x68, 0xc7, 0x89, 0xe8, 0x32, 0x13, 0x42, 0x06, 0x84, 0x84,
+    0x53, 0x4a, 0xb3, 0x0c, 0x1b, 0x4c, 0x8d, 0x1c, 0x51, 0x97, 0x19, 0xab, 0xae, 0x77, 0xff, 0xdb,
+    0xec, 0xf0, 0x10, 0x95, 0x34, 0x33, 0x6b, 0xcb, 0x3e, 0x84, 0x0f, 0xb9, 0xd8, 0x5f, 0xb8, 0xa0,
+    0xb8, 0x55, 0x53, 0x3e, 0x70, 0xf7, 0x18, 0xf5, 0xce, 0x7b, 0x4e, 0xbf, 0x27, 0xce, 0xce, 0xa8,
+    0xb3, 0xbe, 0x40, 0xc5, 0xc5, 0x32, 0x29, 0x3e, 0x71, 0x64, 0x9e, 0xde, 0x8c, 0xf6, 0x75, 0xa1,
+    0xe6, 0xf6, 0x53, 0xc8, 0x31, 0xa8, 0x78, 0xde, 0x50, 0x40, 0xf7, 0x62, 0xde, 0x36, 0xb2, 0xba
+};
+#endif
+
+static esp_err_t
+example_get_sec2_salt(const char **salt, uint16_t *salt_len)
+{
+#if CONFIG_EXAMPLE_OTA_SEC2_DEV_MODE
+    ESP_LOGI(TAG, "Development mode: using hard coded salt");
+    *salt = sec2_salt;
+    *salt_len = sizeof(sec2_salt);
+    return ESP_OK;
+#elif CONFIG_EXAMPLE_OTA_SEC2_PROD_MODE
+    ESP_LOGE(TAG, "Not implemented!");
+    return ESP_FAIL;
+#endif
+    return ESP_FAIL;
+}
+
+static esp_err_t
+example_get_sec2_verifier(const char **verifier, uint16_t *verifier_len)
+{
+#if CONFIG_EXAMPLE_OTA_SEC2_DEV_MODE
+    ESP_LOGI(TAG, "Development mode: using hard coded verifier");
+    *verifier = sec2_verifier;
+    *verifier_len = sizeof(sec2_verifier);
+    return ESP_OK;
+#elif CONFIG_EXAMPLE_OTA_SEC2_PROD_MODE
+    /* This code needs to be updated with appropriate implementation to provide verifier */
+    ESP_LOGE(TAG, "Not implemented!");
+    return ESP_FAIL;
+#endif
+    return ESP_FAIL;
+}
+
+/* Event handler for catching system events */
+static void
+event_handler(void *arg, esp_event_base_t event_base,
+              int32_t event_id, void *event_data)
+{
+    if (event_base == ESP_BLE_OTA_EVENT) {
+        switch (event_id) {
+        case OTA_FILE_RCV:
+            ESP_LOGD(TAG, "File received in appln layer :");
+            ota_recv_fw_cb(event_data, 4096);
+            break;
+        default:
+            break;
+        }
+    } else if (event_base == PROTOCOMM_TRANSPORT_BLE_EVENT) {
+        switch (event_id) {
+        case PROTOCOMM_TRANSPORT_BLE_CONNECTED:
+            ESP_LOGI(TAG, "BLE transport: Connected!");
+            break;
+        case PROTOCOMM_TRANSPORT_BLE_DISCONNECTED:
+            ESP_LOGI(TAG, "BLE transport: Disconnected!");
+            break;
+        default:
+            break;
+        }
+    }
+}
+
+static void
+get_device_service_name(char *service_name, size_t size)
+{
+    char *svc_name = "OTA_123456";
+    strlcpy(service_name, svc_name, size);
+}
+
+#endif /* CONFIG_EXAMPLE_USE_PROTOCOMM */
+
+static RingbufHandle_t s_ringbuf = NULL;
+
+bool
+ble_ota_ringbuf_init(uint32_t ringbuf_size)
+{
+    s_ringbuf = xRingbufferCreate(ringbuf_size, RINGBUF_TYPE_BYTEBUF);
+    if (s_ringbuf == NULL) {
+        return false;
+    }
+
+    return true;
+}
+
+size_t
+write_to_ringbuf(const uint8_t *data, size_t size)
+{
+    BaseType_t done = xRingbufferSend(s_ringbuf, (void *)data, size, (TickType_t)portMAX_DELAY);
+    if (done) {
+        return size;
+    } else {
+        return 0;
+    }
+}
+
+void
+ota_task(void *arg)
+{
+    esp_partition_t *partition_ptr = NULL;
+    esp_partition_t partition;
+    const esp_partition_t *next_partition = NULL;
+    esp_ota_handle_t out_handle = 0;
+
+    uint32_t recv_len = 0;
+    uint8_t *data = NULL;
+    size_t item_size = 0;
+    ESP_LOGI(TAG, "ota_task start");
+    // search ota partition
+    partition_ptr = (esp_partition_t *)esp_ota_get_boot_partition();
+    if (partition_ptr == NULL) {
+        ESP_LOGE(TAG, "boot partition NULL!\r\n");
+        goto OTA_ERROR;
+    }
+    if (partition_ptr->type != ESP_PARTITION_TYPE_APP) {
+        ESP_LOGE(TAG, "esp_current_partition->type != ESP_PARTITION_TYPE_APP\r\n");
+        goto OTA_ERROR;
+    }
+
+    if (partition_ptr->subtype == ESP_PARTITION_SUBTYPE_APP_FACTORY) {
+        partition.subtype = ESP_PARTITION_SUBTYPE_APP_OTA_0;
+    } else {
+        next_partition = esp_ota_get_next_update_partition(partition_ptr);
+        if (next_partition) {
+            partition.subtype = next_partition->subtype;
+        } else {
+            partition.subtype = ESP_PARTITION_SUBTYPE_APP_OTA_0;
+        }
+    }
+    partition.type = ESP_PARTITION_TYPE_APP;
+
+    partition_ptr = (esp_partition_t *)esp_partition_find_first(partition.type, partition.subtype, NULL);
+    if (partition_ptr == NULL) {
+        ESP_LOGE(TAG, "partition NULL!\r\n");
+        goto OTA_ERROR;
+    }
+
+    memcpy(&partition, partition_ptr, sizeof(esp_partition_t));
+    if (esp_ota_begin(&partition, OTA_SIZE_UNKNOWN, &out_handle) != ESP_OK) {
+        ESP_LOGE(TAG, "esp_ota_begin failed!\r\n");
+        goto OTA_ERROR;
+    }
+
+    ESP_LOGI(TAG, "wait for data from ringbuf! fw_len = %u", esp_ble_ota_get_fw_length());
+    /*deal with all receive packet*/
+    for (;;) {
+        data = (uint8_t *)xRingbufferReceive(s_ringbuf, &item_size, (TickType_t)portMAX_DELAY);
+
+        ESP_LOGI(TAG, "recv: %u, recv_total:%"PRIu32"\n", item_size, recv_len + item_size);
+        if (item_size != 0) {
+            if (esp_ota_write(out_handle, (const void *)data, item_size) != ESP_OK) {
+                ESP_LOGE(TAG, "esp_ota_write failed!\r\n");
+                goto OTA_ERROR;
+            }
+            recv_len += item_size;
+            vRingbufferReturnItem(s_ringbuf, (void *)data);
+
+            if (recv_len >= esp_ble_ota_get_fw_length()) {
+                break;
+            }
+        }
+    }
+
+    if (esp_ota_end(out_handle) != ESP_OK) {
+        ESP_LOGE(TAG, "esp_ota_end failed!\r\n");
+        goto OTA_ERROR;
+    }
+
+    if (esp_ota_set_boot_partition(&partition) != ESP_OK) {
+        ESP_LOGE(TAG, "esp_ota_set_boot_partition failed!\r\n");
+        goto OTA_ERROR;
+    }
+
+    esp_restart();
+
+OTA_ERROR:
+    ESP_LOGE(TAG, "OTA failed");
+    vTaskDelete(NULL);
+}
+
+void
+ota_recv_fw_cb(uint8_t *buf, uint32_t length)
+{
+    write_to_ringbuf(buf, length);
+}
+
+static void
+ota_task_init(void)
+{
+    xTaskCreate(&ota_task, "ota_task", OTA_TASK_SIZE, NULL, 5, NULL);
+    return;
+}
+
+void
+app_main(void)
+{
+    esp_err_t ret;
+    esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
+
+    // Initialize NVS
+    ret = nvs_flash_init();
+    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
+        ESP_ERROR_CHECK(nvs_flash_erase());
+        ret = nvs_flash_init();
+    }
+    ESP_ERROR_CHECK(ret);
+
+    if (!ble_ota_ringbuf_init(OTA_RINGBUF_SIZE)) {
+        ESP_LOGE(TAG, "%s init ringbuf fail", __func__);
+        return;
+    }
+
+#if CONFIG_EXAMPLE_USE_PROTOCOMM
+    /* Initialize TCP/IP */
+    ESP_ERROR_CHECK(esp_netif_init());
+
+    /* Initialize the event loop */
+    ESP_ERROR_CHECK(esp_event_loop_create_default());
+
+    /* Register our event handler for Wi-Fi, IP and Provisioning related events */
+    ESP_ERROR_CHECK(esp_event_handler_register(ESP_BLE_OTA_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL));
+    ESP_ERROR_CHECK(esp_event_handler_register(PROTOCOMM_TRANSPORT_BLE_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL));
+
+    /* Configuration for the ota manager */
+    esp_ble_ota_config_t config = {
+        .scheme = esp_ble_ota_scheme_ble,
+
+        .scheme_event_handler = ESP_BLE_OTA_SCHEME_BLE_EVENT_HANDLER_FREE_BTDM
+    };
+
+    /* Initialize ota manager with the
+     * configuration parameters set above */
+    ESP_ERROR_CHECK(esp_ble_ota_init(config));
+
+    char service_name[12];
+    get_device_service_name(service_name, sizeof(service_name));
+    esp_ble_ota_security_t security = ESP_BLE_OTA_SECURITY_2;
+
+#ifdef CONFIG_EXAMPLE_OTA_SECURITY_VERSION_1
+    security = ESP_BLE_OTA_SECURITY_1;
+
+    /* Do we want a proof-of-possession (ignored if Security 0 is selected):
+     *      - this should be a string with length > 0
+     *      - NULL if not used
+     */
+    const char *pop = "abcd1234";
+
+    /* This is the structure for passing security parameters
+     * for the protocomm security 1.
+     */
+    esp_ble_ota_security1_params_t *sec_params = pop;
+
+#elif CONFIG_EXAMPLE_OTA_SECURITY_VERSION_2
+    /* The username must be the same one, which has been used in the generation of salt and verifier */
+
+    esp_ble_ota_security2_params_t sec2_params = {};
+
+    ESP_ERROR_CHECK(example_get_sec2_salt(&sec2_params.salt, &sec2_params.salt_len));
+    ESP_ERROR_CHECK(example_get_sec2_verifier(&sec2_params.verifier, &sec2_params.verifier_len));
+
+    esp_ble_ota_security2_params_t *sec_params = &sec2_params;
+#endif
+    const char *service_key = NULL;
+
+    ESP_ERROR_CHECK(esp_ble_ota_start(security, (const void *) sec_params, service_name, service_key));
+#else
+    ESP_ERROR_CHECK(esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT));
+
+    ret = esp_bt_controller_init(&bt_cfg);
+    if (ret) {
+        ESP_LOGE(TAG, "%s enable controller failed: %s\n", __func__, esp_err_to_name(ret));
+        return;
+    }
+
+    ret = esp_bt_controller_enable(ESP_BT_MODE_BLE);
+    if (ret) {
+        ESP_LOGE(TAG, "%s enable controller failed: %s\n", __func__, esp_err_to_name(ret));
+        return;
+    }
+
+    if (esp_ble_ota_host_init() != ESP_OK) {
+        ESP_LOGE(TAG, "%s initialoze ble host fail: %s\n", __func__, esp_err_to_name(ret));
+        return;
+    }
+
+#if CONFIG_EXAMPLE_USE_PRE_ENC_OTA
+    esp_decrypt_cfg_t cfg = {};
+    cfg.rsa_pub_key = rsa_private_pem_start;
+    cfg.rsa_pub_key_len = rsa_private_pem_end - rsa_private_pem_start;
+    decrypt_handle = esp_encrypted_img_decrypt_start(&cfg);
+    if (!decrypt_handle) {
+        ESP_LOGE(TAG, "OTA upgrade failed");
+        vTaskDelete(NULL);
+    }
+
+    esp_ble_ota_recv_fw_data_callback(ota_recv_fw_cb, decrypt_handle);
+#else
+    esp_ble_ota_recv_fw_data_callback(ota_recv_fw_cb);
+#endif /* CONFIG_EXAMPLE_USE_PRE_ENC_OTA */
+
+#endif /* CONFIG_EXAMPLE_USE_PROTOCOMM */
+    ota_task_init();
+}

+ 6 - 0
components/esp_ble_ota/examples/ble_ota/main/idf_component.yml

@@ -0,0 +1,6 @@
+dependencies:
+  espressif/esp_encrypted_img: "^2.0.1"
+  idf: ">=4.4"
+  ble_ota:
+    version: "~0.1.8"
+    override_path: "../../../../components/bluetooth/ble_profiles/esp/ble_ota"

+ 8 - 0
components/esp_ble_ota/examples/ble_ota/partitions.csv

@@ -0,0 +1,8 @@
+# Name,   Type, SubType, Offset,  Size, Flags
+# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
+
+nvs,      data, nvs,     ,        0x4000,
+otadata,  data, ota,     ,        0x2000,
+phy_init, data, phy,     ,        0x1000,
+ota_0,    app,  ota_0,   ,        1500K,
+ota_1,    app,  ota_1,   ,        1500K,

+ 40 - 0
components/esp_ble_ota/examples/ble_ota/rsa_key/private.pem

@@ -0,0 +1,40 @@
+-----BEGIN PRIVATE KEY-----
+MIIG/QIBADANBgkqhkiG9w0BAQEFAASCBucwggbjAgEAAoIBgQCuJqGUYUJFQdLZ
+Jn4wmTUCcKufWlVrR47OyGLRDESkLElXufr57MTPUJaPAfepjnFCsgZ8E238VDSA
+z/UxsLqlnBq7Snw1JrHO5qgREdfkTsnrKLvHjJiLH8oC/zTcj+bcLoxCSsrdkHt8
+88yqMiHCG0tn4+afojGTVchcMmBaeyzEb0krZHSE/CmcOyeNB3XXLm0ZNuo81wkn
+t7PF5zVK8RcJm8kU+KTyau7qR7EEFxqEZ5buhu4rZdS0fhppbKGVGWFkedIg61jH
+JW3Ij5Z2UA+5yApU8lUafJv8N8HrVWGUnwWh/tuTki6bgXQ5PzMwRlfz5aBjd7H1
+cbmZX4kuDFBFOuMOSTZSM+bAAVcMKOruQFlQa5afs4zYtiFfCgvKSXst5KacWEWY
++9zduN5VQruKo/LRL+Aw8EuSTFcY1gyP0NEj4qzad3V1CTCZpgQNzxgmt127AYES
+K+XcL502g3FffJK9C8J/m1czOpRI8o2ERtab6hsaeBT3PEACQ4kCAwEAAQKCAX9h
+j8LKyAcYRYfu9GGSrZcaMYSeq+dZR6f5WslWfkj2BYQRDApOI8EeDfJKq2TTgw5w
+ZCC94c9RTNsc+WOjiB5dfivQ637UnUN/ZY3srNJa/3NJtR19l9uIFTGr/CI2bhIn
+HOyhNsVE2sGo0+wUbNanUiMsUGsi2E/C+ouvGt8KfCGC6n/95IkM9zdYZCCuy+nN
+uB87wPKjTX8TgRZ1XXxoP31oefMXNzSGROG+M0DidGRRSLcwFDyD6MhV//n2laQD
+ZAqmxHm6emZuiZ0sroThpvu6+ZddxY9VdFYPgJdS0w+uoSeojeFEklzySD36mes+
+DPSqamDVZ5x4oJeXW99wcCHoZRSV2AW+HMicx8ONx1m3aXegkjqWHUhslxwQazz7
+ckAIeRyasobW24CvM6Va0XvhyElakPB615S+qMCt7RQ6/bb+uTStc1bZdndB/EBR
+qMXguQ3KNwNAnZMX2KG3exGzuhKqXcdywirQCWXSV4kn0ZtNSrKuLpgJ/4913QKB
+wQDSBERHJB0Ypwrln+iPgxhQhI59ZDRnxEYWAQNO7lIbYVGoMvluChKXQKGG6guM
+RyDGTE/ykK6OQcHUBJaIQtV3tlu42SzAulNuFiIK3SJ3X0Jxwhd4wKoaD1mvw0N9
+Jp+R3nP80kSkYzIyNubwkyz/8tNOfNkm4sj+cIUmEMUKxSwQKx/CABzRYHFUauEX
+xmN4MoiEuzba10eeaZ0n0N91dwh0kJ6ILgYjYnumhEOTFytC2QMUfQgVoB7ZEz0t
+3eUCgcEA1EgJjYk+7neK4XvrL0xoUTjHzfFRhjEERJGbB7fWKotmCdTpB7OQVOlX
+M0W0qt5fJqWiK86LWFmWgbBCXFGSmo8+wLF2rEx/91ykn09EMtVue24DTBIdxlSN
+1D0IcmxUJolBiex9f4MyRrKACohR4hgduatWHM37AFBdGdedWcIsOxjGYYyGGOA+
+8YU0MJgP5tcnStMR4QYiAgiBc84NOS/lsNOpuc8bvi6JT/SQVL29S6erJp80F1qr
+fyE/S9TVAoHBAIMAhlaaAExQZku146shaxlOllNBHi9cDxvKpfWmUzEhE36HzW8G
+eXI/roIpm+p0s8z97SCxfzDdc7p84ik9B0sVRUGYtoHBuCkDRVjhd45erWOoXlvE
+UlbPHHK2qnJ2lTK4QEGEJ60i8zy/Ym7OX1OIdKaQ2iOXcyjDp0qHS2HcfA/Z1oV8
+7HzuW8L8qEvcRE/FtcKVxTHc95+o4Y7ki38KMQwE6d6oVsvbsqW7+yrVWPbpxzIQ
+i+pxZsyhY5gowQKBwEp/CVMxQfN/4i4lRbCwmL9ANOYx9NEp7Hx/uK8pta/ygZmb
+rTe1rUYPKTnmEXQRW8T9RX8oGcCkl+vlRjSNr+wrrvMClEn9Ar91NJRvxdgtDHum
+bOPKS8apL6i2znsRRyrK6kQTySrxiLhEtih8FHhKzEu3NB4hrx86FKIYB7FTMiB/
+HkdngDvEnnI1s84F3za8dJ6OmeylMVQTVhKk0gEqrOm0LhD1/J9uR2PWyLvkZkTh
+j2+bWTWE7UBNE6ByDQKBwQC2zIyNYJ+chgudYggXRZB36xtFEVbu4ROlWt9Dhrte
+oBHDHpJpihPC1llYXY8CLI1trujVjE5mWBsciUeFTtXum6SHR/mfhnr2UGWNKNrj
+puIcFflT8Ui5dnV2jY3bBKBBnk2KQ+Y6iwAhIFDli/eVyP10I1hs5AZ6WYSmc2iL
+Mlx868S6WPjwkiYywVgsU3Q3AL9iQjaupoz0sfX4O1JxPA2F1Y9KF5G8DHEhrw8P
+8rNxkpcxZmDW443kyS4gRsM=
+-----END PRIVATE KEY-----

+ 1 - 0
components/esp_ble_ota/examples/ble_ota/sdkconfig.ci.bluedroid

@@ -0,0 +1 @@
+CONFIG_BT_BLUEDROID_ENABLED=y

+ 1 - 0
components/esp_ble_ota/examples/ble_ota/sdkconfig.ci.nimble

@@ -0,0 +1 @@
+CONFIG_BT_NIMBLE_ENABLED=y

+ 22 - 0
components/esp_ble_ota/examples/ble_ota/sdkconfig.defaults

@@ -0,0 +1,22 @@
+# Override some defaults so BT stack is enabled
+# by default in this example
+CONFIG_BT_ENABLED=y
+CONFIG_BTDM_CTRL_MODE_BLE_ONLY=y
+CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=n
+CONFIG_BTDM_CTRL_MODE_BTDM=n
+
+#
+# Partition Table
+#
+# CONFIG_PARTITION_TABLE_SINGLE_APP is not set
+# CONFIG_PARTITION_TABLE_TWO_OTA is not set
+CONFIG_PARTITION_TABLE_CUSTOM=y
+CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
+CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
+CONFIG_PARTITION_TABLE_OFFSET=0x8000
+CONFIG_PARTITION_TABLE_MD5=y
+# end of Partition Table
+
+CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
+
+CONFIG_BT_BLE_42_FEATURES_SUPPORTED=y

+ 181 - 0
components/esp_ble_ota/include/ble_ota.h

@@ -0,0 +1,181 @@
+/*
+ * SPDX-FileCopyrightText: 2019-2023 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#pragma once
+
+#ifndef _BLE_OTA_H_
+#define _BLE_OTA_H_
+
+#ifdef CONFIG_PRE_ENC_OTA
+#include "esp_encrypted_img.h"
+#endif
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/**
+ * @brief BLE OAT receive data callback function type
+ * @param buf : The pointer to the receive data buffer.
+ * @param length : The length of receive data buffer.
+ */
+typedef void (* esp_ble_ota_recv_fw_cb_t)(uint8_t *buf, uint32_t length);
+
+/**
+ * @brief BLE OTA callback functions struct
+ */
+typedef struct esp_ble_ota_callback_funs {
+    esp_ble_ota_recv_fw_cb_t recv_fw_cb;    /*!< BLE OTA data receive callback function */
+} esp_ble_ota_callback_funs_t;
+
+/**
+ * @brief BLE OTA notification flags struct
+ */
+typedef struct esp_ble_ota_notification_check {
+    bool recv_fw_ntf_enable;        /*!< BLE OTA receive firmware characteristic */
+    bool process_bar_ntf_enable;    /*!< BLE OTA notify process bar characteristic */
+    bool command_ntf_enable;        /*!< BLE OTA command characteristic */
+    bool customer_ntf_enable;       /*!< BLE OTA customer data characteristic */
+} esp_ble_ota_notification_check_t;
+
+#if CONFIG_BT_BLUEDROID_ENABLED
+/// BLE OTA characteristics
+typedef enum {
+    RECV_FW_CHAR,
+    RECV_FW_CHAR_CCC,
+
+    OTA_STATUS_CHAR,
+    OTA_STATUS_CHAR_CCC,
+
+    CMD_CHAR,
+    CMD_CHAR_CCC,
+
+    CUS_CHAR,
+    CUS_CHAR_CCC,
+
+    INVALID_CHAR,
+} esp_ble_ota_char_t;
+
+/// BLE DIS characteristics
+typedef enum {
+    DIS_SVC_IDX,
+
+    DIS_MODEL_CHAR_IDX,
+    DIS_MODEL_CHAR_VAL_IDX,
+
+    DIS_SN_CHAR_IDX,
+    DIS_SN_CHAR_VAL_IDX,
+
+    DIS_FW_CHAR_IDX,
+    DIS_FW_CHAR_VAL_IDX,
+
+    DIS_IDX_NB,
+} esp_ble_dis_service_index_t;
+
+/// BLE OTA characteristics Index
+typedef enum {
+    OTA_SVC_IDX,
+
+    RECV_FW_CHAR_IDX,
+    RECV_FW_CHAR_VAL_IDX,
+    RECV_FW_CHAR_NTF_CFG,
+
+    OTA_STATUS_CHAR_IDX,
+    OTA_STATUS_CHAR_VAL_IDX,
+    OTA_STATUS_NTF_CFG,
+
+    CMD_CHAR_IDX,
+    CMD_CHAR_VAL_IDX,
+    CMD_CHAR_NTF_CFG,
+
+    CUS_CHAR_IDX,
+    CUS_CHAR_VAL_IDX,
+    CUS_CHAR_NTF_CFG,
+
+    OTA_IDX_NB,
+} esp_ble_ota_service_index_t;
+
+#else
+
+typedef enum {
+    RECV_FW_CHAR,
+    OTA_STATUS_CHAR,
+    CMD_CHAR,
+    CUS_CHAR,
+    INVALID_CHAR,
+} esp_ble_ota_char_t;
+
+typedef enum {
+    RECV_FW_CHAR_VAL_IDX,
+    OTA_STATUS_CHAR_VAL_IDX,
+    CMD_CHAR_VAL_IDX,
+    CUS_CHAR_VAL_IDX,
+} esp_ble_ota_service_index_t;
+
+/**
+ * @brief           This function is called to process write event on characteristics
+ *
+ * @return          void
+ *
+ */
+void esp_ble_ota_write(uint8_t *file, size_t length);
+
+/**
+ * @brief           This function is used to set total file size and each block size
+ *
+ * @return          void
+ *
+ */
+void esp_ble_ota_set_sizes(size_t file_size, size_t block_size);
+
+#endif
+/**
+ * @brief           This function is called to Initialization ble ota host
+ *
+ * @return
+ *                  - ESP_OK: success
+ *                  - other: failed
+ *
+ */
+esp_err_t esp_ble_ota_host_init(void);
+
+/**
+ * @brief           This function is called to register ble ota receive firmware data callback function
+ *
+ * @param[in]       callback : pointer to the application callback function.
+ *
+ * @return
+ *                  - ESP_OK: success
+ *                  - other: failed
+ *
+ */
+#ifdef CONFIG_PRE_ENC_OTA
+esp_err_t esp_ble_ota_recv_fw_data_callback(esp_ble_ota_recv_fw_cb_t callback,
+                                            esp_decrypt_handle_t esp_decrypt_handle);
+#else
+esp_err_t esp_ble_ota_recv_fw_data_callback(esp_ble_ota_recv_fw_cb_t callback);
+#endif
+
+/**
+ * @brief           This function is called to Initialization ble ota process
+ *
+ * @return
+ *                  - length of ota firmware
+ *
+ */
+unsigned int esp_ble_ota_get_fw_length(void);
+
+/**
+ * @brief           This function is called to indicate OTA end
+ *
+ */
+void esp_ble_ota_finish(void);
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 4 - 0
components/esp_ble_ota/include/esp_ble_ota.h

@@ -0,0 +1,4 @@
+// #ifndef _ESP_BLE_OTA_H_
+// #define _ESP_BLE_OTA_H_
+// void esp_ble_ota(void);
+// #endif /* _ESP_BLE_OTA_H_ */

+ 368 - 0
components/esp_ble_ota/include/manager.h

@@ -0,0 +1,368 @@
+/*
+ * SPDX-FileCopyrightText: 2019-2023 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#pragma once
+
+#ifndef _MANAGER_H_
+#define _MANAGER_H_
+
+#ifdef CONFIG_OTA_WITH_PROTOCOMM
+#include <protocomm.h>
+#include "esp_event.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ESP_EVENT_DECLARE_BASE(ESP_BLE_OTA_EVENT);
+
+typedef enum {
+    OTA_INIT,
+    OTA_DEINIT,
+    OTA_END,
+    OTA_FILE_RCV,
+} esp_ble_ota_cb_event_t;
+
+typedef void (*esp_ble_ota_cb_func_t)(void *user_data, esp_ble_ota_cb_event_t event, void *event_data);
+
+/**
+ * @brief   Event handler that is used by the manager while
+ *          ota service is active
+ */
+typedef struct {
+    /**
+     * Callback function to be executed on ota events
+     */
+    esp_ble_ota_cb_func_t event_cb;
+
+    /**
+     * User context data to pass as parameter to callback function
+     */
+    void *user_data;
+} esp_ble_ota_event_handler_t;
+
+/**
+ * @brief Event handler can be set to none if not used
+ */
+#define ESP_BLE_OTA_EVENT_HANDLER_NONE { \
+    .event_cb  = NULL,                 \
+    .user_data = NULL                  \
+}
+
+/**
+ * @brief   Structure for specifying the ota scheme to be
+ *          followed by the manager
+ *
+ * @note    Ready to use schemes are available:
+ *              - esp_ble_ota_scheme_ble     : for ota over BLE transport + GATT server
+ */
+typedef struct esp_ble_ota_scheme {
+    /**
+     * Function which is to be called by the manager when it is to
+     * start the ota service associated with a protocomm instance
+     * and a scheme specific configuration
+     */
+    esp_err_t (*ota_start) (protocomm_t *pc, void *config);
+
+    /**
+     * Function which is to be called by the manager to stop the
+     * ota service previously associated with a protocomm instance
+     */
+    esp_err_t (*ota_stop) (protocomm_t *pc);
+
+    /**
+     * Function which is to be called by the manager to generate
+     * a new configuration for the ota service, that is
+     * to be passed to ota_start()
+     */
+    void *(*new_config) (void);
+
+    /**
+     * Function which is to be called by the manager to delete a
+     * configuration generated using new_config()
+     */
+    void (*delete_config) (void *config);
+
+    /**
+     * Function which is to be called by the manager to set the
+     * service name and key values in the configuration structure
+     */
+    esp_err_t (*set_config_service) (void *config, const char *service_name, const char *service_key);
+
+    /**
+     * Function which is to be called by the manager to set a protocomm endpoint
+     * with an identifying name and UUID in the configuration structure
+     */
+    esp_err_t (*set_config_endpoint) (void *config, const char *endpoint_name, uint16_t uuid);
+
+} esp_ble_ota_scheme_t;
+
+/**
+ * @brief   Structure for specifying the manager configuration
+ */
+typedef struct {
+    /**
+     * Provisioning scheme to use. Following schemes are already available:
+     *     - esp_ble_ota_scheme_ble     : for ota over BLE transport + GATT server
+     */
+    esp_ble_ota_scheme_t scheme;
+
+    /**
+     * Event handler required by the scheme for incorporating scheme specific
+     * behavior while ota manager is running. Various options may be
+     * provided by the scheme for setting this field. Use ESP_BLE_OTA_EVENT_HANDLER_NONE
+     * when not used. When using scheme esp_ble_ota_scheme_ble, the following
+     * options are available:
+     *     - ESP_BLE_OTA_SCHEME_BLE_EVENT_HANDLER_FREE_BTDM
+     *     - ESP_BLE_OTA_SCHEME_BLE_EVENT_HANDLER_FREE_BLE
+     *     - ESP_BLE_OTA_SCHEME_BLE_EVENT_HANDLER_FREE_BT
+     */
+    esp_ble_ota_event_handler_t scheme_event_handler;
+
+    /**
+     * Event handler that can be set for the purpose of incorporating application
+     * specific behavior. Use ESP_BLE_OTA_EVENT_HANDLER_NONE when not used.
+     */
+    esp_ble_ota_event_handler_t app_event_handler;
+} esp_ble_ota_config_t;
+
+/**
+ * @brief   Security modes supported by the ota Manager.
+ *
+ * These are same as the security modes provided by protocomm
+ */
+typedef enum esp_ble_ota_security {
+#ifdef CONFIG_ESP_PROTOCOMM_SUPPORT_SECURITY_VERSION_0
+    /**
+     * No security (plain-text communication)
+     */
+    ESP_BLE_OTA_SECURITY_0 = 0,
+#endif
+#ifdef CONFIG_ESP_PROTOCOMM_SUPPORT_SECURITY_VERSION_1
+    /**
+     * This secure communication mode consists of
+     *   X25519 key exchange
+     * + proof of possession (pop) based authentication
+     * + AES-CTR encryption
+     */
+    ESP_BLE_OTA_SECURITY_1,
+#endif
+#ifdef CONFIG_ESP_PROTOCOMM_SUPPORT_SECURITY_VERSION_2
+    /**
+     * This secure communication mode consists of
+     *  SRP6a based authentication and key exchange
+     *  + AES-GCM encryption/decryption
+     */
+    ESP_BLE_OTA_SECURITY_2
+#endif
+} esp_ble_ota_security_t;
+
+/**
+ * @brief  Security 1 params structure
+ *         This needs to be passed when using ESP_BLE_OTA_SECURITY_1
+ */
+typedef const char esp_ble_ota_security1_params_t;
+
+/**
+ * @brief  Security 2 params structure
+ *         This needs to be passed when using ESP_BLE_OTA_SECURITY_2
+ */
+typedef protocomm_security2_params_t esp_ble_ota_security2_params_t;
+
+/**
+ * @brief   Initialize ota manager instance
+ *
+ * Configures the manager and allocates internal resources
+ *
+ * Configuration specifies the ota scheme (transport)
+ * and event handlers
+ *
+ * Event OTA_INIT is emitted right after initialization
+ * is complete
+ * @param[in] config Configuration structure
+ *
+ * @return
+ *  - ESP_OK      : Success
+ *  - ESP_FAIL    : Fail
+ */
+esp_err_t esp_ble_ota_init(esp_ble_ota_config_t config);
+
+/**
+ * @brief   Stop ota (if running) and release
+ *          resource used by the manager
+ *
+ * Event OTA_DEINIT is emitted right after de-initialization
+ * is finished
+ *
+ * If ota service is  still active when this API is called,
+ * it first stops the service, hence emitting OTA_END, and
+ * then performs the de-initialization
+ */
+void esp_ble_ota_deinit(void);
+
+/**
+ * @brief   Start ota service
+ *
+ * This starts the ota service according to the scheme
+ * configured at the time of initialization. For scheme :
+ * - ota_scheme_ble : This starts protocomm_ble, which internally initializes
+ *                          BLE transport and starts GATT server for handling
+ *                          ota requests
+ *
+ * Event OTA_START is emitted right after ota starts without failure
+ *
+ * @param[in] security      Specify which protocomm security scheme to use :
+ *                              - OTA_SECURITY_0 : For no security
+ *                              - OTA_SECURITY_1 : x25519 secure handshake for session
+ *                                establishment followed by AES-CTR encryption of ota messages
+ *                              - OTA_SECURITY_2:  SRP6a based authentication and key exchange
+ *                                followed by AES-GCM encryption/decryption of ota messages
+ * @param[in] ota_sec_params
+ *                          Pointer to security params (NULL if not needed).
+ *                          This is not needed for protocomm security 0
+ *                          This pointer should hold the struct of type
+ *                          ota_security1_params_t for protocomm security 1
+ *                          and ota_security2_params_t for protocomm security 2 respectively.
+ *                          This pointer and its contents should be valid till the ota service is
+ *                          running and has not been stopped or de-inited.
+
+ * @param[in] service_name  Unique name of the service. This translates to:
+ *                              - Device name when ota mode is BLE
+
+ * @param[in] service_key   Key required by client to access the service (NULL if not needed).
+ *                          This translates to:
+ *                              - ignored when ota mode is BLE
+ *
+ * @return
+ *  - ESP_OK      : OTA started successfully
+ *  - ESP_FAIL    : Failed to start ota service
+ *  - ESP_ERR_INVALID_STATE : OTA manager not initialized or already started
+ */
+esp_err_t esp_ble_ota_start(esp_ble_ota_security_t security, const void *esp_ble_ota_sec_params, const char *service_name, const char *service_key);
+
+/**
+ * @brief   Stop ota service
+ *
+ * If ota service is active, this API will initiate a process to stop
+ * the service and return. Once the service actually stops, the event OTA_END
+ * will be emitted.
+ *
+ * If esp_ble_ota_deinit() is called without calling this API first, it will
+ * automatically stop the ota service and emit the OTA_END, followed
+ * by OTA_DEINIT, before returning.
+ *
+ * This API will generally be used in the scenario when the main application
+ * has registered its own endpoints, and wishes that the ota service is stopped
+ * only when some protocomm command from the client side application is received.
+ *
+ * Calling this API inside an endpoint handler, with sufficient cleanup_delay,
+ * will allow the response / acknowledgment to be sent successfully before the
+ * underlying protocomm service is stopped.
+ *
+ * For straightforward cases, using this API is usually not necessary as
+ * ota is stopped automatically once OTA_CRED_SUCCESS is emitted.
+ */
+void esp_ble_ota_stop(void);
+
+/**
+ * @brief   Wait for ota service to finish
+ *
+ * Calling this API will block until ota service is stopped
+ * i.e. till event OTA_END is emitted.
+ *
+ * This will not block if ota is not started or not initialized.
+ */
+void esp_ble_ota_wait(void);
+
+/**
+ * @brief   Create an additional endpoint and allocate internal resources for it
+ *
+ * This API is to be called by the application if it wants to create an additional
+ * endpoint. All additional endpoints will be assigned UUIDs starting from 0xFF54
+ * and so on in the order of execution.
+ *
+ * protocomm handler for the created endpoint is to be registered later using
+ * esp_ble_ota_endpoint_register() after ota has started.
+ *
+ * @note    This API can only be called BEFORE ota is started
+ *
+ * @note    Additional endpoints can be used for configuring client provided
+ *          parameters, that are necessary for the
+ *          main application and hence must be set prior to starting the application
+ *
+ * @note    After session establishment, the additional endpoints must be targeted
+ *          first by the client side application before sending OTA configuration,
+ *          because once OTA configuration finishes the ota service is
+ *          stopped and hence all endpoints are unregistered
+ *
+ * @param[in] ep_name unique name of the endpoint
+ *
+ * @return
+ *  - ESP_OK      : Success
+ *  - ESP_FAIL    : Failure
+ */
+esp_err_t esp_ble_ota_endpoint_create(const char *ep_name);
+
+/**
+ * @brief   Register a handler for the previously created endpoint
+ *
+ * This API can be called by the application to register a protocomm handler
+ * to any endpoint that was created using esp_ble_ota_endpoint_create().
+ *
+ * @note    This API can only be called AFTER ota has started
+ *
+ * @note    Additional endpoints can be used for configuring client provided
+ *          parameters, that are necessary for the
+ *          main application and hence must be set prior to starting the application
+ *
+ * @note    After session establishment, the additional endpoints must be targeted
+ *          first by the client side application before sending OTA configuration,
+ *          because once OTA configuration finishes the ota service is
+ *          stopped and hence all endpoints are unregistered
+ *
+ * @param[in] ep_name   Name of the endpoint
+ * @param[in] handler   Endpoint handler function
+ * @param[in] user_ctx  User data
+ *
+ * @return
+ *  - ESP_OK      : Success
+ *  - ESP_FAIL    : Failure
+ */
+esp_err_t esp_ble_ota_endpoint_register(const char *ep_name,
+                                        protocomm_req_handler_t handler,
+                                        void *user_ctx);
+
+/**
+ * @brief   Unregister the handler for an endpoint
+ *
+ * This API can be called if the application wants to selectively
+ * unregister the handler of an endpoint while the OTA
+ * is still in progress.
+ *
+ * All the endpoint handlers are unregistered automatically when
+ * the OTA stops.
+ *
+ * @param[in] ep_name Name of the endpoint
+ */
+void esp_ble_ota_endpoint_unregister(const char *ep_name);
+
+/**
+ * @brief   Register the callback handler for writing data to partition
+ *
+ * This API is called by handlers once data is decrpyted and unpacked
+ * and to be written on OTA partition.
+ *
+ * @param[in] buf       Data to be written on OTA partition
+ * @param[in] length    Length of data to be written
+ */
+void ota_recv_fw_cb(uint8_t *buf, uint32_t length);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* CONFIG_OTA_WITH_PROTOCOMM */
+#endif

+ 57 - 0
components/esp_ble_ota/include/ota.h

@@ -0,0 +1,57 @@
+/*
+ * SPDX-FileCopyrightText: 2019-2023 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef _OTA_H_
+#define _OTA_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct ota_ctrl_handlers {
+    /**
+     * Handler function called when ota sending file is requested. Status
+     * is given the parameters :
+     *
+     * file (input) - file received through OTA
+     *
+     * length (input) - length of file received
+     */
+    esp_err_t (*ota_send_file)(uint8_t *file, size_t length);
+
+    /**
+     * Handler function called when ota start is requested. Status
+     * is given the parameters :
+     *
+     * file_size (input) - total size of the file
+     *
+     * block_size (input) - size of one block
+     *
+     * partition_name (input) - custom partition where data is to be written
+     */
+    esp_err_t (*ota_start_cmd)(size_t file_size, size_t block_size, char *partition_name);
+
+    /**
+     * Handler function called when ota is completed.
+     */
+    esp_err_t (*ota_finish_cmd)();
+} ota_handlers_t;
+
+/**
+ * @brief   Handler for ota requests
+ *
+ * This is to be registered as the `recv-fw`, 'ota-bar', 'ota-command',
+ * 'ota-customer' endpoint handler (protocomm `protocomm_req_handler_t`)
+ * using `protocomm_add_endpoint()`
+*/
+esp_err_t ota_handler(uint32_t session_id, const uint8_t *inbuf, ssize_t inlen,
+                      uint8_t **outbuf, ssize_t *outlen, void *priv_data);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 104 - 0
components/esp_ble_ota/include/scheme_ble.h

@@ -0,0 +1,104 @@
+/*
+ * SPDX-FileCopyrightText: 2019-2023 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#pragma once
+
+#ifndef _SCHEME_BLE_H_
+#define _SCHEME_BLE_H_
+
+#ifdef CONFIG_OTA_WITH_PROTOCOMM
+#include <protocomm.h>
+#include <protocomm_ble.h>
+
+#include "manager.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief   Scheme that can be used by manager for ota
+ *          over BLE transport with GATT server
+ */
+extern const esp_ble_ota_scheme_t esp_ble_ota_scheme_ble;
+
+/* This scheme specific event handler is to be used when application
+ * doesn't require BT and BLE after ota has finished */
+#define ESP_BLE_OTA_SCHEME_BLE_EVENT_HANDLER_FREE_BTDM {    \
+    .event_cb  = esp_ble_ota_scheme_ble_event_cb_free_btdm, \
+    .user_data = NULL                                     \
+}
+
+/* This scheme specific event handler is to be used when application
+ * doesn't require BLE to be active after ota has finished */
+#define ESP_BLE_OTA_SCHEME_BLE_EVENT_HANDLER_FREE_BLE {    \
+    .event_cb  = esp_ble_ota_scheme_ble_event_cb_free_ble, \
+    .user_data = NULL                                    \
+}
+
+/* This scheme specific event handler is to be used when application
+ * doesn't require BT to be active after ota has finished */
+#define ESP_BLE_OTA_SCHEME_BLE_EVENT_HANDLER_FREE_BT {    \
+    .event_cb  = esp_ble_ota_scheme_ble_event_cb_free_bt, \
+    .user_data = NULL                                   \
+}
+
+void esp_ble_ota_scheme_ble_event_cb_free_btdm(void *user_data, esp_ble_ota_cb_event_t event, void *event_data);
+void esp_ble_ota_scheme_ble_event_cb_free_ble (void *user_data, esp_ble_ota_cb_event_t event, void *event_data);
+void esp_ble_ota_scheme_ble_event_cb_free_bt  (void *user_data, esp_ble_ota_cb_event_t event, void *event_data);
+
+/**
+ * @brief   Set the 128 bit GATT service UUID used for ota
+ *
+ * This API is used to override the default 128 bit ota
+ * service UUID, which is 0000ffff-0000-1000-8000-00805f9b34fb.
+ *
+ * This must be called before starting ota, i.e. before
+ * making a call to esp_ble_ota_start(), otherwise
+ * the default UUID will be used.
+ *
+ * @note    The data being pointed to by the argument must be valid
+ *          atleast till ota is started. Upon start, the
+ *          manager will store an internal copy of this UUID, and
+ *          this data can be freed or invalidated afterwords.
+ *
+ * @param[in] uuid128  A custom 128 bit UUID
+ *
+ * @return
+ *  - ESP_OK              : Success
+ *  - ESP_ERR_INVALID_ARG : Null argument
+ */
+esp_err_t esp_ble_ota_scheme_ble_set_service_uuid(uint8_t *uuid128);
+
+/**
+ * @brief   Set manufacturer specific data in scan response
+ *
+ * This must be called before starting ota, i.e. before
+ * making a call to esp_ble_ota_start().
+ *
+ * @note    It is important to understand that length of custom manufacturer
+ *          data should be within limits. The manufacturer data goes into scan
+ *          response along with BLE device name. By default, BLE device name
+ *          length is of 11 Bytes, however it can vary as per application use
+ *          case. So, one has to honour the scan response data size limits i.e.
+ *          (mfg_data_len + 2) < 31 - (device_name_length + 2 ). If the
+ *          mfg_data length exceeds this limit, the length will be truncated.
+ *
+ * @param[in] mfg_data      Custom manufacturer data
+ * @param[in] mfg_data_len  Manufacturer data length
+ *
+ * @return
+ *  - ESP_OK              : Success
+ *  - ESP_ERR_INVALID_ARG : Null argument
+ */
+esp_err_t esp_ble_ota_scheme_ble_set_mfg_data(uint8_t *mfg_data, ssize_t mfg_data_len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* CONFIG_OTA_WITH_PROTOCOMM */
+#endif

+ 202 - 0
components/esp_ble_ota/license.txt

@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.

+ 46 - 0
components/esp_ble_ota/proto-c/constants.pb-c.c

@@ -0,0 +1,46 @@
+/* Generated by the protocol buffer compiler.  DO NOT EDIT! */
+/* Generated from: constants.proto */
+
+/* Do not generate deprecated warnings for self */
+#ifndef PROTOBUF_C__NO_DEPRECATED
+#define PROTOBUF_C__NO_DEPRECATED
+#endif
+
+#include "constants.pb-c.h"
+static const ProtobufCEnumValue status__enum_values_by_number[8] = {
+    { "Success", "STATUS__Success", 0 },
+    { "InvalidSecScheme", "STATUS__InvalidSecScheme", 1 },
+    { "InvalidProto", "STATUS__InvalidProto", 2 },
+    { "TooManySessions", "STATUS__TooManySessions", 3 },
+    { "InvalidArgument", "STATUS__InvalidArgument", 4 },
+    { "InternalError", "STATUS__InternalError", 5 },
+    { "CryptoError", "STATUS__CryptoError", 6 },
+    { "InvalidSession", "STATUS__InvalidSession", 7 },
+};
+static const ProtobufCIntRange status__value_ranges[] = {
+    {0, 0}, {0, 8}
+};
+static const ProtobufCEnumValueIndex status__enum_values_by_name[8] = {
+    { "CryptoError", 6 },
+    { "InternalError", 5 },
+    { "InvalidArgument", 4 },
+    { "InvalidProto", 2 },
+    { "InvalidSecScheme", 1 },
+    { "InvalidSession", 7 },
+    { "Success", 0 },
+    { "TooManySessions", 3 },
+};
+const ProtobufCEnumDescriptor status__descriptor = {
+    PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC,
+    "Status",
+    "Status",
+    "Status",
+    "",
+    8,
+    status__enum_values_by_number,
+    8,
+    status__enum_values_by_name,
+    1,
+    status__value_ranges,
+    NULL, NULL, NULL, NULL /* reserved[1234] */
+};

+ 0 - 0
components/esp_ble_ota/proto-c/constants.pb-c.h


Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác