updates
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
idf_component_register(SRCS "main.cpp" "ota_service.cpp" INCLUDE_DIRS "."
|
||||
REQUIRES app_update esp_http_server esp_wifi esp_netif
|
||||
nvs_flash mbedtls)
|
||||
@@ -0,0 +1,3 @@
|
||||
dependencies:
|
||||
espressif/esp32-camera: "^2.1.7"
|
||||
espressif/pedestrian_detect: "^0.3.2"
|
||||
@@ -0,0 +1,251 @@
|
||||
// DFR1154 V1.1 person-position experiment using Espressif's pedestrian model.
|
||||
// Camera pin assignments match DFRobot's DFR1154 CameraWebServer example.
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
|
||||
#include "dl_image_jpeg.hpp"
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_camera.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_timer.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "pedestrian_detect.hpp"
|
||||
#include "driver/uart.h"
|
||||
#include "../../shared/GazeProtocol.h"
|
||||
#include "../../shared/TrackingGate.h"
|
||||
#include "ota_service.h"
|
||||
|
||||
namespace {
|
||||
constexpr char kTag[] = "freddy_person";
|
||||
constexpr float kDiagnosticScoreThreshold = 0.35f;
|
||||
constexpr gpio_num_t kIrLedPin = GPIO_NUM_47;
|
||||
constexpr uart_port_t kGazeUart = UART_NUM_1;
|
||||
constexpr int kGazeTxPin = 43;
|
||||
constexpr int kGazeBaud = 115200;
|
||||
|
||||
void init_gaze_uart() {
|
||||
const uart_config_t config = {
|
||||
.baud_rate = kGazeBaud,
|
||||
.data_bits = UART_DATA_8_BITS,
|
||||
.parity = UART_PARITY_DISABLE,
|
||||
.stop_bits = UART_STOP_BITS_1,
|
||||
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
|
||||
.rx_flow_ctrl_thresh = 0,
|
||||
.source_clk = UART_SCLK_DEFAULT,
|
||||
.flags = {},
|
||||
};
|
||||
ESP_ERROR_CHECK(uart_driver_install(kGazeUart, 1024, 0, 0, nullptr, 0));
|
||||
ESP_ERROR_CHECK(uart_param_config(kGazeUart, &config));
|
||||
ESP_ERROR_CHECK(uart_set_pin(kGazeUart, kGazeTxPin, UART_PIN_NO_CHANGE,
|
||||
UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));
|
||||
ESP_LOGI(kTag, "GAZE_UART_READY tx_gpio=%d baud=%d", kGazeTxPin, kGazeBaud);
|
||||
}
|
||||
|
||||
void send_gaze(uint16_t sequence, int x, int y, bool valid) {
|
||||
gaze::Frame command = {sequence, static_cast<uint16_t>(x),
|
||||
static_cast<uint16_t>(y), valid};
|
||||
uint8_t bytes[gaze::kFrameSize];
|
||||
gaze::encode(command, bytes);
|
||||
if (uart_write_bytes(kGazeUart, bytes, sizeof(bytes)) != sizeof(bytes)) {
|
||||
ESP_LOGW(kTag, "GAZE_UART_WRITE_ERROR seq=%u", sequence);
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef FREDDY_GAZE_LINK_SELF_TEST
|
||||
#define FREDDY_GAZE_LINK_SELF_TEST 0
|
||||
#endif
|
||||
|
||||
#if FREDDY_GAZE_LINK_SELF_TEST
|
||||
// Bench-only UART fault injection. Keep disabled in the normal camera build.
|
||||
void run_gaze_link_self_test() {
|
||||
uint8_t bytes[gaze::kFrameSize];
|
||||
gaze::encode({0xF000, 500, 500, false}, bytes);
|
||||
uart_write_bytes(kGazeUart, bytes, sizeof(bytes));
|
||||
vTaskDelay(pdMS_TO_TICKS(150));
|
||||
uart_write_bytes(kGazeUart, bytes, sizeof(bytes)); // Duplicate sequence.
|
||||
vTaskDelay(pdMS_TO_TICKS(150));
|
||||
gaze::encode({0xEFFF, 500, 500, false}, bytes);
|
||||
uart_write_bytes(kGazeUart, bytes, sizeof(bytes)); // Old sequence.
|
||||
vTaskDelay(pdMS_TO_TICKS(150));
|
||||
gaze::encode({0xF001, 500, 500, false}, bytes);
|
||||
bytes[2] = 2; // Wrong version.
|
||||
uart_write_bytes(kGazeUart, bytes, sizeof(bytes));
|
||||
vTaskDelay(pdMS_TO_TICKS(150));
|
||||
gaze::encode({0xF001, 500, 500, false}, bytes);
|
||||
bytes[10] ^= 1; // Damaged CRC.
|
||||
uart_write_bytes(kGazeUart, bytes, sizeof(bytes));
|
||||
vTaskDelay(pdMS_TO_TICKS(150));
|
||||
send_gaze(0xF002, 500, 500, false); // Recovery.
|
||||
ESP_LOGI(kTag, "GAZE_LINK_SELF_TEST_SENT");
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef FREDDY_CAMERA_DIAGNOSTIC_JPEG
|
||||
#define FREDDY_CAMERA_DIAGNOSTIC_JPEG 0
|
||||
#endif
|
||||
|
||||
#if FREDDY_CAMERA_DIAGNOSTIC_JPEG
|
||||
// Optional bench diagnostic: one JPEG roughly every five seconds.
|
||||
void print_jpeg_for_diagnostics(const camera_fb_t *frame, unsigned frame_number) {
|
||||
printf("FRAME_BEGIN %u %u\n", frame_number, static_cast<unsigned>(frame->len));
|
||||
for (size_t offset = 0; offset < frame->len; offset += 48) {
|
||||
printf("FRAME_HEX ");
|
||||
for (size_t i = offset; i < frame->len && i < offset + 48; ++i) {
|
||||
printf("%02x", frame->buf[i]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
printf("FRAME_END %u\n", frame_number);
|
||||
fflush(stdout);
|
||||
}
|
||||
#endif
|
||||
|
||||
camera_config_t camera_config() {
|
||||
camera_config_t config = {};
|
||||
config.ledc_channel = LEDC_CHANNEL_0;
|
||||
config.ledc_timer = LEDC_TIMER_0;
|
||||
config.pin_d0 = 16;
|
||||
config.pin_d1 = 18;
|
||||
config.pin_d2 = 21;
|
||||
config.pin_d3 = 17;
|
||||
config.pin_d4 = 14;
|
||||
config.pin_d5 = 7;
|
||||
config.pin_d6 = 6;
|
||||
config.pin_d7 = 4;
|
||||
config.pin_xclk = 5;
|
||||
config.pin_pclk = 15;
|
||||
config.pin_vsync = 1;
|
||||
config.pin_href = 2;
|
||||
config.pin_sccb_sda = 8;
|
||||
config.pin_sccb_scl = 9;
|
||||
config.pin_pwdn = -1;
|
||||
config.pin_reset = -1;
|
||||
config.xclk_freq_hz = 20000000;
|
||||
config.pixel_format = PIXFORMAT_JPEG;
|
||||
config.frame_size = FRAMESIZE_QVGA;
|
||||
config.jpeg_quality = 12;
|
||||
config.fb_count = 1;
|
||||
config.grab_mode = CAMERA_GRAB_WHEN_EMPTY;
|
||||
config.fb_location = CAMERA_FB_IN_PSRAM;
|
||||
return config;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
extern "C" void app_main(void) {
|
||||
init_gaze_uart();
|
||||
#if FREDDY_GAZE_LINK_SELF_TEST
|
||||
run_gaze_link_self_test();
|
||||
#endif
|
||||
ESP_ERROR_CHECK(gpio_reset_pin(kIrLedPin));
|
||||
ESP_ERROR_CHECK(gpio_set_direction(kIrLedPin, GPIO_MODE_OUTPUT));
|
||||
ESP_ERROR_CHECK(gpio_set_level(kIrLedPin, 1));
|
||||
ESP_LOGI(kTag, "IR_GPIO47_HIGH");
|
||||
|
||||
camera_config_t config = camera_config();
|
||||
const esp_err_t camera_error = esp_camera_init(&config);
|
||||
if (camera_error != ESP_OK) {
|
||||
ESP_LOGE(kTag, "CAMERA_INIT_ERROR 0x%X", camera_error);
|
||||
return;
|
||||
}
|
||||
sensor_t *sensor = esp_camera_sensor_get();
|
||||
if (sensor) {
|
||||
ESP_LOGI(kTag, "SENSOR_PID 0x%04X", sensor->id.PID);
|
||||
if (sensor->id.PID == OV3660_PID) sensor->set_vflip(sensor, 1);
|
||||
}
|
||||
|
||||
auto *detector = new PedestrianDetect();
|
||||
detector->set_score_thr(kDiagnosticScoreThreshold);
|
||||
ESP_LOGI(kTag, "PERSON_DETECTOR_READY threshold=%.2f", kDiagnosticScoreThreshold);
|
||||
ota_service_start();
|
||||
ota_service_confirm_healthy();
|
||||
|
||||
unsigned frame_number = 0;
|
||||
uint16_t gaze_sequence = 0;
|
||||
tracking::Gate target_gate;
|
||||
for (;;) {
|
||||
const int64_t capture_started = esp_timer_get_time();
|
||||
camera_fb_t *frame = esp_camera_fb_get();
|
||||
if (!frame) {
|
||||
ESP_LOGW(kTag, "CAPTURE_ERROR");
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
continue;
|
||||
}
|
||||
++frame_number;
|
||||
#if FREDDY_CAMERA_DIAGNOSTIC_JPEG
|
||||
if (frame_number % 20 == 1) {
|
||||
print_jpeg_for_diagnostics(frame, frame_number);
|
||||
}
|
||||
#endif
|
||||
dl::image::jpeg_img_t jpeg = {
|
||||
.data = frame->buf,
|
||||
.data_len = frame->len,
|
||||
};
|
||||
auto image = dl::image::sw_decode_jpeg(jpeg, dl::image::DL_IMAGE_PIX_TYPE_RGB888);
|
||||
esp_camera_fb_return(frame);
|
||||
if (!image.data) {
|
||||
ESP_LOGW(kTag, "DECODE_ERROR");
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
continue;
|
||||
}
|
||||
|
||||
auto &results = detector->run(image);
|
||||
const int64_t completed = esp_timer_get_time();
|
||||
const dl::detect::result_t *best = nullptr;
|
||||
int rejected_full_frame = 0;
|
||||
int rejected_full_height = 0;
|
||||
for (const auto &result : results) {
|
||||
const int box_width = result.box[2] - result.box[0];
|
||||
const int box_height = result.box[3] - result.box[1];
|
||||
// The empty-scene diagnostic repeatedly labels almost the entire image
|
||||
// as a person. Keep this shape from commanding either eye.
|
||||
if (box_width >= image.width * 9 / 10 &&
|
||||
box_height >= image.height * 9 / 10) {
|
||||
++rejected_full_frame;
|
||||
continue;
|
||||
}
|
||||
// The confirmed empty view repeatedly produces boxes running nearly
|
||||
// from the top edge to the bottom edge. Earlier real-person bench boxes
|
||||
// did not have this geometry; validate again at the installed distance.
|
||||
if (box_height >= image.height * 95 / 100 && result.box[1] <= 2) {
|
||||
++rejected_full_height;
|
||||
continue;
|
||||
}
|
||||
if (!best || result.score > best->score) best = &result;
|
||||
}
|
||||
const int center_x = best ? (best->box[0] + best->box[2]) / 2 : 0;
|
||||
const int center_y = best ? (best->box[1] + best->box[3]) / 2 : 0;
|
||||
const tracking::Decision target = target_gate.update(
|
||||
best != nullptr, center_x, center_y,
|
||||
static_cast<uint32_t>(completed / 1000));
|
||||
const int gaze_x = (!best && !target.valid)
|
||||
? 500
|
||||
: std::clamp(target.x, 0, image.width - 1) * 1000 /
|
||||
(image.width - 1);
|
||||
const int gaze_y = (!best && !target.valid)
|
||||
? 500
|
||||
: std::clamp(target.y, 0, image.height - 1) * 1000 /
|
||||
(image.height - 1);
|
||||
send_gaze(gaze_sequence++, gaze_x, gaze_y, target.valid);
|
||||
if (best) {
|
||||
ESP_LOGI(kTag,
|
||||
"PERSON t_ms=%lld score=%.3f cx=%d cy=%d box=%d,%d,%d,%d frame=%d,%d latency_ms=%lld gaze_valid=%d target_hold=%d rejected_full_height=%d",
|
||||
static_cast<long long>(completed / 1000), best->score, center_x,
|
||||
center_y, best->box[0], best->box[1], best->box[2], best->box[3],
|
||||
image.width, image.height,
|
||||
static_cast<long long>((completed - capture_started) / 1000),
|
||||
target.valid, target.held, rejected_full_height);
|
||||
} else {
|
||||
ESP_LOGI(kTag, "NO_PERSON t_ms=%lld frame=%d,%d latency_ms=%lld rejected_full_frame=%d rejected_full_height=%d gaze_valid=%d target_hold=%d",
|
||||
static_cast<long long>(completed / 1000), image.width, image.height,
|
||||
static_cast<long long>((completed - capture_started) / 1000),
|
||||
rejected_full_frame, rejected_full_height, target.valid,
|
||||
target.held);
|
||||
}
|
||||
heap_caps_free(image.data);
|
||||
vTaskDelay(pdMS_TO_TICKS(20));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
#include "ota_service.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#include "esp_event.h"
|
||||
#include "esp_http_server.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_netif.h"
|
||||
#include "esp_ota_ops.h"
|
||||
#include "esp_random.h"
|
||||
#include "esp_timer.h"
|
||||
#include "esp_wifi.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "mbedtls/sha256.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "../../shared/OtaAuth.h"
|
||||
#include "../../shared/OtaSecrets.h"
|
||||
|
||||
namespace {
|
||||
constexpr char kTag[] = "freddy_ota";
|
||||
constexpr char kDevice[] = "camera";
|
||||
constexpr char kImageTarget[] = "FREDDY_OTA_TARGET:camera";
|
||||
constexpr int kPort = 3232;
|
||||
char nonce[33] = {};
|
||||
int64_t nonce_time_us = 0;
|
||||
httpd_handle_t server = nullptr;
|
||||
|
||||
void new_nonce() {
|
||||
uint8_t random_bytes[16];
|
||||
esp_fill_random(random_bytes, sizeof(random_bytes));
|
||||
freddy_ota::to_hex(random_bytes, sizeof(random_bytes), nonce);
|
||||
nonce_time_us = esp_timer_get_time();
|
||||
}
|
||||
|
||||
esp_err_t info_handler(httpd_req_t *req) {
|
||||
new_nonce();
|
||||
const esp_partition_t *slot = esp_ota_get_next_update_partition(nullptr);
|
||||
const esp_partition_t *running = esp_ota_get_running_partition();
|
||||
char body[180];
|
||||
snprintf(body, sizeof(body),
|
||||
"{\"device\":\"%s\",\"nonce\":\"%s\",\"slot_bytes\":%u,\"running_slot\":%d}",
|
||||
kDevice, nonce, slot ? static_cast<unsigned>(slot->size) : 0,
|
||||
running ? static_cast<int>(running->subtype) - ESP_PARTITION_SUBTYPE_APP_OTA_0 : -1);
|
||||
httpd_resp_set_type(req, "application/json");
|
||||
return httpd_resp_sendstr(req, body);
|
||||
}
|
||||
|
||||
bool read_header(httpd_req_t *req, const char *name, char *out, size_t capacity) {
|
||||
const size_t length = httpd_req_get_hdr_value_len(req, name);
|
||||
return length > 0 && length < capacity &&
|
||||
httpd_req_get_hdr_value_str(req, name, out, capacity) == ESP_OK;
|
||||
}
|
||||
|
||||
void restart_task(void *) {
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
esp_restart();
|
||||
}
|
||||
|
||||
esp_err_t upload_handler(httpd_req_t *req) {
|
||||
char request_nonce[33], request_sha[65], signature[65];
|
||||
const esp_partition_t *slot = esp_ota_get_next_update_partition(nullptr);
|
||||
if (!slot || req->content_len <= 0 ||
|
||||
static_cast<size_t>(req->content_len) > slot->size ||
|
||||
!read_header(req, "X-Freddy-Nonce", request_nonce, sizeof(request_nonce)) ||
|
||||
!read_header(req, "X-Freddy-SHA256", request_sha, sizeof(request_sha)) ||
|
||||
!read_header(req, "X-Freddy-Signature", signature, sizeof(signature)) ||
|
||||
esp_timer_get_time() - nonce_time_us > 60000000 ||
|
||||
strcmp(request_nonce, nonce) != 0 ||
|
||||
!freddy_ota::verify(FREDDY_OTA_KEY, kDevice, nonce, request_sha,
|
||||
req->content_len, signature)) {
|
||||
httpd_resp_send_err(req, HTTPD_403_FORBIDDEN, "OTA authorization failed");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
nonce[0] = 0; // Prevent replay even after an interrupted upload.
|
||||
esp_ota_handle_t handle;
|
||||
esp_err_t result = esp_ota_begin(slot, req->content_len, &handle);
|
||||
if (result != ESP_OK) {
|
||||
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "OTA begin failed");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
mbedtls_sha256_context sha_context;
|
||||
mbedtls_sha256_init(&sha_context);
|
||||
result = mbedtls_sha256_starts(&sha_context, 0) == 0 ? ESP_OK : ESP_FAIL;
|
||||
uint8_t buffer[2048];
|
||||
int remaining = req->content_len;
|
||||
unsigned timeouts = 0;
|
||||
while (result == ESP_OK && remaining > 0) {
|
||||
const int received = httpd_req_recv(
|
||||
req, reinterpret_cast<char *>(buffer),
|
||||
remaining < static_cast<int>(sizeof(buffer)) ? remaining : sizeof(buffer));
|
||||
if (received == HTTPD_SOCK_ERR_TIMEOUT && ++timeouts < 5) continue;
|
||||
if (received <= 0) {
|
||||
result = ESP_FAIL;
|
||||
break;
|
||||
}
|
||||
timeouts = 0;
|
||||
result = esp_ota_write(handle, buffer, received);
|
||||
if (result == ESP_OK && mbedtls_sha256_update(&sha_context, buffer, received) != 0)
|
||||
result = ESP_FAIL;
|
||||
remaining -= received;
|
||||
}
|
||||
uint8_t digest[32] = {};
|
||||
if (result == ESP_OK &&
|
||||
mbedtls_sha256_finish(&sha_context, digest) != 0) result = ESP_FAIL;
|
||||
mbedtls_sha256_free(&sha_context);
|
||||
char actual_sha[65];
|
||||
freddy_ota::to_hex(digest, sizeof(digest), actual_sha);
|
||||
if (result == ESP_OK &&
|
||||
!freddy_ota::constant_time_equal(actual_sha, request_sha, 64))
|
||||
result = ESP_FAIL;
|
||||
if (result == ESP_OK) result = esp_ota_end(handle);
|
||||
else esp_ota_abort(handle);
|
||||
if (result == ESP_OK) result = esp_ota_set_boot_partition(slot);
|
||||
if (result != ESP_OK) {
|
||||
ESP_LOGE(kTag, "OTA upload failed: %s", esp_err_to_name(result));
|
||||
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "OTA image rejected");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
httpd_resp_set_type(req, "application/json");
|
||||
httpd_resp_sendstr(req, "{\"ok\":true}");
|
||||
xTaskCreate(restart_task, "ota_restart", 2048, nullptr, 5, nullptr);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void wifi_event(void *, esp_event_base_t base, int32_t id, void *data) {
|
||||
if (base == WIFI_EVENT && id == WIFI_EVENT_STA_DISCONNECTED) {
|
||||
esp_wifi_connect();
|
||||
} else if (base == IP_EVENT && id == IP_EVENT_STA_GOT_IP) {
|
||||
const auto *event = static_cast<ip_event_got_ip_t *>(data);
|
||||
ESP_LOGI(kTag, "OTA_READY device=%s image=%s ip=" IPSTR " port=%d",
|
||||
kDevice, kImageTarget, IP2STR(&event->ip_info.ip), kPort);
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void ota_service_start() {
|
||||
if (!FREDDY_WIFI_SSID[0] || strlen(FREDDY_OTA_KEY) < 32) {
|
||||
ESP_LOGW(kTag, "OTA_DISABLED: configure firmware/private/ first");
|
||||
return;
|
||||
}
|
||||
esp_err_t result = nvs_flash_init();
|
||||
if (result == ESP_ERR_NVS_NO_FREE_PAGES ||
|
||||
result == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
||||
ESP_ERROR_CHECK(nvs_flash_erase());
|
||||
result = nvs_flash_init();
|
||||
}
|
||||
ESP_ERROR_CHECK(result);
|
||||
ESP_ERROR_CHECK(esp_netif_init());
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
esp_netif_create_default_wifi_sta();
|
||||
wifi_init_config_t init = WIFI_INIT_CONFIG_DEFAULT();
|
||||
ESP_ERROR_CHECK(esp_wifi_init(&init));
|
||||
ESP_ERROR_CHECK(esp_event_handler_register(
|
||||
WIFI_EVENT, ESP_EVENT_ANY_ID, wifi_event, nullptr));
|
||||
ESP_ERROR_CHECK(esp_event_handler_register(
|
||||
IP_EVENT, IP_EVENT_STA_GOT_IP, wifi_event, nullptr));
|
||||
wifi_config_t config = {};
|
||||
if (strlen(FREDDY_WIFI_SSID) >= sizeof(config.sta.ssid) ||
|
||||
strlen(FREDDY_WIFI_PASSWORD) >= sizeof(config.sta.password)) {
|
||||
ESP_LOGE(kTag, "Wi-Fi credentials exceed ESP32 limits");
|
||||
return;
|
||||
}
|
||||
strncpy(reinterpret_cast<char *>(config.sta.ssid), FREDDY_WIFI_SSID,
|
||||
sizeof(config.sta.ssid) - 1);
|
||||
strncpy(reinterpret_cast<char *>(config.sta.password), FREDDY_WIFI_PASSWORD,
|
||||
sizeof(config.sta.password) - 1);
|
||||
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
|
||||
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &config));
|
||||
ESP_ERROR_CHECK(esp_wifi_start());
|
||||
ESP_ERROR_CHECK(esp_wifi_connect());
|
||||
httpd_config_t http_config = HTTPD_DEFAULT_CONFIG();
|
||||
http_config.server_port = kPort;
|
||||
http_config.stack_size = 8192;
|
||||
http_config.lru_purge_enable = true;
|
||||
ESP_ERROR_CHECK(httpd_start(&server, &http_config));
|
||||
const httpd_uri_t info = {
|
||||
.uri = "/ota/info", .method = HTTP_GET, .handler = info_handler,
|
||||
.user_ctx = nullptr,
|
||||
};
|
||||
const httpd_uri_t upload = {
|
||||
.uri = "/ota", .method = HTTP_POST, .handler = upload_handler,
|
||||
.user_ctx = nullptr,
|
||||
};
|
||||
ESP_ERROR_CHECK(httpd_register_uri_handler(server, &info));
|
||||
ESP_ERROR_CHECK(httpd_register_uri_handler(server, &upload));
|
||||
}
|
||||
|
||||
void ota_service_confirm_healthy() {
|
||||
const esp_err_t result = esp_ota_mark_app_valid_cancel_rollback();
|
||||
if (result == ESP_OK) ESP_LOGI(kTag, "OTA_IMAGE_CONFIRMED");
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
void ota_service_start();
|
||||
void ota_service_confirm_healthy();
|
||||
Reference in New Issue
Block a user