avatar

松果工作室

欢迎光临

  • 首页
  • freeRTOS
  • LVGL
  • ESP
  • 开发手册
  • 快速笔记
  • 个人收藏
  • 时事记录
  • 考察日志
  • 工具
Home ESP32(十) BLE OTA
文章

ESP32(十) BLE OTA

Posted 17 days ago Updated 17 days ago
By YCP
206~265 min read

本质是利用创建2个新的GATTS服务,先传输OTA数据,之后进行升级

ble_ota.c创建了一些服务

#include <string.h>
#include "esp_idf_version.h"

#include "esp_log.h"
#include "ble_ota.h"
#include "esp_gap_ble_api.h"
#include "esp_gatts_api.h"
#include "esp_bt_main.h"


#define TAG  "ESP_BLE_OTA"
#define DEVICE_NAME  "ESP-C919"

#define OTA_PROFILE_NUM           3
#define OTA_PROFILE_APP_IDX       1
#define DIS_PROFILE_APP_IDX       2
#define BUF_LENGTH                4098

#define BLE_OTA_MAX_CHAR_VAL_LEN  600

#define BLE_OTA_START_CMD         0x0001
#define BLE_OTA_STOP_CMD          0x0002
#define BLE_OTA_ACK_CMD           0x0003

#define CHAR_DECLARATION_SIZE    (sizeof(uint8_t))

typedef enum {
    BLE_OTA_CMD_ACK = 0,        /*!< Command Ack */
    BLE_OTA_FW_ACK = 1,        /*!< Firmware Ack */
} ble_ota_ack_type_t;

typedef enum {
    BLE_OTA_CMD_SUCCESS = 0x0000,       /*!< Success Ack */
    BLE_OTA_REJECT = 0x0001,       /*!< Reject Cmd Ack */
} ble_ota_cmd_ack_status_t;

typedef enum {
    BLE_OTA_FW_SUCCESS = 0x0000,        /*!< Success */
    BLE_OTA_FW_CRC_ERR = 0x0001,        /*!< CRC error */
    BLE_OTA_FW_IND_ERR = 0x0002,        /*!< Sector Index error*/
    BLE_OTA_FW_LEN_ERR = 0x0003,        /*!< Payload length error*/
} ble_ota_fw_ack_statuc_t;

struct gatts_profile_inst {
    esp_gatts_cb_t gatts_cb;
    uint16_t gatts_if;
    uint16_t conn_id;
    uint16_t mtu_size;
};

#ifdef CONFIG_BT_BLE_50_FEATURES_SUPPORTED
#define EXT_ADV_HANDLE                            0
#define NUM_EXT_ADV_SET                           1
#define EXT_ADV_DURATION                          0
#define EXT_ADV_MAX_EVENTS                        0

static uint8_t ext_ota_adv_data[] = {
    0x02, 0x01, 0x06,
    0x03, 0x03, 0x18, 0x80, //UUID
    0x09, 0x09, 0x45, 0x53, 0x50, 0x2D, 0x43, 0x39, 0x31, 0x39, //ESP-C919
    0x0d, 0xff, 0xe5, 0x02, 0x01, 0x01, 0x27, 0x95, 0x01, 0x00, 0x00, 0x00, 0xff, 0xff
};

static esp_ble_gap_ext_adv_t ext_adv[1] = {
    [0] = {EXT_ADV_HANDLE, EXT_ADV_DURATION, EXT_ADV_MAX_EVENTS},
};

esp_ble_gap_ext_adv_params_t ext_ota_adv_params = {
    .type = ESP_BLE_GAP_SET_EXT_ADV_PROP_CONNECTABLE,
    .interval_min = 0x20,
    .interval_max = 0x20,
    .channel_map = ADV_CHNL_ALL,
    .filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY,
    .primary_phy = ESP_BLE_GAP_PHY_1M,
    .max_skip = 0,
    .secondary_phy = ESP_BLE_GAP_PHY_2M,
    .sid = 0,
    .scan_req_notif = false,
    .own_addr_type = BLE_ADDR_TYPE_PUBLIC,
    .tx_power = EXT_ADV_TX_PWR_NO_PREFERENCE,
};
#else
static esp_ble_adv_params_t ota_adv_params = {
        .adv_int_min        = 0x20,
        .adv_int_max        = 0x40,
        .adv_type           = ADV_TYPE_IND,
        .own_addr_type      = BLE_ADDR_TYPE_PUBLIC,
        .channel_map        = ADV_CHNL_ALL,
        .adv_filter_policy  = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY,
};

static const uint8_t ota_adv_data[31] = {
        0x02, 0x01, 0x06,
        0x03, 0x03, 0x18, 0x80, //UUID
        0x09, 0x09, 0x45, 0x53, 0x50, 0x2D, 0x43, 0x39, 0x31, 0x39, //ESP-C919
        0x0d, 0xff, 0xe5, 0x02, 0x01, 0x01, 0x27, 0x95, 0x01, 0x00, 0x00, 0x00, 0xff, 0xff
};

static const uint8_t ota_scan_rsp_data[31] = {
        0x02, 0x01, 0x06,
        0x03, 0x03, 0x18, 0x80, //UUID
        0x09, 0x09, 0x45, 0x53, 0x50, 0x2D, 0x43, 0x39, 0x31, 0x39, //ESP-C919
        0x0d, 0xff, 0xe5, 0x02, 0x01, 0x01, 0x27, 0x95, 0x01, 0x00, 0x00, 0x00, 0xff, 0xff
};
#endif

esp_ble_ota_callback_funs_t ota_cb_fun_t = {
        .recv_fw_cb = NULL
};

esp_ble_ota_notification_check_t ota_notification = {
        .recv_fw_ntf_enable = false,
        .process_bar_ntf_enable = false,
        .command_ntf_enable = false,
        .customer_ntf_enable = false,
};

static void
gatts_ota_profile_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param);

static void
gatts_dis_profile_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param);

/**
 * @brief           This function is called to send notification to remote device
 *
 * @param[in]       ota_char: the characteristic index which to be send
 * @param[in]       value: the pointer to the send value
 * @param[in]       length: the value length
 *
 * @return
 *                  - ESP_OK : success
 *                  - other  : failed
 */
esp_err_t esp_ble_ota_notification_data(esp_ble_ota_char_t ota_char, uint8_t *value, uint8_t length);

static struct gatts_profile_inst ota_profile_tab[OTA_PROFILE_NUM] = {
        [OTA_PROFILE_APP_IDX] = {
                .gatts_cb = gatts_ota_profile_event_handler,
                .gatts_if = ESP_GATT_IF_NONE,       /* Not get the gatt_if, so initial is ESP_GATT_IF_NONE */
                .conn_id  = 0xff,
                .mtu_size = 23,
        },
        [DIS_PROFILE_APP_IDX] = {
                .gatts_cb = gatts_dis_profile_event_handler,
                .gatts_if = ESP_GATT_IF_NONE,       /* Not get the gatt_if, so initial is ESP_GATT_IF_NONE */
                .conn_id  = 0xff,
                .mtu_size = 23,
        },
};

static bool start_ota = false;
static unsigned int ota_total_len = 0;
static unsigned int cur_sector = 0;
static unsigned int cur_packet = 0;
static uint8_t *fw_buf = NULL;
static unsigned int fw_buf_offset = 0;
static uint8_t *temp_prep_write_buf = NULL;
static unsigned int temp_buf_len = 0;

static const uint16_t primary_service_uuid = ESP_GATT_UUID_PRI_SERVICE;
static const uint16_t character_declaration_uuid = ESP_GATT_UUID_CHAR_DECLARE;
static const uint16_t character_client_config_uuid = ESP_GATT_UUID_CHAR_CLIENT_CONFIG;

static const uint8_t char_prop_read = ESP_GATT_CHAR_PROP_BIT_READ;
static const uint8_t char_prop_read_indicate = ESP_GATT_CHAR_PROP_BIT_READ | ESP_GATT_CHAR_PROP_BIT_INDICATE;
static const uint8_t char_prop_write_indicate = ESP_GATT_CHAR_PROP_BIT_WRITE | ESP_GATT_CHAR_PROP_BIT_INDICATE;

static const uint16_t BLE_OTA_SERVICE_UUID = 0x8018;

static const uint16_t RECV_FW_UUID = 0x8020;
static const uint16_t OTA_BAR_UUID = 0x8021;
static const uint16_t COMMAND_UUID = 0x8022;
static const uint16_t CUSTOMER_UUID = 0x8023;

static uint8_t receive_fw_val[BLE_OTA_MAX_CHAR_VAL_LEN] = {0};
static uint8_t receive_fw_val_ccc[2] = {0x00, 0x00};

static uint8_t ota_status_val[20] = {0};
static uint8_t ota_status_val_ccc[2] = {0x00, 0x00};

static uint8_t command_val[20] = {0};
static uint8_t command_val_ccc[2] = {0x00, 0x00};

static uint8_t custom_val[20] = {0};
static uint8_t custom_val_ccc[2] = {0x00, 0x00};

static uint16_t ota_handle_table[OTA_IDX_NB];

/* OTA Full Database Description - Used to add attributes into the database */
static const esp_gatts_attr_db_t ota_gatt_db[OTA_IDX_NB] = {
        // Service Declaration
        [OTA_SVC_IDX]        =
                {{ESP_GATT_AUTO_RSP},
                 {
                  ESP_UUID_LEN_16, (uint8_t *) &primary_service_uuid, ESP_GATT_PERM_READ,
                         sizeof(BLE_OTA_SERVICE_UUID), sizeof(BLE_OTA_SERVICE_UUID), (uint8_t *) &BLE_OTA_SERVICE_UUID
                 }
                },

        /* Characteristic Declaration */
        [RECV_FW_CHAR_IDX]      =
                {{ESP_GATT_AUTO_RSP},
                 {
                  ESP_UUID_LEN_16, (uint8_t *) &character_declaration_uuid, ESP_GATT_PERM_READ,
                         CHAR_DECLARATION_SIZE, CHAR_DECLARATION_SIZE, (uint8_t *) &char_prop_write_indicate
                 }
                },

        /* Characteristic Value */
        [RECV_FW_CHAR_VAL_IDX]  =
                {{ESP_GATT_AUTO_RSP},
                 {
                  ESP_UUID_LEN_16, (uint8_t *) &RECV_FW_UUID, ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE,
                         sizeof(receive_fw_val), sizeof(receive_fw_val), (uint8_t *) receive_fw_val
                 }
                },

        //data notify characteristic Declaration
        [RECV_FW_CHAR_NTF_CFG]  =
                {{ESP_GATT_AUTO_RSP},
                 {
                  ESP_UUID_LEN_16, (uint8_t *) &character_client_config_uuid, ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE,
                         sizeof(receive_fw_val_ccc), sizeof(receive_fw_val_ccc), (uint8_t *) receive_fw_val_ccc
                 }
                },

        //data receive characteristic Declaration
        [OTA_STATUS_CHAR_IDX]            =
                {{ESP_GATT_AUTO_RSP},
                 {
                  ESP_UUID_LEN_16, (uint8_t *) &character_declaration_uuid, ESP_GATT_PERM_READ,
                         CHAR_DECLARATION_SIZE, CHAR_DECLARATION_SIZE, (uint8_t *) &char_prop_read_indicate
                 }
                },

        //data receive characteristic Value
        [OTA_STATUS_CHAR_VAL_IDX]               =
                {{ESP_GATT_AUTO_RSP},
                 {
                  ESP_UUID_LEN_16, (uint8_t *) &OTA_BAR_UUID, ESP_GATT_PERM_READ,
                         sizeof(ota_status_val), sizeof(ota_status_val), (uint8_t *) ota_status_val
                 }
                },

        //data notify characteristic Declaration
        [OTA_STATUS_NTF_CFG]  =
                {{ESP_GATT_AUTO_RSP},
                 {
                  ESP_UUID_LEN_16, (uint8_t *) &character_client_config_uuid, ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE,
                         sizeof(ota_status_val_ccc), sizeof(ota_status_val_ccc), (uint8_t *) ota_status_val_ccc
                 }
                },

        //data receive characteristic Declaration
        [CMD_CHAR_IDX]            =
                {{ESP_GATT_AUTO_RSP},
                 {
                  ESP_UUID_LEN_16, (uint8_t *) &character_declaration_uuid, ESP_GATT_PERM_READ,
                         CHAR_DECLARATION_SIZE, CHAR_DECLARATION_SIZE, (uint8_t *) &char_prop_write_indicate
                 }
                },

        //data receive characteristic Value
        [CMD_CHAR_VAL_IDX]              =
                {{ESP_GATT_AUTO_RSP},
                 {
                  ESP_UUID_LEN_16, (uint8_t *) &COMMAND_UUID, ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE,
                         sizeof(command_val), sizeof(command_val), (uint8_t *) command_val
                 }
                },

        //data notify characteristic Declaration
        [CMD_CHAR_NTF_CFG]  =
                {{ESP_GATT_AUTO_RSP},
                 {
                  ESP_UUID_LEN_16, (uint8_t *) &character_client_config_uuid, ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE,
                         sizeof(command_val_ccc), sizeof(command_val_ccc), (uint8_t *) command_val_ccc
                 }
                },

        //data receive characteristic Declaration
        [CUS_CHAR_IDX]            =
                {{ESP_GATT_AUTO_RSP},
                 {
                  ESP_UUID_LEN_16, (uint8_t *) &character_declaration_uuid, ESP_GATT_PERM_READ,
                         CHAR_DECLARATION_SIZE, CHAR_DECLARATION_SIZE, (uint8_t *) &char_prop_write_indicate
                 }
                },

        //data receive characteristic Value
        [CUS_CHAR_VAL_IDX]              =
                {{ESP_GATT_AUTO_RSP},
                 {
                  ESP_UUID_LEN_16, (uint8_t *) &CUSTOMER_UUID, ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE,
                         sizeof(custom_val), sizeof(custom_val), (uint8_t *) custom_val
                 }
                },

        //data notify characteristic Declaration
        [CUS_CHAR_NTF_CFG]  =
                {{ESP_GATT_AUTO_RSP},
                 {
                  ESP_UUID_LEN_16, (uint8_t *) &character_client_config_uuid, ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE,
                         sizeof(custom_val_ccc), sizeof(custom_val_ccc), (uint8_t *) custom_val_ccc
                 }
                },

};

static const uint16_t DIS_SERVICE_UUID = 0x180A;
static const uint16_t DIS_MODEL_CHAR_UUID = 0x2A24;
static const uint16_t DIS_SN_CHAR_UUID = 0x2A25;
static const uint16_t DIS_FW_CHAR_UUID = 0x2A26;

static uint8_t dis_model_value[] = "Espressif";
static uint8_t dis_sn_value[] = "esp-ota";
static uint8_t dis_fw_value[] = "1.0";

static uint16_t dis_handle_table[DIS_IDX_NB];

/* DIS Full Database Description - Used to add attributes into the database */
static const esp_gatts_attr_db_t dis_gatt_db[DIS_IDX_NB] = {
        // Service Declaration
        [DIS_SVC_IDX]        =
                {{ESP_GATT_AUTO_RSP},
                 {
                  ESP_UUID_LEN_16, (uint8_t *) &primary_service_uuid, ESP_GATT_PERM_READ,
                         sizeof(DIS_SERVICE_UUID), sizeof(DIS_SERVICE_UUID), (uint8_t *) &DIS_SERVICE_UUID
                 }
                },

        /* Characteristic Declaration */
        [DIS_MODEL_CHAR_IDX]      =
                {{ESP_GATT_AUTO_RSP},
                 {
                  ESP_UUID_LEN_16, (uint8_t *) &character_declaration_uuid, ESP_GATT_PERM_READ,
                         CHAR_DECLARATION_SIZE, CHAR_DECLARATION_SIZE, (uint8_t *) &char_prop_read
                 }
                },

        /* Characteristic Value */
        [DIS_MODEL_CHAR_VAL_IDX]  =
                {{ESP_GATT_AUTO_RSP},
                 {
                  ESP_UUID_LEN_16, (uint8_t *) &DIS_MODEL_CHAR_UUID, ESP_GATT_PERM_READ,
                         sizeof(dis_model_value), sizeof(dis_model_value), (uint8_t *) dis_model_value
                 }
                },

        /* Characteristic Declaration */
        [DIS_SN_CHAR_IDX]      =
                {{ESP_GATT_AUTO_RSP},
                 {
                  ESP_UUID_LEN_16, (uint8_t *) &character_declaration_uuid, ESP_GATT_PERM_READ,
                         CHAR_DECLARATION_SIZE, CHAR_DECLARATION_SIZE, (uint8_t *) &char_prop_read
                 }
                },

        /* Characteristic Value */
        [DIS_SN_CHAR_VAL_IDX]  =
                {{ESP_GATT_AUTO_RSP},
                 {
                  ESP_UUID_LEN_16, (uint8_t *) &DIS_SN_CHAR_UUID, ESP_GATT_PERM_READ,
                         sizeof(dis_sn_value), sizeof(dis_sn_value), (uint8_t *) dis_sn_value
                 }
                },

        /* Characteristic Declaration */
        [DIS_FW_CHAR_IDX]      =
                {{ESP_GATT_AUTO_RSP},
                 {
                  ESP_UUID_LEN_16, (uint8_t *) &character_declaration_uuid, ESP_GATT_PERM_READ,
                         CHAR_DECLARATION_SIZE, CHAR_DECLARATION_SIZE, (uint8_t *) &char_prop_read
                 }
                },

        /* Characteristic Value */
        [DIS_FW_CHAR_VAL_IDX]  =
                {{ESP_GATT_AUTO_RSP},
                 {
                  ESP_UUID_LEN_16, (uint8_t *) &DIS_FW_CHAR_UUID, ESP_GATT_PERM_READ,
                         sizeof(dis_fw_value), sizeof(dis_fw_value), (uint8_t *) dis_fw_value
                 }
                },
};

static uint16_t crc16_ccitt(const unsigned char *buf, int len) {
    uint16_t crc16 = 0;
    int32_t i;

    while (len--) {
        crc16 ^= *buf++ << 8;

        for (i = 0; i < 8; i++) {
            if (crc16 & 0x8000) {
                crc16 = (crc16 << 1) ^ 0x1021;
            } else {
                crc16 = crc16 << 1;
            }
        }
    }

    return crc16;
}

void esp_ble_ota_set_fw_length(unsigned int length) {
    ota_total_len = length;
}

unsigned int esp_ble_ota_get_fw_length(void) {
    return ota_total_len;
}

static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) {
    esp_bd_addr_t bd_addr;

    ESP_LOGD(TAG, "GAP_EVT, event %d\n", event);

    switch (event) {
#ifdef CONFIG_BT_BLE_50_FEATURES_SUPPORTED
        case ESP_GAP_BLE_EXT_ADV_SET_PARAMS_COMPLETE_EVT:
            esp_ble_gap_config_ext_adv_data_raw(EXT_ADV_HANDLE,  sizeof(ext_ota_adv_data), &ext_ota_adv_data[0]);
            break;
        case ESP_GAP_BLE_EXT_ADV_DATA_SET_COMPLETE_EVT:
            esp_ble_gap_ext_adv_start(NUM_EXT_ADV_SET, &ext_adv[0]);
            break;
        case ESP_GAP_BLE_EXT_ADV_START_COMPLETE_EVT:
            ESP_LOGI(TAG, "Ext adv start, status = %d", param->ext_adv_data_set.status);
            break;
#else
        case ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT:
            esp_ble_gap_start_advertising(&ota_adv_params);
            break;
        case ESP_GAP_BLE_ADV_START_COMPLETE_EVT:
            //advertising start complete event to indicate advertising start successfully or failed
            if (param->adv_start_cmpl.status != ESP_BT_STATUS_SUCCESS) {
                ESP_LOGE(TAG, "Advertising start failed\n");
            }
            break;
#endif
        case ESP_GAP_BLE_SEC_REQ_EVT:
            for (int i = 0; i < ESP_BD_ADDR_LEN; i++) {
                ESP_LOGI(TAG, "%x:", param->ble_security.ble_req.bd_addr[i]);
            }
            esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr, true);
            break;
        case ESP_GAP_BLE_AUTH_CMPL_EVT:
            memcpy(bd_addr, param->ble_security.auth_cmpl.bd_addr, sizeof(esp_bd_addr_t));
            ESP_LOGI(TAG, "remote BD_ADDR: %08x%04x", \
                 (bd_addr[0] << 24) + (bd_addr[1] << 16) + (bd_addr[2] << 8) + bd_addr[3],
                     (bd_addr[4] << 8) + bd_addr[5]);
            ESP_LOGI(TAG, "address type = %d", param->ble_security.auth_cmpl.addr_type);
            ESP_LOGI(TAG, "pair status = %s", param->ble_security.auth_cmpl.success ? "success" : "fail");
            if (!param->ble_security.auth_cmpl.success) {
                ESP_LOGE(TAG, "fail reason = 0x%x", param->ble_security.auth_cmpl.fail_reason);
            }
            break;
        case ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT:
            ESP_LOGI(TAG,
                     "update connection params status = %d, min_int = %d, max_int = %d,conn_int = %d,latency = %d, timeout = %d",
                     param->update_conn_params.status,
                     param->update_conn_params.min_int,
                     param->update_conn_params.max_int,
                     param->update_conn_params.conn_int,
                     param->update_conn_params.latency,
                     param->update_conn_params.timeout);
            break;
        default:
            break;
    }
}

static void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) {
    // ESP_LOGI(TAG, "EVT %d, gatts if %d\n", event, gatts_if);

    /* If event is register event, store the gatts_if for each profile */
    if (event == ESP_GATTS_REG_EVT) {
        if (param->reg.status == ESP_GATT_OK) {
            ota_profile_tab[param->reg.app_id].gatts_if = gatts_if;
        } else {
            ESP_LOGE(TAG, "Reg app failed, app_id %04x, status %d\n", param->reg.app_id, param->reg.status);
            return;
        }
    }

    do {
        int idx;
        for (idx = 0; idx < OTA_PROFILE_NUM; idx++) {
            if (gatts_if == ESP_GATT_IF_NONE ||
                /* ESP_GATT_IF_NONE, not specify a certain gatt_if, need to call every profile cb function */
                gatts_if == ota_profile_tab[idx].gatts_if) {
                if (ota_profile_tab[idx].gatts_cb) {
                    ota_profile_tab[idx].gatts_cb(event, gatts_if, param);
                }
            }
        }
    } while (0);
}

static void gatts_dis_profile_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) {
    esp_err_t ret;

    ESP_LOGD(TAG, "%s - event: %d", __func__, event);

    switch (event) {
        case ESP_GATTS_REG_EVT:
            ret = esp_ble_gatts_create_attr_tab(dis_gatt_db, gatts_if, DIS_IDX_NB, DIS_PROFILE_APP_IDX);
            if (ret) {
                ESP_LOGE(TAG, "%s - create attr table failed, error code = %x", __func__, ret);
            }
            break;
        case ESP_GATTS_READ_EVT:
            ESP_LOGI(TAG, "DIS ESP_GATTS_READ_EVT");
            break;
        case ESP_GATTS_WRITE_EVT:
            ESP_LOGI(TAG, "DIS ESP_GATTS_WRITE_EVT");
            break;
        case ESP_GATTS_EXEC_WRITE_EVT:
            ESP_LOGI(TAG, "DIS ESP_GATTS_EXEC_WRITE_EVT");
            break;
        case ESP_GATTS_MTU_EVT:
            ESP_LOGI(TAG, "DIS ESP_GATTS_MTU_EVT  mtu = %d", param->mtu.mtu);
            break;
        case ESP_GATTS_CONF_EVT:
            break;
        case ESP_GATTS_START_EVT:
            ESP_LOGI(TAG, "DIS SERVICE_START_EVT, status %d, service_handle %d", param->start.status,
                     param->start.service_handle);
            if (param->start.status != ESP_GATT_OK) {
                ESP_LOGE(TAG, "SERVICE START FAIL, status %d", param->start.status);
                break;
            }
            break;
        case ESP_GATTS_CONNECT_EVT:
            break;
        case ESP_GATTS_DISCONNECT_EVT:
            break;
        case ESP_GATTS_CREAT_ATTR_TAB_EVT:
            if (param->add_attr_tab.status != ESP_GATT_OK) {
                ESP_LOGE(TAG, "dis create attribute table failed, error code=0x%x", param->add_attr_tab.status);
            } else if (param->add_attr_tab.num_handle != DIS_IDX_NB) {
                ESP_LOGE(TAG, "dis create attribute table abnormally, num_handle (%d) doesn't equal to DIS_IDX_NB(%d)",
                         param->add_attr_tab.num_handle, DIS_IDX_NB);
            } else {
                ESP_LOGI(TAG, "dis create attribute table successfully, the number handle = %d\n",
                         param->add_attr_tab.num_handle);
                memcpy(dis_handle_table, param->add_attr_tab.handles, sizeof(dis_handle_table));
                esp_ble_gatts_start_service(dis_handle_table[DIS_SVC_IDX]);
            }
            break;
        case ESP_GATTS_STOP_EVT:
            break;
        case ESP_GATTS_OPEN_EVT:
            break;
        case ESP_GATTS_CANCEL_OPEN_EVT:
            break;
        case ESP_GATTS_CLOSE_EVT:
            break;
        case ESP_GATTS_LISTEN_EVT:
            break;
        case ESP_GATTS_CONGEST_EVT:
            break;
        case ESP_GATTS_UNREG_EVT:
            break;
        case ESP_GATTS_DELETE_EVT:
            break;
        default:
            break;
    }
}

static esp_ble_ota_char_t find_ota_char_and_desr_by_handle(uint16_t handle) {
    esp_ble_ota_char_t ret = INVALID_CHAR;

    for (int i = 0; i < OTA_IDX_NB; i++) {
        if (handle == ota_handle_table[i]) {
            switch (i) {
                case RECV_FW_CHAR_VAL_IDX:
                    ret = RECV_FW_CHAR;
                    break;
                case RECV_FW_CHAR_NTF_CFG:
                    ret = RECV_FW_CHAR_CCC;
                    break;
                case OTA_STATUS_CHAR_VAL_IDX:
                    ret = OTA_STATUS_CHAR;
                    break;
                case OTA_STATUS_NTF_CFG:
                    ret = OTA_STATUS_CHAR_CCC;
                    break;
                case CMD_CHAR_VAL_IDX:
                    ret = CMD_CHAR;
                    break;
                case CMD_CHAR_NTF_CFG:
                    ret = CMD_CHAR_CCC;
                    break;
                case CUS_CHAR_VAL_IDX:
                    ret = CUS_CHAR;
                    break;
                case CUS_CHAR_NTF_CFG:
                    ret = CUS_CHAR_CCC;
                    break;
                default:
                    ret = INVALID_CHAR;
                    break;
            }
        }
    }

    return ret;
}

esp_err_t esp_ble_ota_recv_fw_handler(uint8_t *buf, unsigned int length) {
    if (ota_cb_fun_t.recv_fw_cb) {
        ota_cb_fun_t.recv_fw_cb(buf, length);
    }

    return ESP_OK;
}

void esp_ble_ota_send_ack_data(ble_ota_ack_type_t ack_type, uint16_t ack_status, uint16_t ack_param) {
    uint8_t cmd_ack[20] = {0};
    uint16_t crc16 = 0;

    switch (ack_type) {
        case BLE_OTA_CMD_ACK:
            cmd_ack[0] = (BLE_OTA_ACK_CMD & 0xff);
            cmd_ack[1] = (BLE_OTA_ACK_CMD & 0xff00) >> 8;

            cmd_ack[2] = (ack_param & 0xff);
            cmd_ack[3] = (ack_param & 0xff00) >> 8;

            cmd_ack[4] = (ack_status & 0xff);
            cmd_ack[5] = (ack_status & 0xff00) >> 8;

            crc16 = crc16_ccitt(cmd_ack, 18);
            cmd_ack[18] = crc16 & 0xff;
            cmd_ack[19] = (crc16 & 0xff00) >> 8;

            esp_ble_ota_notification_data(CMD_CHAR, cmd_ack, 20);
            break;
        case BLE_OTA_FW_ACK:
            cmd_ack[0] = (ack_param & 0xff);
            cmd_ack[1] = (ack_param & 0xff00) >> 8;

            cmd_ack[2] = (ack_status & 0xff);
            cmd_ack[3] = (ack_status & 0xff00) >> 8;

            cmd_ack[4] = (cur_sector & 0xff);
            cmd_ack[5] = (cur_sector & 0xff00) >> 8;

            crc16 = crc16_ccitt(cmd_ack, 18);
            cmd_ack[18] = crc16 & 0xff;
            cmd_ack[19] = (crc16 & 0xff00) >> 8;

            esp_ble_ota_notification_data(RECV_FW_CHAR, cmd_ack, 20);
            break;
        default:
            break;
    }
}

void esp_ble_ota_process_recv_data(esp_ble_ota_char_t ota_char, uint8_t *val, uint16_t val_len) {
    unsigned int recv_sector = 0;

    switch (ota_char) {
        case CMD_CHAR:
            // Start BLE OTA Process
            if ((val[0] == 0x01) && (val[1] == 0x00)) {
                if (start_ota) {
                    esp_ble_ota_send_ack_data(BLE_OTA_CMD_ACK, BLE_OTA_REJECT, BLE_OTA_START_CMD);
                } else {
                    start_ota = true;
                    // Calculating Firmware Length
                    ota_total_len = (val[2]) +
                                    (val[3] * 256) +
                                    (val[4] * 256 * 256) +
                                    (val[5] * 256 * 256 * 256);
                    esp_ble_ota_set_fw_length(ota_total_len);
                    ESP_LOGI(TAG, "Recv ota start cmd, fw_length = %d", ota_total_len);
                    // Malloc buffer to store receive Firmware
                    fw_buf = (uint8_t *) malloc(BUF_LENGTH * sizeof(uint8_t));
                    if (fw_buf == NULL) {
                        ESP_LOGE(TAG, "Malloc fail");
                        break;
                    } else {
                        memset(fw_buf, 0x0, BUF_LENGTH);
                    }

                    esp_ble_ota_send_ack_data(BLE_OTA_CMD_ACK, BLE_OTA_CMD_SUCCESS, BLE_OTA_START_CMD);
                }
            }
                // Stop BLE OTA Process
            else if ((val[0] == 0x02) && (val[1] == 0x00)) {
                if (start_ota) {
                    start_ota = false;
                    esp_ble_ota_set_fw_length(0);
                    ESP_LOGI(TAG, "recv ota stop cmd");
                    esp_ble_ota_send_ack_data(BLE_OTA_CMD_ACK, BLE_OTA_CMD_SUCCESS, BLE_OTA_STOP_CMD);

                    if (fw_buf) {
                        free(fw_buf);
                        fw_buf = NULL;
                    }
                } else {
                    esp_ble_ota_send_ack_data(BLE_OTA_CMD_ACK, BLE_OTA_REJECT, BLE_OTA_STOP_CMD);
                }

            } else {
                ESP_LOGE(TAG, "Unknown Command [0x%02x%02x]", val[1], val[0]);
            }
            break;
        case RECV_FW_CHAR:
            if (start_ota) {
                // Calculating the received sector index
                recv_sector = (val[0] + (val[1] * 256));

                if (recv_sector != cur_sector) { // sector error
                    if (recv_sector == 0xffff) { // last sector
                        ESP_LOGI(TAG, "Laster sector");
                    } else {  // sector error
                        ESP_LOGE(TAG, "Sector index error, cur: %d, recv: %d", cur_sector, recv_sector);
                        esp_ble_ota_send_ack_data(BLE_OTA_FW_ACK, BLE_OTA_FW_IND_ERR, recv_sector);
                    }
                }

                if (val[2] != cur_packet) { // packet seq error
                    if (val[2] == 0xff) { // last packet
                        ESP_LOGI(TAG, "laster packet");
                        goto write_ota_data;
                    } else { // packet seq error
                        ESP_LOGE(TAG, "Packet index error, cur: %d, recv: %d", cur_packet, val[2]);
                    }
                }
                write_ota_data:
                memcpy(fw_buf + fw_buf_offset, val + 3, val_len - 3);
                fw_buf_offset += val_len - 3;
                ESP_LOGI(TAG, "DEBUG: Sector:%d, total length:%d, length:%d", cur_sector, fw_buf_offset, val_len - 3);
                if (val[2] == 0xff) {
                    cur_packet = 0;
                    cur_sector++;
                    ESP_LOGD(TAG, "DEBUG: recv %d sector", cur_sector);
                    goto sector_end;
                } else {
                    cur_packet++;
                }

                break;
                sector_end:
                esp_ble_ota_recv_fw_handler(fw_buf, 4096);
                memset(fw_buf, 0x0, 4096);
                fw_buf_offset = 0;
                esp_ble_ota_send_ack_data(BLE_OTA_FW_ACK, BLE_OTA_FW_SUCCESS, recv_sector);
            } else {
                ESP_LOGE(TAG, "BLE OTA hasn't started yet");
            }
            break;
        case OTA_STATUS_CHAR:
            break;
        case CUS_CHAR:
            break;
        default:
            ESP_LOGW(TAG, "Invalid data was received, char[%d]", ota_char);
            break;
    }
}

static void gatts_ota_profile_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) {
    esp_err_t ret;
    esp_ble_ota_char_t ota_char;

    ESP_LOGI(TAG, "%s - event: %d", __func__, event);

    switch (event) {
        case ESP_GATTS_REG_EVT:
            ret = esp_ble_gap_set_device_name(DEVICE_NAME);
            if (ret) {
                ESP_LOGE(TAG, "set device name failed, error code = %x", ret);
            }
#ifdef CONFIG_BT_BLE_50_FEATURES_SUPPORTED
            ret = esp_ble_gap_ext_adv_set_params(EXT_ADV_HANDLE, &ext_ota_adv_params);
            if (ret) {
                ESP_LOGE(TAG, "set ext adv params failed, error code = %x", ret);
            }
#else
            ret = esp_ble_gap_config_adv_data_raw((uint8_t *) ota_adv_data, sizeof(ota_adv_data));
            if (ret) {
                ESP_LOGE(TAG, "set adv data failed, error code = %x", ret);
            }

            ret = esp_ble_gap_config_scan_rsp_data_raw((uint8_t *) ota_scan_rsp_data, sizeof(ota_scan_rsp_data));
            if (ret) {
                ESP_LOGE(TAG, "set scan rsp data failed, error code = %x", ret);
            }
#endif

            ret = esp_ble_gatts_create_attr_tab(ota_gatt_db, gatts_if, OTA_IDX_NB, OTA_PROFILE_APP_IDX);
            if (ret) {
                ESP_LOGE(TAG, "Create attr table failed, error code = %x", ret);
            }
            break;
        case ESP_GATTS_READ_EVT:
            ota_char = find_ota_char_and_desr_by_handle(param->read.handle);
            ESP_LOGI(TAG, "Read event - ota_char: %d", ota_char);
            break;
        case ESP_GATTS_WRITE_EVT:
            ota_char = find_ota_char_and_desr_by_handle(param->write.handle);
            ESP_LOGD(TAG, "Write event - ota_char: %d", ota_char);
            // Enable indication
            if ((param->write.len == 2) && (param->write.value[0] == 0x02) && (param->write.value[1] == 0x00)) {
                if (ota_char == OTA_STATUS_CHAR_CCC) {
                    ota_notification.process_bar_ntf_enable = true;
                }
                if (ota_char == RECV_FW_CHAR_CCC) {
                    ota_notification.recv_fw_ntf_enable = true;
                }
                if (ota_char == CMD_CHAR_CCC) {
                    ota_notification.command_ntf_enable = true;
                }
                if (ota_char == CUS_CHAR_CCC) {
                    ota_notification.customer_ntf_enable = true;
                }
            }
                // Disable indication
            else if ((param->write.len == 2) && (param->write.value[0] == 0x00) && (param->write.value[1] == 0x00)) {
                if (ota_char == OTA_STATUS_CHAR_CCC) {
                    ota_notification.process_bar_ntf_enable = false;
                }
                if (ota_char == RECV_FW_CHAR_CCC) {
                    ota_notification.recv_fw_ntf_enable = false;
                }
                if (ota_char == CMD_CHAR_CCC) {
                    ota_notification.command_ntf_enable = false;
                }
                if (ota_char == CUS_CHAR_CCC) {
                    ota_notification.customer_ntf_enable = false;
                }
            }

            if (param->write.is_prep == false) {
                esp_ble_ota_process_recv_data(ota_char, param->write.value, param->write.len);
            } else {
                if (temp_prep_write_buf == NULL) {
                    temp_prep_write_buf = (uint8_t *) malloc(BLE_OTA_MAX_CHAR_VAL_LEN * sizeof(uint8_t));
                    if (temp_prep_write_buf == NULL) {
                        ESP_LOGE(TAG, "Malloc buffer for prep write fail");
                        break;
                    }

                    memset(temp_prep_write_buf, 0x0, BLE_OTA_MAX_CHAR_VAL_LEN);
                    temp_buf_len = 0;
                }

                memcpy(temp_prep_write_buf + temp_buf_len, param->write.value, param->write.len);
                temp_buf_len += param->write.len;
            }
            break;
        case ESP_GATTS_EXEC_WRITE_EVT:
            if (param->exec_write.exec_write_flag == ESP_GATT_PREP_WRITE_EXEC) {
                if (temp_prep_write_buf) {
                    esp_ble_ota_process_recv_data(RECV_FW_CHAR, temp_prep_write_buf, temp_buf_len);
                }
            } else {
                if (temp_prep_write_buf) {
                    free(temp_prep_write_buf);
                    temp_prep_write_buf = NULL;
                }

                temp_buf_len = 0;
            }
            break;
        case ESP_GATTS_MTU_EVT:
            ESP_LOGI(TAG, "ESP_GATTS_MTU_EVT - mtu = %d", param->mtu.mtu);
            ota_profile_tab[OTA_PROFILE_APP_IDX].mtu_size = param->mtu.mtu;
            break;
        case ESP_GATTS_CONF_EVT:
            break;
        case ESP_GATTS_START_EVT:
            ESP_LOGI(TAG, "SERVICE_START_EVT, status %d, service_handle %d", param->start.status,
                     param->start.service_handle);
            if (param->start.status != ESP_GATT_OK) {
                ESP_LOGE(TAG, "SERVICE START FAIL, status %d", param->start.status);
                break;
            }
            break;
        case ESP_GATTS_CONNECT_EVT:
            ota_profile_tab[OTA_PROFILE_APP_IDX].conn_id = param->connect.conn_id;
            esp_ble_conn_update_params_t conn_params = {0};
            memcpy(conn_params.bda, param->connect.remote_bda, sizeof(esp_bd_addr_t));
            /* For the IOS system, please reference the apple official documents about the ble connection parameters restrictions. */
            conn_params.latency = 0;
            conn_params.max_int = 0x06;    // max_int = 0x6*1.25ms = 7.5ms
            conn_params.min_int = 0x06;    // min_int = 0x6*1.25ms = 7.5ms
            conn_params.timeout = 400;     // timeout = 400*10ms = 4000ms
            ESP_LOGI(TAG, "ESP_GATTS_CONNECT_EVT, conn_id %d, remote %02x:%02x:%02x:%02x:%02x:%02x:",
                     param->connect.conn_id,
                     param->connect.remote_bda[0], param->connect.remote_bda[1], param->connect.remote_bda[2],
                     param->connect.remote_bda[3], param->connect.remote_bda[4], param->connect.remote_bda[5]);
            //start sent the update connection parameters to the peer device.
            esp_ble_gap_update_conn_params(&conn_params);
            break;
        case ESP_GATTS_DISCONNECT_EVT:
#ifdef CONFIG_BT_BLE_50_FEATURES_SUPPORTED
            esp_ble_gap_ext_adv_start(NUM_EXT_ADV_SET, &ext_adv[0]);
#else
            esp_ble_gap_start_advertising(&ota_adv_params);
#endif
            ota_profile_tab[OTA_PROFILE_APP_IDX].mtu_size = 23;
            break;
        case ESP_GATTS_CREAT_ATTR_TAB_EVT:
            if (param->add_attr_tab.status != ESP_GATT_OK) {
                ESP_LOGE(TAG, "create attribute table failed, error code=0x%x", param->add_attr_tab.status);
            } else if (param->add_attr_tab.num_handle != OTA_IDX_NB) {
                ESP_LOGE(TAG, "create attribute table abnormally, num_handle (%d) doesn't equal to OTA_IDX_NB(%d)",
                         param->add_attr_tab.num_handle, OTA_IDX_NB);
            } else {
                ESP_LOGI(TAG, "create attribute table successfully, the number handle = %d\n",
                         param->add_attr_tab.num_handle);
                memcpy(ota_handle_table, param->add_attr_tab.handles, sizeof(ota_handle_table));
                esp_ble_gatts_start_service(ota_handle_table[OTA_SVC_IDX]);
            }
            break;
        case ESP_GATTS_STOP_EVT:
            break;
        case ESP_GATTS_OPEN_EVT:
            break;
        case ESP_GATTS_CANCEL_OPEN_EVT:
            break;
        case ESP_GATTS_CLOSE_EVT:
            break;
        case ESP_GATTS_LISTEN_EVT:
            break;
        case ESP_GATTS_CONGEST_EVT:
            break;
        case ESP_GATTS_UNREG_EVT:
            break;
        case ESP_GATTS_DELETE_EVT:
            break;
        default:
            break;
    }
}

static esp_err_t esp_ble_ota_send_indication(esp_ble_ota_service_index_t index, uint8_t *value, uint8_t length, bool need_ack) {
    esp_err_t ret;
    uint16_t offset = 0;

    if (length <= (ota_profile_tab[OTA_PROFILE_APP_IDX].mtu_size - 3)) {
        ret = esp_ble_gatts_send_indicate(ota_profile_tab[OTA_PROFILE_APP_IDX].gatts_if,
                                          ota_profile_tab[OTA_PROFILE_APP_IDX].conn_id, ota_handle_table[index], length,
                                          value, need_ack);
        if (ret) {
            ESP_LOGE(TAG, "%s send notification fail: %s\n", __func__, esp_err_to_name(ret));
            return ESP_FAIL;
        }
    } else {
        while ((length - offset) > (ota_profile_tab[OTA_PROFILE_APP_IDX].mtu_size - 3)) {
            ret = esp_ble_gatts_send_indicate(ota_profile_tab[OTA_PROFILE_APP_IDX].gatts_if,
                                              ota_profile_tab[OTA_PROFILE_APP_IDX].conn_id, ota_handle_table[index],
                                              (ota_profile_tab[OTA_PROFILE_APP_IDX].mtu_size - 3), value + offset,
                                              need_ack);
            if (ret) {
                ESP_LOGE(TAG, "%s send notification fail: %s\n", __func__, esp_err_to_name(ret));
                return ESP_FAIL;
            }
            offset += (ota_profile_tab[OTA_PROFILE_APP_IDX].mtu_size - 3);
        }

        if ((length - offset) > 0) {
            ret = esp_ble_gatts_send_indicate(ota_profile_tab[OTA_PROFILE_APP_IDX].gatts_if,
                                              ota_profile_tab[OTA_PROFILE_APP_IDX].conn_id, ota_handle_table[index],
                                              (length - offset), value + offset, need_ack);
            if (ret) {
                ESP_LOGE(TAG, "%s send notification fail: %s\n", __func__, esp_err_to_name(ret));
                return ESP_FAIL;
            }
        }
    }

    return ESP_OK;
}

esp_err_t esp_ble_ota_notification_data(esp_ble_ota_char_t ota_char, uint8_t *value, uint8_t length) {
    esp_err_t ret = ESP_FAIL;

    switch (ota_char) {
        case RECV_FW_CHAR:
            if (ota_notification.recv_fw_ntf_enable) {
                ret = esp_ble_ota_send_indication(RECV_FW_CHAR_VAL_IDX, value, length, false);
            } else {
                ESP_LOGE(TAG, "notify isn't enable");
            }
            break;
        case OTA_STATUS_CHAR:
            if (ota_notification.process_bar_ntf_enable) {
                ret = esp_ble_ota_send_indication(OTA_STATUS_CHAR_VAL_IDX, value, length, false);
            } else {
                ESP_LOGE(TAG, "notify isn't enable");
            }
            break;
        case CMD_CHAR:
            if (ota_notification.command_ntf_enable) {
                ret = esp_ble_ota_send_indication(CMD_CHAR_VAL_IDX, value, length, false);
            } else {
                ESP_LOGE(TAG, "notify isn't enable");
            }
            break;
        case CUS_CHAR:
            if (ota_notification.customer_ntf_enable) {
                ret = esp_ble_ota_send_indication(CUS_CHAR_VAL_IDX, value, length, false);
            } else {
                ESP_LOGE(TAG, "notify isn't enable");
            }
            break;
        default:
            ret = ESP_FAIL;
            break;
    }

    if (ret) {
        ESP_LOGE(TAG, "%s notification fail: %s\n", __func__, esp_err_to_name(ret));
        return ESP_FAIL;
    }

    return ESP_OK;
}

esp_err_t esp_ble_ota_recv_fw_data_callback(esp_ble_ota_recv_fw_cb_t callback) {
    ota_cb_fun_t.recv_fw_cb = callback;
    return ESP_OK;
}

esp_err_t esp_ble_ota_host_init(void) {
    esp_err_t ret;
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 2, 0)
    esp_bluedroid_config_t cfg = BT_BLUEDROID_INIT_CONFIG_DEFAULT();
    ret = esp_bluedroid_init_with_cfg(&cfg);
#else
    ret = esp_bluedroid_init();
#endif
    if (ret) {
        ESP_LOGE(TAG, "%s init bluetooth failed: %s\n", __func__, esp_err_to_name(ret));
        return ret;
    }

    ret = esp_bluedroid_enable();
    if (ret) {
        ESP_LOGE(TAG, "%s enable bluetooth failed: %s\n", __func__, esp_err_to_name(ret));
        return ret;
    }

    esp_ble_gatts_register_callback(gatts_event_handler);
    esp_ble_gap_register_callback(gap_event_handler);

    esp_ble_gatts_app_register(OTA_PROFILE_APP_IDX);
    esp_ble_gatts_app_register(DIS_PROFILE_APP_IDX);

    /* set the security iocap & auth_req & key size & init key response key parameters to the stack*/
    esp_ble_auth_req_t auth_req = ESP_LE_AUTH_BOND;     //bonding with peer device after authentication
    esp_ble_io_cap_t iocap = ESP_IO_CAP_NONE;           //set the IO capability to No output No input
    uint8_t key_size = 16;      //the key size should be 7~16 bytes
    uint8_t init_key = ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK;
    uint8_t rsp_key = ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK;
    esp_ble_gap_set_security_param(ESP_BLE_SM_AUTHEN_REQ_MODE, &auth_req, sizeof(uint8_t));
    esp_ble_gap_set_security_param(ESP_BLE_SM_IOCAP_MODE, &iocap, sizeof(uint8_t));
    esp_ble_gap_set_security_param(ESP_BLE_SM_MAX_KEY_SIZE, &key_size, sizeof(uint8_t));
    /* If your BLE device act as a Slave, the init_key means you hope which types of key of the master should distribute to you,
    and the response key means which key you can distribute to the Master;
    If your BLE device act as a master, the response key means you hope which types of key of the slave should distribute to you,
    and the init key means which key you can distribute to the slave. */
    esp_ble_gap_set_security_param(ESP_BLE_SM_SET_INIT_KEY, &init_key, sizeof(uint8_t));
    esp_ble_gap_set_security_param(ESP_BLE_SM_SET_RSP_KEY, &rsp_key, sizeof(uint8_t));

    return ESP_OK;
}

// 由于OTA的原理就是通过服务中的特征值进行通信接收OTA信息
// 因此在需要OTA的时候执行以下函数即可
// 以下函数替换了原有的应用,因此一旦执行OTA升级,那原有的通信功能将失效#pragma once

#include "stdbool.h"
#include "esp_err.h"

typedef void (*esp_ble_ota_recv_fw_cb_t)(uint8_t *buf, uint32_t length);

typedef struct esp_ble_ota_callback_funs {
    esp_ble_ota_recv_fw_cb_t recv_fw_cb;
} esp_ble_ota_callback_funs_t;


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;

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;


esp_err_t esp_ble_ota_host_init(void);

esp_err_t esp_ble_ota_recv_fw_data_callback(esp_ble_ota_recv_fw_cb_t callback);

unsigned int esp_ble_ota_get_fw_length(void);

void esp_ble_ota_start();

// 如果你已经创建好了一个服务,那你一定已经初始化完成,也就不用使用esp_ble_ota_host_init再进行初始化
void esp_ble_ota_start(){
    esp_ble_gatts_register_callback(gatts_event_handler);
    esp_ble_gap_register_callback(gap_event_handler);

    esp_ble_gatts_app_register(OTA_PROFILE_APP_IDX);
    esp_ble_gatts_app_register(DIS_PROFILE_APP_IDX);
}
#pragma once

#include "stdbool.h"
#include "esp_err.h"

typedef void (*esp_ble_ota_recv_fw_cb_t)(uint8_t *buf, uint32_t length);

typedef struct esp_ble_ota_callback_funs {
    esp_ble_ota_recv_fw_cb_t recv_fw_cb;
} esp_ble_ota_callback_funs_t;


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;

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;


esp_err_t esp_ble_ota_host_init(void);

esp_err_t esp_ble_ota_recv_fw_data_callback(esp_ble_ota_recv_fw_cb_t callback);

unsigned int esp_ble_ota_get_fw_length(void);

void esp_ble_ota_start();

ble_ota_task.c和升级写入flash相关

#include "dh_p1_ble_ota.h"
#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 "ble_ota.h"

#define OTA_RINGBUF_SIZE                    8192
#define OTA_TASK_SIZE                       8192

static const char *TAG = "ESP_BLE_OTA";

static RingbufHandle_t s_ringbuf = NULL;
#pragma once

#include "stdint.h"
#include "stdbool.h"

void start_ble_ota();
ESP
License:  CC BY 4.0
Share

Further Reading

OLDER

ESP32(九) BLE GATTS

NEWER

Recently Updated

  • ESP32(十) BLE OTA
  • ESP32(九) BLE GATTS
  • LVGL(四) 动画
  • LVGL(三) 对象中创建对象
  • LVGL(二) 定时回调

Trending Tags

LVGL WCH Linux Elec freeRTOS STM ESP Flutter Others SwiftUI

Contents

©2025 松果工作室. Some rights reserved.

Using the Halo theme Chirpy