/* * SPDX-FileCopyrightText: 2019-2023 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #include #include "freertos/FreeRTOS.h" #include "esp_log.h" #include "ble_ota.h" #ifdef CONFIG_BT_BLUEDROID_ENABLED #include "esp_bt.h" #include "esp_gap_ble_api.h" #include "esp_gatts_api.h" #include "esp_bt_main.h" #endif #define TAG "ESP_BLE_OTA" #define DEVICE_NAME "ESP-C919" #ifdef CONFIG_BT_BLUEDROID_ENABLED #define OTA_PROFILE_NUM 2 #define OTA_PROFILE_APP_IDX 0 #define DIS_PROFILE_APP_IDX 1 #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, 0x26, 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, 0x26, 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, 0x26, 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); /** * @brief This function is called to set the attribute value by the application * * @param[in] ota_char: the characteristic index which to be set * @param[in] value: the pointer to the attribute value * @param[in] length: the value length * * @return * - ESP_OK : success * - other : failed */ esp_err_t esp_ble_ota_set_value(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 & 0xff) >> 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 & 0xff) >> 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 & 0xff) >> 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, "Unknow 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_set_characteristic_value(esp_ble_ota_service_index_t index, uint8_t *value, uint8_t length) { esp_err_t ret; ret = esp_ble_gatts_set_attr_value(ota_handle_table[index], length, value); if (ret) { ESP_LOGE(TAG, "%s set attribute value fail: %s\n", __func__, esp_err_to_name(ret)); return ESP_FAIL; } return ESP_OK; } esp_err_t esp_ble_ota_set_value(esp_ble_ota_char_t ota_char, uint8_t *value, uint8_t length) { esp_err_t ret; switch (ota_char) { case RECV_FW_CHAR: ret = esp_ble_ota_set_characteristic_value(RECV_FW_CHAR_VAL_IDX, value, length); break; case OTA_STATUS_CHAR: ret = esp_ble_ota_set_characteristic_value(OTA_STATUS_CHAR_VAL_IDX, value, length); break; case CMD_CHAR: ret = esp_ble_ota_set_characteristic_value(CMD_CHAR_VAL_IDX, value, length); break; case CUS_CHAR: ret = esp_ble_ota_set_characteristic_value(CUS_CHAR_VAL_IDX, value, length); break; default: ret = ESP_FAIL; break; } if (ret) { ESP_LOGE(TAG, "%s set characteristic value fail: %s\n", __func__, esp_err_to_name(ret)); return ESP_FAIL; } return ESP_OK; } 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; ret = esp_bluedroid_init(); 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_LOGI(TAG, "ble ota Version[%d.%d.%d]\n", BLE_OTA_VER_MAJOR, BLE_OTA_VER_MINOR, BLE_OTA_VER_PATCH); 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; } #endif