esp_alox/main/ota_update.c
simon f2296a33e6 Create a simpler version of the OTA Update
Using no Broadcast logic for speed but its working now.
There is to much Acks going on but for the prototyp that is okay
2025-09-28 20:52:36 +02:00

661 lines
22 KiB
C

#include "ota_update.h"
#include "client_handler.h"
#include "communication_handler.h"
#include "driver/uart.h"
#include "esp_app_format.h"
#include "esp_err.h"
#include "esp_log.h"
#include "esp_log_buffer.h"
#include "esp_now.h"
#include "esp_ota_ops.h"
#include "esp_partition.h"
#include "esp_system.h"
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "freertos/task.h"
#include "message_builder.h"
#include "message_handler.h"
#include "message_structs.h"
#include "uart_handler.h"
#include "uart_msg_ids.h"
#include <stdbool.h>
#include <stddef.h>
#include <string.h>
static const char *TAG = "ALOX - OTA";
static QueueHandle_t ota_task_queue = NULL;
static esp_ota_handle_t update_handle = 0;
static uint8_t update_buffer[UPDATE_BUFFER_SIZE];
static uint8_t update_buffer_chunk[250];
static uint8_t update_buffer_chunk_len;
static uint32_t chunk_bitmask;
static uint16_t current_block_id;
static uint16_t update_buffer_write_index;
static uint32_t update_size;
static uint16_t sequenz_counter; // how often the update buffer gets written
static esp_partition_t partition_to_read_update_from;
static uint32_t partition_to_read_from_read_index;
static ClientList *client_list;
static bool all_chunks_send;
static bool finished;
uint32_t get_app_size(const esp_partition_t *app_size_partition) {
esp_app_desc_t app_desc;
esp_ota_get_partition_description(app_size_partition, &app_desc);
esp_image_header_t header;
esp_partition_read(app_size_partition, 0, &header, sizeof(header));
if (header.magic != ESP_IMAGE_HEADER_MAGIC) {
ESP_LOGE(TAG, "KEIN VALIDER HEADER");
return 0;
}
uint32_t data_len = sizeof(header);
for (int i = 0; i < header.segment_count; i++) {
esp_image_segment_header_t segment_header;
esp_partition_read(app_size_partition, data_len, &segment_header,
sizeof(segment_header));
ESP_LOGI(TAG, "SEGMENT %d Address %d, Segment DataLen %d", i,
segment_header.load_addr, segment_header.data_len);
uint32_t padded_len = (segment_header.data_len + 3) & ~3;
data_len += padded_len + sizeof(segment_header);
}
data_len += 1;
data_len += 32;
uint32_t padding = (16 - (data_len % 16)) % 16;
data_len += padding;
return data_len;
}
int ota_send_finish(ClientList *client_list) {
// read flash and send data
BaseMessage replyMessage = {};
OTA_FINISH_UPDATE_Payload payload = {};
ESP_LOGI(TAG, "OTA SEND FINISH");
replyMessage = MessageBuilder(OTA_FINISH_UPDATE, *(PayloadUnion *)&payload,
sizeof(payload));
for (int i = 0; i < MAX_CLIENTS; i++) {
if (client_list->Clients[i].slotIsUsed) {
if (client_list->Clients[i].ota_status == OTA_UPDATING) {
esp_now_send(client_list->Clients[i].macAddr, (uint8_t *)&replyMessage,
sizeof(BaseMessage));
client_list->Clients[i].ota_status = OTA_AWAITING_ACK;
}
}
}
finished = true;
return 0;
}
int ota_send_next_update_chunk(ClientList *client_list) {
// read flash and send data
BaseMessage replyMessage = {};
OTA_CHUNK_Payload payload = {};
size_t actual_read = 200;
if (partition_to_read_from_read_index + actual_read > update_size) {
actual_read = update_size - partition_to_read_from_read_index;
}
esp_err_t err = esp_partition_read(&partition_to_read_update_from,
partition_to_read_from_read_index,
payload.data, actual_read);
if (actual_read < 200) {
ESP_LOG_BUFFER_HEX(TAG, payload.data, actual_read);
}
if (err != ESP_OK) {
ESP_LOGE(TAG, "Could not read partition");
}
partition_to_read_from_read_index += actual_read;
payload.data_len = actual_read;
ESP_LOGI(TAG, "READ %d Bytes Sendig it to all Clients waiting", actual_read);
ESP_LOGI(TAG, "READ PARTITION AT %d Bytes MAX Bytes %d",
partition_to_read_from_read_index, update_size);
replyMessage =
MessageBuilder(OTA_CHUNK, *(PayloadUnion *)&payload, sizeof(payload));
for (int i = 0; i < MAX_CLIENTS; i++) {
if (client_list->Clients[i].slotIsUsed) {
if (client_list->Clients[i].ota_status == OTA_UPDATING) {
esp_now_send(client_list->Clients[i].macAddr, (uint8_t *)&replyMessage,
sizeof(BaseMessage));
client_list->Clients[i].ota_status = OTA_AWAITING_ACK;
}
}
}
if (partition_to_read_from_read_index == update_size)
return 1; // last update chunk send now finish it!
return 0;
}
void MasterOTATask(void *pvParameter) {
ESP_LOGI(TAG, "master_ota_task started");
ota_task_queue_message_t msg;
MasterOTA_TaskParams_t task_params = *(MasterOTA_TaskParams_t *)pvParameter;
client_list = task_params.client_list;
while (1) {
if (xQueueReceive(ota_task_queue, &msg, portMAX_DELAY)) {
ESP_LOGI(TAG, "master ota_task received command: %d", msg.command);
BaseMessage replyMessage = {};
switch (msg.command) {
case OTA_SEND_SLAVES_PREPARE_MESSAGE: {
}
case OTA_SLAVE_WILL_PREPARE: {
int id = get_client_id(client_list, msg.mac_addr);
if (id < 0) {
// error
ESP_LOGE(TAG, "Error set OTA_PREPARE could not get client id");
}
// just wait
// mark client that it will wait
ESP_LOGI(TAG, "MASTER OTA TASK: Marking Client %d as OTA_PREPARING",
id);
client_list->Clients[id].ota_status = OTA_PREPARING;
break;
}
case OTA_SLAVE_IS_PREPARED: {
// client is prepared check if all clients are preapred to send chunks
int id = get_client_id(client_list, msg.mac_addr);
if (id < 0) {
// error
}
if (client_list->Clients[id].ota_status == OTA_PREPARING) {
ESP_LOGI(TAG, "MASTER OTA TASK: Marking Client %d as OTA_UPDATING",
id);
client_list->Clients[id].ota_status = OTA_UPDATING;
} else {
ESP_LOGE(TAG, "MASTER OTA TASK: Client this should not happend");
}
bool start = true;
// check if all clients are prepared
for (int i = 0; i < MAX_CLIENTS; i++) {
if (client_list->Clients[i].slotIsUsed) {
if (client_list->Clients[i].ota_status != OTA_UPDATING)
start = false;
}
}
if (start)
ota_send_next_update_chunk(client_list);
break;
}
case OTA_SLAVE_ACKED: {
// mark client as acked check if all clients acked to send next message
int id = get_client_id(client_list, msg.mac_addr);
if (id < 0) {
// error
}
if (client_list->Clients[id].ota_status == OTA_AWAITING_ACK) {
ESP_LOGI(TAG, "OTA_SLAVE_ACKED Client %d Status Update OTA_UPDATING",
id);
client_list->Clients[id].ota_status = OTA_UPDATING;
} else {
ESP_LOGE(TAG, "OTA_SLAVE_ACKED Client %d Status Should not HAPPEND",
id);
}
bool start = true;
// check if all clients are prepared
for (int i = 0; i < MAX_CLIENTS; i++) {
if (client_list->Clients[i].slotIsUsed) {
ESP_LOGI(TAG, "SLOT %d is USED", i);
if (client_list->Clients[i].ota_status != OTA_UPDATING)
start = false;
}
}
if (start) {
if (finished)
break; // dont need to send anything else
if (all_chunks_send) {
ota_send_finish(client_list);
break;
}
ESP_LOGE(TAG, "OTA_SLAVE_ACKED all clients have the status "
"OTA_UPDATING SENDING NEXT CHUNK");
int end = ota_send_next_update_chunk(client_list);
if (end) {
all_chunks_send = true;
}
}
break;
}
case OTA_SLAVE_ERROR:
break;
// mark client as error
case OTA_MASTER_SEND_PREAPRE_REQUEST:
break;
case OTA_MASTER_SEND_CHUNK:
break;
case OTA_MASTER_SEND_FINISH:
break;
}
}
}
}
void slave_ota_task(void *pvParameter) {
ESP_LOGI(TAG, "slave_ota_task started");
ota_task_queue_message_t msg;
BaseMessage replyMessage = {};
while (1) {
if (xQueueReceive(ota_task_queue, &msg, portMAX_DELAY)) {
ESP_LOGI(TAG, "slave ota_task received command: %d", msg.command);
switch (msg.command) {
case OTA_SEND_SLAVES_PREPARE_MESSAGE:
break;
case OTA_SLAVE_WILL_PREPARE:
break;
case OTA_SLAVE_IS_PREPARED:
break;
case OTA_SLAVE_ACKED:
break;
case OTA_SLAVE_ERROR:
break;
case OTA_MASTER_SEND_PREAPRE_REQUEST: {
ESP_LOGE(TAG, "START PREAPRE CALL");
int part = prepare_ota_update(); // this part function is blocking
OTA_READY_TO_RECEIVE_Payload payload = {};
if (part < 0) {
payload.status = 1; // ERROR
} else {
payload.status = 0; // READY
}
ESP_LOGE(TAG, "PREPARED %d", part);
replyMessage = MessageBuilder(
OTA_READY_TO_RECEIVE, *(PayloadUnion *)&payload, sizeof(payload));
esp_now_send(msg.mac_addr, (uint8_t *)&replyMessage,
sizeof(BaseMessage));
break;
}
case OTA_MASTER_SEND_CHUNK: {
// TODO: Move Update_buffer_chunk in normal update buffer no need for
// the extra step...
// TODO: at the moment its just so i can use the write_ota_update
// function unmodified
ESP_LOGI(TAG, "Master send Chunk writing it!");
write_ota_update(update_buffer_chunk_len, update_buffer_chunk);
ESP_LOGI(TAG, "AFTER WRITE_OTA_UPDATE!");
OTA_UPDATE_ACK_Payload payload = {
.update_buffer_write_index = update_buffer_write_index,
.current_block_id = current_block_id,
.sequenz_counter = sequenz_counter,
.status = 0,
};
replyMessage = MessageBuilder(
OTA_UPDATE_SLAVE_ACKED, *(PayloadUnion *)&payload, sizeof(payload));
esp_now_send(msg.mac_addr, (uint8_t *)&replyMessage,
sizeof(BaseMessage));
break;
}
case OTA_MASTER_SEND_FINISH: {
esp_err_t err = end_ota_update();
int status = 0;
if (err != ESP_OK) {
status = 1; // TODO: Set real error
}
ESP_LOGI(TAG, "UPDATE FERTIG STATUS %d should be 0", status);
OTA_UPDATE_ACK_Payload payload = {
.update_buffer_write_index = update_buffer_write_index,
.current_block_id = current_block_id,
.sequenz_counter = sequenz_counter,
.status = status,
};
replyMessage = MessageBuilder(
OTA_UPDATE_SLAVE_ACKED, *(PayloadUnion *)&payload, sizeof(payload));
esp_now_send(msg.mac_addr, (uint8_t *)&replyMessage,
sizeof(BaseMessage));
break;
}
}
}
}
}
void start_uart_update(uint8_t msgid, const uint8_t *payload,
size_t payload_len, uint8_t *send_payload_buffer,
size_t send_payload_buffer_size, uint8_t *send_buffer,
size_t send_buffer_size) {
ESP_LOGI(TAG, "OTA Update Start Uart Command");
vTaskPrioritySet(NULL, 2);
update_size = 0;
update_buffer_write_index = 0;
sequenz_counter = 0;
all_chunks_send = false;
finished = false;
int part = prepare_ota_update();
// TODO: what does this do? maybe comment it out for now?
if (part < 0) {
send_payload_buffer[1] = (part * -1) & 0xff;
} else {
send_payload_buffer[0] = part & 0xff;
}
int send_payload_len = 2;
int len = build_message(UART_OTA_START, send_payload_buffer, send_payload_len,
send_buffer, send_buffer_size);
if (len < 0) {
ESP_LOGE(TAG,
"Error Building UART Message: payload_len, %d, sendbuffer_size: "
"%d, mes_len(error): %d",
payload_len, send_buffer_size, len);
return;
}
uart_write_bytes(MASTER_UART, send_buffer, len);
}
esp_err_t write_ota_update(uint32_t write_len, const uint8_t *payload) {
ESP_LOGI(TAG, "write_ota_update: write_len: %d", write_len);
ESP_LOGI(TAG, "write_ota_update: update_buffer_write_index: %d",
update_buffer_write_index);
if (update_buffer_write_index + write_len > UPDATE_BUFFER_SIZE) {
ESP_LOGI(TAG, "write_ota_update: schreib das update weg!");
esp_err_t err =
esp_ota_write(update_handle, update_buffer, update_buffer_write_index);
if (err != ESP_OK) {
return err;
}
update_buffer_write_index = 0;
sequenz_counter++;
}
memcpy(&update_buffer[update_buffer_write_index], payload, write_len);
update_buffer_write_index += write_len;
return ESP_OK;
}
void payload_uart_update(uint8_t msgid, const uint8_t *payload_data_from_uart,
size_t total_payload_len_from_uart,
uint8_t *send_payload_buffer,
size_t send_payload_buffer_size, uint8_t *send_buffer,
size_t send_buffer_size) {
const uint8_t *actual_firmware_data = payload_data_from_uart;
uint32_t write_len = total_payload_len_from_uart;
if (update_size == 0) {
ESP_LOGI(TAG, "First chunk received. write_len: %d", write_len);
}
update_size += write_len;
esp_err_t err = write_ota_update(write_len, actual_firmware_data);
if (err != ESP_OK) {
ESP_LOGE(TAG, "GOT ESP ERROR WRITE OTA %d", err);
}
size_t send_payload_len = 4;
memcpy(send_payload_buffer, &sequenz_counter, 2);
memcpy(&send_payload_buffer[2], &update_buffer_write_index, 2);
send_payload_buffer[4] = 0x00; // error
int len = build_message(UART_OTA_PAYLOAD, send_payload_buffer,
send_payload_len, send_buffer, send_buffer_size);
if (len < 0) {
ESP_LOGE(TAG,
"Error Building UART Message: payload_len, %d, sendbuffer_size: "
"%d, mes_len(error): %d",
total_payload_len_from_uart, send_buffer_size, len);
return;
}
uart_write_bytes(MASTER_UART, send_buffer, len);
}
void end_uart_update(uint8_t msgid, const uint8_t *payload, size_t payload_len,
uint8_t *send_payload_buffer,
size_t send_payload_buffer_size, uint8_t *send_buffer,
size_t send_buffer_size) {
ESP_LOGI(TAG, "OTA Update End Uart Command");
esp_err_t err = end_ota_update();
int send_payload_len = 1;
send_payload_buffer[0] = err & 0xff;
int len = build_message(UART_OTA_END, send_payload_buffer, send_payload_len,
send_buffer, send_buffer_size);
if (len < 0) {
ESP_LOGE(TAG,
"Error Building UART Message: payload_len, %d, sendbuffer_size: "
"%d, mes_len(error): %d",
payload_len, send_buffer_size, len);
return;
}
uart_write_bytes(MASTER_UART, send_buffer, len);
vTaskPrioritySet(NULL, 1);
}
void init_ota() {
ota_task_queue = xQueueCreate(50, sizeof(ota_task_queue_message_t));
RegisterCallback(UART_OTA_START, start_uart_update);
RegisterCallback(UART_OTA_PAYLOAD, payload_uart_update);
RegisterCallback(UART_OTA_END, end_uart_update);
}
int prepare_ota_update() {
const esp_partition_t *running = esp_ota_get_running_partition();
ESP_LOGI(TAG, "Running Partition: %s", running->label);
const esp_partition_t *update_partition =
esp_ota_get_next_update_partition(NULL);
if (update_partition == NULL) {
ESP_LOGE(TAG, "Failed to find OTA partition.");
return -1;
}
ESP_LOGI(TAG, "Writing OTA Update to Partition: %s", update_partition->label);
esp_err_t err =
esp_ota_begin(update_partition, OTA_SIZE_UNKNOWN, &update_handle);
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_ota_begin failed (%s)", esp_err_to_name(err));
return -2;
}
return 0;
}
esp_err_t end_ota_update() {
if (update_buffer_write_index > 0) {
ESP_LOG_BUFFER_HEX(TAG, update_buffer, update_buffer_write_index);
esp_err_t err =
esp_ota_write(update_handle, update_buffer, update_buffer_write_index);
vTaskDelay(1);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Error writing remaining data to partition: %s",
esp_err_to_name(err));
return err;
}
}
esp_err_t err = esp_ota_end(update_handle);
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_ota_end failed: %s", esp_err_to_name(err));
ESP_LOGI(TAG, "Total blocks written: %u, Last partial block size: %u",
sequenz_counter, update_buffer_write_index);
return err;
}
const esp_partition_t *update_partition =
esp_ota_get_next_update_partition(NULL);
err = esp_ota_set_boot_partition(update_partition);
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_ota_set_boot_partition failed: %s",
esp_err_to_name(err));
}
return err;
}
// Acknoledge that the slave should prepare for an update
// Queues the Prepare Task beacuse it takes like 30 seconds
void slave_Prep_Upgrade_Callback(const esp_now_recv_info_t *esp_now_info,
const uint8_t *data, int data_len) {
ESP_LOGE(TAG, "SLAVE PREPARE FOR UPDATE Callback");
update_size = 0;
update_buffer_write_index = 0;
sequenz_counter = 0;
const BaseMessage *message = (const BaseMessage *)data;
const OTA_PREPARE_FOR_UPDATE_Payload *payload =
&message->payload.ota_prepare_for_update_payload;
// total_update_size = payload->total_size;
// Queue Command for Task to call the prepare method
ota_task_queue_message_t msg = {.command = OTA_MASTER_SEND_PREAPRE_REQUEST};
memcpy(msg.mac_addr, esp_now_info->src_addr, ESP_NOW_ETH_ALEN);
if (xQueueSend(ota_task_queue, &msg, pdMS_TO_TICKS(10)) != pdTRUE) {
ESP_LOGE(TAG, "Failed to send prepare command to OTA task");
}
ESP_LOGE(TAG, "SLAVE PREPARE CALLBACK AFTER QUEUE SEND");
// Tell the master that the slave will preapre
OTA_PREPARE_ACKNOWLEDGED_Payload *reply_payload;
BaseMessage reply_message =
MessageBuilder(OTA_PREPARE_ACKNOWLEDGED, *(PayloadUnion *)&reply_payload,
sizeof(OTA_PREPARE_ACKNOWLEDGED_Payload));
ESP_ERROR_CHECK(esp_now_send(esp_now_info->src_addr,
(uint8_t *)&reply_message, sizeof(BaseMessage)));
}
void slave_Update_Chunk_Callback(const esp_now_recv_info_t *esp_now_info,
const uint8_t *data, int data_len) {
const BaseMessage *message = (const BaseMessage *)data;
const OTA_CHUNK_Payload *payload = &message->payload.ota_chunk_payload;
// copy data to update_buffer_chunk so that the write method can write it
// back later
memcpy(update_buffer_chunk, payload->data, payload->data_len);
update_buffer_chunk_len = payload->data_len;
ESP_LOGI(TAG, "slave_update_Chunk_Callback got %d bytes from Master",
payload->data_len);
// Queue Command for Task to call the ota_write_message method
ota_task_queue_message_t msg = {.command = OTA_MASTER_SEND_CHUNK};
memcpy(msg.mac_addr, esp_now_info->src_addr, ESP_NOW_ETH_ALEN);
if (xQueueSend(ota_task_queue, &msg, pdMS_TO_TICKS(10)) != pdTRUE) {
ESP_LOGE(TAG, "Failed to send prepare command to OTA task");
}
}
void slave_Update_Finished_Callback(const esp_now_recv_info_t *esp_now_info,
const uint8_t *data, int data_len) {
const BaseMessage *message = (const BaseMessage *)data;
const OTA_FINISH_UPDATE_Payload *payload =
&message->payload.ota_finish_update_payload;
ESP_LOGI(TAG, "slave_Update_Finished_Callback");
// Queue Command for Task to call the ota_write_message method
ota_task_queue_message_t msg = {.command = OTA_MASTER_SEND_FINISH};
memcpy(msg.mac_addr, esp_now_info->src_addr, ESP_NOW_ETH_ALEN);
if (xQueueSend(ota_task_queue, &msg, pdMS_TO_TICKS(10)) != pdTRUE) {
ESP_LOGE(TAG, "Failed to send prepare command to OTA task");
}
}
void start_ota_update_espnow(uint8_t msgid, const uint8_t *payload,
size_t payload_len, uint8_t *send_payload_buffer,
size_t send_payload_buffer_size,
uint8_t *send_buffer, size_t send_buffer_size) {
ESP_LOGI(TAG, "Starting OTA update for all clients");
const esp_partition_t *ota_update_partition;
ota_update_partition = esp_ota_get_next_update_partition(NULL);
if (ota_update_partition == NULL) {
ESP_LOGE(TAG, "Failed to get update partition");
return;
}
update_size = get_app_size(ota_update_partition);
partition_to_read_update_from = *ota_update_partition;
partition_to_read_from_read_index = 0;
BaseMessage replyMessage = {};
OTA_PREPARE_FOR_UPDATE_Payload replyPayload = {};
replyMessage =
MessageBuilder(OTA_PREPARE_FOR_UPDATE, *(PayloadUnion *)&replyPayload,
sizeof(replyPayload));
for (int i = 0; i < MAX_CLIENTS; i++) {
if (client_list->Clients[i].slotIsUsed) {
esp_now_send(client_list->Clients[i].macAddr, (uint8_t *)&replyMessage,
sizeof(BaseMessage));
client_list->Clients[i].ota_status = OTA_READY;
}
}
}
void master_ota_prepare_acknowledge_callback(
const esp_now_recv_info_t *esp_now_info, const uint8_t *data,
int data_len) {
ESP_LOGI(TAG, "entering master_ota_prepare_acknowledge_callback");
// Queue Command for Task to call the ota_write_message method
ota_task_queue_message_t msg = {.command = OTA_SLAVE_WILL_PREPARE};
memcpy(msg.mac_addr, esp_now_info->src_addr, ESP_NOW_ETH_ALEN);
if (xQueueSend(ota_task_queue, &msg, pdMS_TO_TICKS(10)) != pdTRUE) {
ESP_LOGE(TAG, "Failed to send prepare command to OTA task");
}
}
void master_ota_ready_to_recieve_callback(
const esp_now_recv_info_t *esp_now_info, const uint8_t *data,
int data_len) {
ESP_LOGI(TAG, "entering master_ota_ready_to_recieve_callback");
// Queue Command for Task to call the ota_write_message method
ota_task_queue_message_t msg = {.command = OTA_SLAVE_IS_PREPARED};
memcpy(msg.mac_addr, esp_now_info->src_addr, ESP_NOW_ETH_ALEN);
if (xQueueSend(ota_task_queue, &msg, pdMS_TO_TICKS(10)) != pdTRUE) {
ESP_LOGE(TAG, "Failed to send prepare command to OTA task");
}
}
void master_ota_update_slave_acknowledge_callback(
const esp_now_recv_info_t *esp_now_info, const uint8_t *data,
int data_len) {
ESP_LOGI(TAG, "entering master_ota_update_slave_acknowledge_callback");
// Queue Command for Task to call the ota_write_message method
ota_task_queue_message_t msg = {.command = OTA_SLAVE_ACKED};
memcpy(msg.mac_addr, esp_now_info->src_addr, ESP_NOW_ETH_ALEN);
if (xQueueSend(ota_task_queue, &msg, pdMS_TO_TICKS(10)) != pdTRUE) {
ESP_LOGE(TAG, "Failed to send prepare command to OTA task");
}
}