src.nth.io/

summaryrefslogtreecommitdiff
path: root/main/state_machine.c
diff options
context:
space:
mode:
Diffstat (limited to 'main/state_machine.c')
-rw-r--r--main/state_machine.c25
1 files changed, 23 insertions, 2 deletions
diff --git a/main/state_machine.c b/main/state_machine.c
index 5e688bc..eb2548c 100644
--- a/main/state_machine.c
+++ b/main/state_machine.c
@@ -4,15 +4,22 @@
#include "esp_log.h"
#include "esp_timer.h"
#include "freertos/FreeRTOS.h"
+#include "freertos/semphr.h"
#include "freertos/task.h"
#include "display.h"
+#include "jpeg_decoder.h"
#include "local_screens.h"
#include "state_client.h"
static const char *TAG = "state";
static esp_timer_handle_t s_idle_timer;
+// Serializes whole transitions (state write + display side-effects).
+// Without it, concurrent wake+sleep callers (HTTP task, touch task, idle
+// timer) can interleave display_wake/display_sleep after the state lock
+// is dropped and leave the backlight not matching st->state.
+static SemaphoreHandle_t s_transition_mutex;
// ms = idle_timeout_ms snapshotted under the caller's state lock.
// ms == 0 disables the timer (caller's idle_timeout_ms feature).
@@ -31,6 +38,8 @@ static void idle_timer_fired(void *arg)
esp_err_t state_machine_init(void)
{
+ s_transition_mutex = xSemaphoreCreateMutex();
+ if (!s_transition_mutex) return ESP_ERR_NO_MEM;
esp_timer_create_args_t args = {
.callback = &idle_timer_fired,
.name = "viewport_idle",
@@ -43,11 +52,14 @@ esp_err_t state_machine_set(viewport_run_state_t target)
if (target != VIEWPORT_STATE_AWAKE && target != VIEWPORT_STATE_ASLEEP)
return ESP_ERR_INVALID_ARG;
+ xSemaphoreTake(s_transition_mutex, portMAX_DELAY);
+
viewport_state_lock();
viewport_state_t *st = viewport_state_get();
if (st->state == target) {
viewport_state_unlock();
+ xSemaphoreGive(s_transition_mutex);
return ESP_OK; // idempotent no-op
}
st->state = target;
@@ -63,8 +75,16 @@ esp_err_t state_machine_set(viewport_run_state_t target)
// backlight comes up. Without the delay, esp_lcd_panel_draw_bitmap
// returns before the panel has been refreshed and the user sees
// a flash of the previous /frame's contents.
- if (configured) local_screens_show_loading();
- else local_screens_show_info();
+ //
+ // Decoder lock: the stream decode-task paints under this lock,
+ // and concurrent esp_lcd_panel_draw_bitmap calls from two tasks
+ // aren't safe. If the decoder is busy >500 ms a live frame is
+ // painting anyway — skip the placeholder and just light up.
+ if (jpeg_decoder_try_lock(500)) {
+ if (configured) local_screens_show_loading();
+ else local_screens_show_info();
+ jpeg_decoder_unlock();
+ }
vTaskDelay(pdMS_TO_TICKS(33));
display_wake();
}
@@ -75,6 +95,7 @@ esp_err_t state_machine_set(viewport_run_state_t target)
if (display_is_up()) display_sleep();
ESP_LOGI(TAG, "ASLEEP");
}
+ xSemaphoreGive(s_transition_mutex);
return ESP_OK;
}