OTA Update through MQTT fails
Posted: Wed Oct 30, 2024 10:32 am
Hello everyone,
I am working on implementing OTA updates received via MQTT in an ESP32 project using the ESP-IDF framework. The OTA update process involves receiving packets over MQTT. However, when I attempt to finalize the OTA update it does not happen.
I have added debug statements to trace the process, and it stops on "Update handle is invalid. Cannot finalize OTA.".
Could anyone suggest what might be causing this issue or where I should look for potential problems?
Here is my code for mqtt event handler:
void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data) {
esp_mqtt_event_handle_t event = (esp_mqtt_event_handle_t) event_data;
switch (event->event_id) {
case MQTT_EVENT_CONNECTED:
ESP_LOGI(TAG, "MQTT connected");
esp_mqtt_client_subscribe(mqtt_client, UPDATE_TOPIC, 1);
break;
case MQTT_EVENT_DATA:
if (event->topic_len == strlen(UPDATE_TOPIC) && strncmp(event->topic, UPDATE_TOPIC, event->topic_len) == 0) {
ESP_LOGI(TAG, "Received OTA data: %.*s", event->data_len, event->data);
// Allocate memory for the payload and null-terminate it
char* json_payload = (char*) malloc(event->data_len + 1);
if (!json_payload) {
ESP_LOGE(TAG, "Failed to allocate memory for JSON payload");
return;
}
memcpy(json_payload, event->data, event->data_len);
json_payload[event->data_len] = '\0';
ESP_LOGI(TAG, "Full JSON payload received: %s", json_payload);
if (json_payload[0] != '{' || json_payload[event->data_len - 1] != '}') {
ESP_LOGE(TAG, "Invalid JSON format: %s", json_payload);
free(json_payload);
return;
}
cJSON* json = cJSON_Parse(json_payload);
free(json_payload);
if (!json) {
ESP_LOGE(TAG, "JSON parsing error");
return;
}
// Check for EOF to finalize OTA
cJSON* eof = cJSON_GetObjectItem(json, "EOF");
if (eof && cJSON_IsString(eof) && strlen(eof->valuestring) == 0) {
ESP_LOGI(TAG, "Received EOF, finalizing OTA update");
if (update_handle != ESP_OTA_HANDLE_INVALID) {
esp_err_t end_result = esp_ota_end(update_handle);
if (end_result != ESP_OK) {
ESP_LOGE(TAG, "esp_ota_end failed: %s", esp_err_to_name(end_result));
update_handle = ESP_OTA_HANDLE_INVALID;
cJSON_Delete(json);
return;
}
esp_err_t boot_result = esp_ota_set_boot_partition(update_partition);
if (boot_result != ESP_OK) {
ESP_LOGE(TAG, "esp_ota_set_boot_partition failed: %s", esp_err_to_name(boot_result));
cJSON_Delete(json);
return;
}
ESP_LOGI(TAG, "Restarting the device...");
esp_restart();
} else {
ESP_LOGE(TAG, "Update handle is invalid. Cannot finalize OTA.");
}
} else {
// Get the packet sequence number
cJSON* packet_num = cJSON_GetObjectItem(json, "i");
if (packet_num && cJSON_IsNumber(packet_num)) {
packet_counter = packet_num->valueint;
}
// Only decode base64 if this is not the first or last packet
if (packet_counter > 1 && !eof) {
cJSON* payload = cJSON_GetObjectItem(json, "p");
if (payload && cJSON_IsString(payload)) {
const char* b64_data = payload->valuestring;
char binary_data[CHUNK_SIZE];
int decoded_size = base64_decode(binary_data, b64_data);
if (decoded_size < 0) {
ESP_LOGE(TAG, "Failed to decode base64 data");
cJSON_Delete(json);
return;
}
// Initialize update handle if not already done
if (update_handle == ESP_OTA_HANDLE_INVALID) {
update_partition = esp_ota_get_next_update_partition(NULL);
if (!update_partition) {
ESP_LOGE(TAG, "No available partition for OTA update");
cJSON_Delete(json);
return;
}
esp_err_t begin_result = esp_ota_begin(update_partition, OTA_SIZE_UNKNOWN, &update_handle);
if (begin_result != ESP_OK) {
ESP_LOGE(TAG, "esp_ota_begin failed: %s", esp_err_to_name(begin_result));
update_handle = ESP_OTA_HANDLE_INVALID;
cJSON_Delete(json);
return;
}
ESP_LOGI(TAG, "OTA begin succeeded, update_handle: %lu", (unsigned long)update_handle);
}
// Only write if update_handle is valid
if (update_handle != ESP_OTA_HANDLE_INVALID) {
esp_err_t write_result = esp_ota_write(update_handle, binary_data, decoded_size);
if (write_result != ESP_OK) {
ESP_LOGE(TAG, "Failed to write OTA data: %s", esp_err_to_name(write_result));
} else {
ESP_LOGI(TAG, "Wrote %d bytes to OTA", decoded_size);
}
} else {
ESP_LOGE(TAG, "Update handle is invalid during write.");
}
} else {
ESP_LOGE(TAG, "Base64 payload missing or invalid");
}
} else {
ESP_LOGI(TAG, "Skipping base64 decoding for first or last payload");
}
}
cJSON_Delete(json);
}
break;
case MQTT_EVENT_DISCONNECTED:
ESP_LOGI(TAG, "MQTT disconnected");
break;
case MQTT_EVENT_ERROR:
ESP_LOGE(TAG, "MQTT event error");
break;
default:
break;
}
}
I am working on implementing OTA updates received via MQTT in an ESP32 project using the ESP-IDF framework. The OTA update process involves receiving packets over MQTT. However, when I attempt to finalize the OTA update it does not happen.
I have added debug statements to trace the process, and it stops on "Update handle is invalid. Cannot finalize OTA.".
Could anyone suggest what might be causing this issue or where I should look for potential problems?
Here is my code for mqtt event handler:
void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data) {
esp_mqtt_event_handle_t event = (esp_mqtt_event_handle_t) event_data;
switch (event->event_id) {
case MQTT_EVENT_CONNECTED:
ESP_LOGI(TAG, "MQTT connected");
esp_mqtt_client_subscribe(mqtt_client, UPDATE_TOPIC, 1);
break;
case MQTT_EVENT_DATA:
if (event->topic_len == strlen(UPDATE_TOPIC) && strncmp(event->topic, UPDATE_TOPIC, event->topic_len) == 0) {
ESP_LOGI(TAG, "Received OTA data: %.*s", event->data_len, event->data);
// Allocate memory for the payload and null-terminate it
char* json_payload = (char*) malloc(event->data_len + 1);
if (!json_payload) {
ESP_LOGE(TAG, "Failed to allocate memory for JSON payload");
return;
}
memcpy(json_payload, event->data, event->data_len);
json_payload[event->data_len] = '\0';
ESP_LOGI(TAG, "Full JSON payload received: %s", json_payload);
if (json_payload[0] != '{' || json_payload[event->data_len - 1] != '}') {
ESP_LOGE(TAG, "Invalid JSON format: %s", json_payload);
free(json_payload);
return;
}
cJSON* json = cJSON_Parse(json_payload);
free(json_payload);
if (!json) {
ESP_LOGE(TAG, "JSON parsing error");
return;
}
// Check for EOF to finalize OTA
cJSON* eof = cJSON_GetObjectItem(json, "EOF");
if (eof && cJSON_IsString(eof) && strlen(eof->valuestring) == 0) {
ESP_LOGI(TAG, "Received EOF, finalizing OTA update");
if (update_handle != ESP_OTA_HANDLE_INVALID) {
esp_err_t end_result = esp_ota_end(update_handle);
if (end_result != ESP_OK) {
ESP_LOGE(TAG, "esp_ota_end failed: %s", esp_err_to_name(end_result));
update_handle = ESP_OTA_HANDLE_INVALID;
cJSON_Delete(json);
return;
}
esp_err_t boot_result = esp_ota_set_boot_partition(update_partition);
if (boot_result != ESP_OK) {
ESP_LOGE(TAG, "esp_ota_set_boot_partition failed: %s", esp_err_to_name(boot_result));
cJSON_Delete(json);
return;
}
ESP_LOGI(TAG, "Restarting the device...");
esp_restart();
} else {
ESP_LOGE(TAG, "Update handle is invalid. Cannot finalize OTA.");
}
} else {
// Get the packet sequence number
cJSON* packet_num = cJSON_GetObjectItem(json, "i");
if (packet_num && cJSON_IsNumber(packet_num)) {
packet_counter = packet_num->valueint;
}
// Only decode base64 if this is not the first or last packet
if (packet_counter > 1 && !eof) {
cJSON* payload = cJSON_GetObjectItem(json, "p");
if (payload && cJSON_IsString(payload)) {
const char* b64_data = payload->valuestring;
char binary_data[CHUNK_SIZE];
int decoded_size = base64_decode(binary_data, b64_data);
if (decoded_size < 0) {
ESP_LOGE(TAG, "Failed to decode base64 data");
cJSON_Delete(json);
return;
}
// Initialize update handle if not already done
if (update_handle == ESP_OTA_HANDLE_INVALID) {
update_partition = esp_ota_get_next_update_partition(NULL);
if (!update_partition) {
ESP_LOGE(TAG, "No available partition for OTA update");
cJSON_Delete(json);
return;
}
esp_err_t begin_result = esp_ota_begin(update_partition, OTA_SIZE_UNKNOWN, &update_handle);
if (begin_result != ESP_OK) {
ESP_LOGE(TAG, "esp_ota_begin failed: %s", esp_err_to_name(begin_result));
update_handle = ESP_OTA_HANDLE_INVALID;
cJSON_Delete(json);
return;
}
ESP_LOGI(TAG, "OTA begin succeeded, update_handle: %lu", (unsigned long)update_handle);
}
// Only write if update_handle is valid
if (update_handle != ESP_OTA_HANDLE_INVALID) {
esp_err_t write_result = esp_ota_write(update_handle, binary_data, decoded_size);
if (write_result != ESP_OK) {
ESP_LOGE(TAG, "Failed to write OTA data: %s", esp_err_to_name(write_result));
} else {
ESP_LOGI(TAG, "Wrote %d bytes to OTA", decoded_size);
}
} else {
ESP_LOGE(TAG, "Update handle is invalid during write.");
}
} else {
ESP_LOGE(TAG, "Base64 payload missing or invalid");
}
} else {
ESP_LOGI(TAG, "Skipping base64 decoding for first or last payload");
}
}
cJSON_Delete(json);
}
break;
case MQTT_EVENT_DISCONNECTED:
ESP_LOGI(TAG, "MQTT disconnected");
break;
case MQTT_EVENT_ERROR:
ESP_LOGE(TAG, "MQTT event error");
break;
default:
break;
}
}