ota: add ota update capabilities
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
#include "gpio_controller.h"
|
||||
#include "uart_handler.h"
|
||||
#include "ethernet_manager.h"
|
||||
#include "ota_handler.h"
|
||||
#include "esp_log.h"
|
||||
#include "cJSON.h"
|
||||
#include <string.h>
|
||||
@@ -504,6 +505,206 @@ static esp_err_t api_system_status_handler(httpd_req_t *req) {
|
||||
return send_json_success(req, status);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// OTA API Handlers
|
||||
// ============================================================================
|
||||
|
||||
static esp_err_t api_ota_status_handler(httpd_req_t *req) {
|
||||
cJSON *status = cJSON_CreateObject();
|
||||
|
||||
cJSON_AddStringToObject(status, "partition", ota_get_running_partition());
|
||||
cJSON_AddBoolToObject(status, "updating", ota_is_updating());
|
||||
cJSON_AddNumberToObject(status, "progress", ota_get_progress());
|
||||
|
||||
return send_json_success(req, status);
|
||||
}
|
||||
|
||||
static esp_err_t api_ota_update_handler(httpd_req_t *req) {
|
||||
// Check if already updating
|
||||
if (ota_is_updating()) {
|
||||
return send_json_error(req, "OTA update already in progress");
|
||||
}
|
||||
|
||||
// Parse JSON body
|
||||
char buf[512];
|
||||
int ret = httpd_req_recv(req, buf, sizeof(buf) - 1);
|
||||
if (ret <= 0) {
|
||||
return send_json_error(req, "No data received");
|
||||
}
|
||||
buf[ret] = '\0';
|
||||
|
||||
cJSON *json = cJSON_Parse(buf);
|
||||
if (!json) {
|
||||
return send_json_error(req, "Invalid JSON");
|
||||
}
|
||||
|
||||
cJSON *url_item = cJSON_GetObjectItem(json, "url");
|
||||
if (!url_item || !cJSON_IsString(url_item)) {
|
||||
cJSON_Delete(json);
|
||||
return send_json_error(req, "Missing or invalid 'url' field");
|
||||
}
|
||||
|
||||
const char *url = url_item->valuestring;
|
||||
|
||||
// Start OTA update
|
||||
esp_err_t err = ota_start_update(url);
|
||||
cJSON_Delete(json);
|
||||
|
||||
if (err != ESP_OK) {
|
||||
return send_json_error(req, "Failed to start OTA update");
|
||||
}
|
||||
|
||||
cJSON *response = cJSON_CreateObject();
|
||||
cJSON_AddStringToObject(response, "message", "OTA update started");
|
||||
return send_json_success(req, response);
|
||||
}
|
||||
|
||||
static esp_err_t api_ota_upload_handler(httpd_req_t *req) {
|
||||
// Check if already updating
|
||||
if (ota_is_updating()) {
|
||||
ESP_LOGE(TAG_HTTP, "OTA update already in progress");
|
||||
httpd_resp_set_status(req, "400 Bad Request");
|
||||
httpd_resp_set_type(req, "application/json");
|
||||
httpd_resp_sendstr(req, "{\"success\":false,\"error\":\"OTA update already in progress\"}");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
// Get content type to check for multipart
|
||||
char content_type[128] = {0};
|
||||
esp_err_t hdr_ret = httpd_req_get_hdr_value_str(req, "Content-Type", content_type, sizeof(content_type));
|
||||
if (hdr_ret != ESP_OK || strstr(content_type, "multipart/form-data") == NULL) {
|
||||
ESP_LOGE(TAG_HTTP, "Invalid content type for upload");
|
||||
httpd_resp_set_status(req, "400 Bad Request");
|
||||
httpd_resp_set_type(req, "application/json");
|
||||
httpd_resp_sendstr(req, "{\"success\":false,\"error\":\"Expected multipart/form-data\"}");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
// Find boundary
|
||||
char *boundary_start = strstr(content_type, "boundary=");
|
||||
if (!boundary_start) {
|
||||
ESP_LOGE(TAG_HTTP, "No boundary in content type");
|
||||
httpd_resp_set_status(req, "400 Bad Request");
|
||||
httpd_resp_set_type(req, "application/json");
|
||||
httpd_resp_sendstr(req, "{\"success\":false,\"error\":\"No boundary found\"}");
|
||||
return ESP_OK;
|
||||
}
|
||||
boundary_start += 9; // Skip "boundary="
|
||||
|
||||
// Extract boundary string
|
||||
char boundary[64] = {0};
|
||||
char *boundary_end = boundary_start;
|
||||
while (*boundary_end && *boundary_end != '\r' && *boundary_end != '\n' && *boundary_end != ';') {
|
||||
boundary_end++;
|
||||
}
|
||||
int boundary_len = boundary_end - boundary_start;
|
||||
if (boundary_len >= (int)sizeof(boundary)) {
|
||||
boundary_len = sizeof(boundary) - 1;
|
||||
}
|
||||
strncpy(boundary, boundary_start, boundary_len);
|
||||
|
||||
// Start OTA upload
|
||||
esp_err_t err = ota_start_upload();
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG_HTTP, "Failed to start OTA upload: %s", esp_err_to_name(err));
|
||||
httpd_resp_set_status(req, "500 Internal Server Error");
|
||||
httpd_resp_set_type(req, "application/json");
|
||||
httpd_resp_sendstr(req, "{\"success\":false,\"error\":\"Failed to start OTA upload\"}");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG_HTTP, "OTA upload started, receiving firmware data...");
|
||||
|
||||
// Buffer for reading data
|
||||
char recv_buf[1024];
|
||||
int total_received = 0;
|
||||
bool found_header = false;
|
||||
|
||||
while (true) {
|
||||
int received = httpd_req_recv(req, recv_buf, sizeof(recv_buf));
|
||||
if (received < 0) {
|
||||
ESP_LOGE(TAG_HTTP, "Error receiving data");
|
||||
ota_abort_upload();
|
||||
httpd_resp_set_status(req, "500 Internal Server Error");
|
||||
httpd_resp_set_type(req, "application/json");
|
||||
httpd_resp_sendstr(req, "{\"success\":false,\"error\":\"Error receiving data\"}");
|
||||
return ESP_OK;
|
||||
}
|
||||
if (received == 0) {
|
||||
break; // End of data
|
||||
}
|
||||
|
||||
total_received += received;
|
||||
|
||||
// Parse multipart data - find file data start
|
||||
if (!found_header) {
|
||||
// Look for end of headers (double CRLF)
|
||||
char *header_end = strstr(recv_buf, "\r\n\r\n");
|
||||
if (header_end) {
|
||||
// Skip headers
|
||||
int header_len = (header_end + 4) - recv_buf;
|
||||
found_header = true;
|
||||
|
||||
// Write remaining data after headers
|
||||
int data_len = received - header_len;
|
||||
if (data_len > 0) {
|
||||
// Check if this is the end boundary
|
||||
char *boundary_pos = strstr(header_end + 4, boundary);
|
||||
if (boundary_pos) {
|
||||
// This is the end, don't write boundary
|
||||
data_len = (boundary_pos - 2) - (header_end + 4); // -2 for \r\n before boundary
|
||||
if (data_len > 0) {
|
||||
err = ota_write_data((uint8_t *)(header_end + 4), data_len);
|
||||
}
|
||||
} else {
|
||||
err = ota_write_data((uint8_t *)(header_end + 4), data_len);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Already in file data - check for boundary
|
||||
char *boundary_pos = strstr(recv_buf, boundary);
|
||||
if (boundary_pos) {
|
||||
// Found end boundary - write data up to boundary (minus \r\n)
|
||||
int data_len = boundary_pos - recv_buf - 2; // -2 for \r\n before boundary
|
||||
if (data_len > 0) {
|
||||
err = ota_write_data((uint8_t *)recv_buf, data_len);
|
||||
}
|
||||
break; // Done
|
||||
} else {
|
||||
// Write all data
|
||||
err = ota_write_data((uint8_t *)recv_buf, received);
|
||||
}
|
||||
}
|
||||
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG_HTTP, "Failed to write OTA data");
|
||||
ota_abort_upload();
|
||||
httpd_resp_set_status(req, "500 Internal Server Error");
|
||||
httpd_resp_set_type(req, "application/json");
|
||||
httpd_resp_sendstr(req, "{\"success\":false,\"error\":\"Failed to write OTA data\"}");
|
||||
return ESP_OK;
|
||||
}
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG_HTTP, "OTA upload complete, total received: %d bytes", total_received);
|
||||
|
||||
// Finish OTA upload (this will restart)
|
||||
err = ota_finish_upload();
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG_HTTP, "Failed to finish OTA upload");
|
||||
httpd_resp_set_status(req, "500 Internal Server Error");
|
||||
httpd_resp_set_type(req, "application/json");
|
||||
httpd_resp_sendstr(req, "{\"success\":false,\"error\":\"Failed to finish OTA upload\"}");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
// Send success (device will restart, client may not receive this)
|
||||
httpd_resp_set_type(req, "application/json");
|
||||
httpd_resp_sendstr(req, "{\"success\":true,\"data\":{\"message\":\"OTA update complete, restarting...\"}}");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// URI Registration
|
||||
// ============================================================================
|
||||
@@ -593,6 +794,24 @@ static const httpd_uri_t uri_api_system_status = {
|
||||
.handler = api_system_status_handler,
|
||||
};
|
||||
|
||||
static const httpd_uri_t uri_api_ota_status = {
|
||||
.uri = "/api/ota/status",
|
||||
.method = HTTP_GET,
|
||||
.handler = api_ota_status_handler,
|
||||
};
|
||||
|
||||
static const httpd_uri_t uri_api_ota_update = {
|
||||
.uri = "/api/ota/update",
|
||||
.method = HTTP_POST,
|
||||
.handler = api_ota_update_handler,
|
||||
};
|
||||
|
||||
static const httpd_uri_t uri_api_ota_upload = {
|
||||
.uri = "/api/ota/upload",
|
||||
.method = HTTP_POST,
|
||||
.handler = api_ota_upload_handler,
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// Public Functions
|
||||
// ============================================================================
|
||||
@@ -639,6 +858,9 @@ esp_err_t web_server_start(void) {
|
||||
httpd_register_uri_handler(server, &uri_ws_serial);
|
||||
httpd_register_uri_handler(server, &uri_api_system_info);
|
||||
httpd_register_uri_handler(server, &uri_api_system_status);
|
||||
httpd_register_uri_handler(server, &uri_api_ota_status);
|
||||
httpd_register_uri_handler(server, &uri_api_ota_update);
|
||||
httpd_register_uri_handler(server, &uri_api_ota_upload);
|
||||
|
||||
// Register UART callback for WebSocket broadcast at startup
|
||||
esp_err_t cb_ret = uart_register_rx_callback(uart_to_ws_callback);
|
||||
|
||||
Reference in New Issue
Block a user