diff --git a/firmware/src/main.cpp b/firmware/src/main.cpp index 54e6cda..8fdf7f6 100644 --- a/firmware/src/main.cpp +++ b/firmware/src/main.cpp @@ -18,8 +18,9 @@ #define CAM_INTERVAL_MS (1000 / CAM_FPS) #define REPORT_INTERVAL_S 3600 -static DeviceConfig g_cfg; -static CVState g_cv; +static DeviceConfig g_cfg; +static CVState g_cv; +static SemaphoreHandle_t s_cv_mutex = nullptr; // LED: simple on/off — blink patterns can be added later static void led_set(bool on) { digitalWrite(LED_PIN, on ? HIGH : LOW); } @@ -38,10 +39,13 @@ static void check_factory_reset() { // Camera + CV task — runs on core 1 at 5 fps static void task_camera(void*) { - uint8_t frame[CV_PIXELS]; + static uint8_t frame[CV_PIXELS]; // static: avoids 9KB on task stack while (true) { if (camera_capture_96(frame)) { - cv_process(g_cv, frame, g_cfg.line_offset); + if (xSemaphoreTake(s_cv_mutex, pdMS_TO_TICKS(100)) == pdTRUE) { + cv_process(g_cv, frame, g_cfg.line_offset); + xSemaphoreGive(s_cv_mutex); + } } vTaskDelay(pdMS_TO_TICKS(CAM_INTERVAL_MS)); } @@ -49,13 +53,18 @@ static void task_camera(void*) { // Hourly reporter task — runs on core 0 static void task_reporter(void*) { - uint32_t last_report_ts = (uint32_t)(time(nullptr)); + uint32_t last_report_ts = 0; // 0 = not initialized yet + while (true) { vTaskDelay(pdMS_TO_TICKS(10000)); // check every 10s uint32_t now = (uint32_t)(time(nullptr)); - // Skip if NTP not synced or hour not elapsed - if (now < 1700000000UL || (now - last_report_ts) < REPORT_INTERVAL_S) continue; + if (now < 1700000000UL) continue; // NTP not synced + + // First valid timestamp — initialize without reporting + if (last_report_ts == 0) { last_report_ts = now; continue; } + + if ((now - last_report_ts) < REPORT_INTERVAL_S) continue; uint32_t period_start = last_report_ts; uint32_t period_end = now; @@ -65,9 +74,17 @@ static void task_reporter(void*) { ble_scanner_pause(); led_set(true); // yellow indicator (single LED: on = uploading) - CameraHourlyRecord cam_rec = {period_start, period_end, - g_cv.entries, g_cv.exits}; - cv_reset_counts(g_cv); + CameraHourlyRecord cam_rec; + if (xSemaphoreTake(s_cv_mutex, pdMS_TO_TICKS(500)) == pdTRUE) { + cam_rec = {period_start, period_end, g_cv.entries, g_cv.exits}; + cv_reset_counts(g_cv); + xSemaphoreGive(s_cv_mutex); + } else { + // Failed to acquire — skip this cycle, will report next hour + ble_scanner_resume(); + led_set(false); + continue; + } BLEHourlyRecord ble_rec = ble_scanner_collect(period_start, period_end); @@ -124,6 +141,8 @@ void setup() { ble_scanner_start(); + s_cv_mutex = xSemaphoreCreateMutex(); + xTaskCreatePinnedToCore(task_camera, "cam", 4096, nullptr, 2, nullptr, 1); xTaskCreatePinnedToCore(task_reporter, "rep", 8192, nullptr, 1, nullptr, 0); }