feat(firmware): enable task watchdog on camera/reporter/loop tasks
30s TWDT subscribes all three long-running tasks and panics on hang. The reporter task's retry loop explicitly feeds between attempts so the 3-try sequence (worst case 52s) does not itself trip the dog. Reset reason on next boot is visible via esp_reset_reason() which EVT_BOOT already logs.
This commit is contained in:
@@ -11,6 +11,7 @@
|
|||||||
#include "event_log.h"
|
#include "event_log.h"
|
||||||
#include "net_guard.h"
|
#include "net_guard.h"
|
||||||
#include <esp_system.h>
|
#include <esp_system.h>
|
||||||
|
#include <esp_task_wdt.h>
|
||||||
|
|
||||||
// LED on GPIO2 (TimerCamera-F built-in LED) — verify against board schematic
|
// LED on GPIO2 (TimerCamera-F built-in LED) — verify against board schematic
|
||||||
// Factory reset: hold GPIO37 (BOOT button) for 5 seconds
|
// Factory reset: hold GPIO37 (BOOT button) for 5 seconds
|
||||||
@@ -53,6 +54,7 @@ static void check_factory_reset() {
|
|||||||
ESP.restart();
|
ESP.restart();
|
||||||
}
|
}
|
||||||
delay(50);
|
delay(50);
|
||||||
|
esp_task_wdt_reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,6 +62,7 @@ static void check_factory_reset() {
|
|||||||
static void task_camera(void*) {
|
static void task_camera(void*) {
|
||||||
static uint8_t frame[CV_PIXELS]; // static: avoids 9KB on task stack
|
static uint8_t frame[CV_PIXELS]; // static: avoids 9KB on task stack
|
||||||
int last_logged_track_id = 0; // diagnostic: log each new track once
|
int last_logged_track_id = 0; // diagnostic: log each new track once
|
||||||
|
esp_task_wdt_add(nullptr);
|
||||||
while (true) {
|
while (true) {
|
||||||
if (camera_capture_96(frame)) {
|
if (camera_capture_96(frame)) {
|
||||||
if (xSemaphoreTake(s_cv_mutex, pdMS_TO_TICKS(100)) == pdTRUE) {
|
if (xSemaphoreTake(s_cv_mutex, pdMS_TO_TICKS(100)) == pdTRUE) {
|
||||||
@@ -86,15 +89,18 @@ static void task_camera(void*) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
vTaskDelay(pdMS_TO_TICKS(CAM_INTERVAL_MS));
|
vTaskDelay(pdMS_TO_TICKS(CAM_INTERVAL_MS));
|
||||||
|
esp_task_wdt_reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hourly reporter task — runs on core 0
|
// Hourly reporter task — runs on core 0
|
||||||
static void task_reporter(void*) {
|
static void task_reporter(void*) {
|
||||||
uint32_t last_report_ts = 0; // 0 = not initialized yet
|
uint32_t last_report_ts = 0; // 0 = not initialized yet
|
||||||
|
esp_task_wdt_add(nullptr);
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
vTaskDelay(pdMS_TO_TICKS(10000)); // check every 10s
|
vTaskDelay(pdMS_TO_TICKS(10000)); // check every 10s
|
||||||
|
esp_task_wdt_reset();
|
||||||
|
|
||||||
uint32_t now = (uint32_t)(time(nullptr));
|
uint32_t now = (uint32_t)(time(nullptr));
|
||||||
if (now < 1700000000UL) continue; // NTP not synced
|
if (now < 1700000000UL) continue; // NTP not synced
|
||||||
@@ -203,11 +209,17 @@ void setup() {
|
|||||||
|
|
||||||
s_cv_mutex = xSemaphoreCreateMutex();
|
s_cv_mutex = xSemaphoreCreateMutex();
|
||||||
|
|
||||||
|
// Task watchdog: 30s timeout, panic on trigger so we reboot and log
|
||||||
|
// via esp_reset_reason() in EVT_BOOT on the next boot.
|
||||||
|
esp_task_wdt_init(30, /*panic=*/true);
|
||||||
|
esp_task_wdt_add(nullptr); // subscribe the Arduino loopTask
|
||||||
|
|
||||||
xTaskCreatePinnedToCore(task_camera, "cam", 8192, nullptr, 2, nullptr, 1);
|
xTaskCreatePinnedToCore(task_camera, "cam", 8192, nullptr, 2, nullptr, 1);
|
||||||
xTaskCreatePinnedToCore(task_reporter, "rep", 8192, nullptr, 1, nullptr, 0);
|
xTaskCreatePinnedToCore(task_reporter, "rep", 8192, nullptr, 1, nullptr, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
|
esp_task_wdt_reset();
|
||||||
ArduinoOTA.handle();
|
ArduinoOTA.handle();
|
||||||
check_factory_reset();
|
check_factory_reset();
|
||||||
net_guard_tick();
|
net_guard_tick();
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <freertos/semphr.h>
|
#include <freertos/semphr.h>
|
||||||
|
#include <esp_task_wdt.h>
|
||||||
|
|
||||||
static std::vector<CameraHourlyRecord> s_cam_buf;
|
static std::vector<CameraHourlyRecord> s_cam_buf;
|
||||||
static std::vector<BLEHourlyRecord> s_ble_buf;
|
static std::vector<BLEHourlyRecord> s_ble_buf;
|
||||||
@@ -53,12 +54,14 @@ static bool post_json_once(const DeviceConfig& cfg, const char* path, const Stri
|
|||||||
}
|
}
|
||||||
|
|
||||||
static bool post_json(const DeviceConfig& cfg, const char* path, const String& body) {
|
static bool post_json(const DeviceConfig& cfg, const char* path, const String& body) {
|
||||||
// 3 attempts with 0/2s/5s delays. Total worst case ~ 2x(5+10)s + 7s = 37s;
|
// 3 attempts. Worst case per call: 3 × (5s connect + 10s response) + 0 + 2 + 5 = 52s.
|
||||||
// acceptable for an hourly task and within the 30s TWDT once Task 6 lands
|
// TWDT is fed before the backoff delay and before each attempt so the 30s
|
||||||
// IF TWDT is fed between attempts (see Task 6).
|
// timeout doesn't fire mid-sequence.
|
||||||
static const uint16_t DELAYS_MS[] = { 0, 2000, 5000 };
|
static const uint16_t DELAYS_MS[] = { 0, 2000, 5000 };
|
||||||
for (int i = 0; i < 3; i++) {
|
for (int i = 0; i < 3; i++) {
|
||||||
|
esp_task_wdt_reset();
|
||||||
if (DELAYS_MS[i]) vTaskDelay(pdMS_TO_TICKS(DELAYS_MS[i]));
|
if (DELAYS_MS[i]) vTaskDelay(pdMS_TO_TICKS(DELAYS_MS[i]));
|
||||||
|
esp_task_wdt_reset();
|
||||||
if (post_json_once(cfg, path, body)) return true;
|
if (post_json_once(cfg, path, body)) return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
Reference in New Issue
Block a user