blob: 5e688bc1e9d496a59fefb193f18b5f75e9a8c1a8 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
|
#include "state_machine.h"
#include "esp_check.h"
#include "esp_log.h"
#include "esp_timer.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "display.h"
#include "local_screens.h"
#include "state_client.h"
static const char *TAG = "state";
static esp_timer_handle_t s_idle_timer;
// ms = idle_timeout_ms snapshotted under the caller's state lock.
// ms == 0 disables the timer (caller's idle_timeout_ms feature).
static void arm_idle_timer(uint32_t ms)
{
esp_timer_stop(s_idle_timer);
if (ms == 0) return;
esp_timer_start_once(s_idle_timer, (uint64_t)ms * 1000ULL);
}
static void idle_timer_fired(void *arg)
{
ESP_LOGI(TAG, "idle timer expired — sleeping");
state_machine_set_local(VIEWPORT_STATE_ASLEEP);
}
esp_err_t state_machine_init(void)
{
esp_timer_create_args_t args = {
.callback = &idle_timer_fired,
.name = "viewport_idle",
};
return esp_timer_create(&args, &s_idle_timer);
}
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;
viewport_state_lock();
viewport_state_t *st = viewport_state_get();
if (st->state == target) {
viewport_state_unlock();
return ESP_OK; // idempotent no-op
}
st->state = target;
bool configured = st->configured;
uint32_t idle_ms = st->idle_timeout_ms;
viewport_state_unlock();
if (target == VIEWPORT_STATE_AWAKE) {
if (display_is_up()) {
// Paint the placeholder BEFORE turning the backlight on, then
// wait ~33 ms (two 60 Hz refresh cycles) so the DSI engine
// actually pushes the new framebuffer to the panel before the
// 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();
vTaskDelay(pdMS_TO_TICKS(33));
display_wake();
}
arm_idle_timer(idle_ms);
ESP_LOGI(TAG, "AWAKE (%s)", configured ? "configured" : "no scrypted URL");
} else {
arm_idle_timer(0);
if (display_is_up()) display_sleep();
ESP_LOGI(TAG, "ASLEEP");
}
return ESP_OK;
}
void state_machine_frame_painted(void)
{
viewport_state_lock();
viewport_state_t *st = viewport_state_get();
bool awake = (st->state == VIEWPORT_STATE_AWAKE);
uint32_t idle_ms = st->idle_timeout_ms;
viewport_state_unlock();
if (awake) arm_idle_timer(idle_ms);
}
void state_machine_set_local(viewport_run_state_t target)
{
esp_err_t err = state_machine_set(target);
if (err != ESP_OK) return;
// Only POST when there's a Scrypted to talk to. Without a scrypted URL,
// tap toggles change the local display state without notifying anyone.
viewport_state_lock();
bool configured = viewport_state_get()->configured;
viewport_state_unlock();
if (configured) state_client_post(target);
}
viewport_run_state_t state_machine_current(void)
{
viewport_state_lock();
viewport_run_state_t s = viewport_state_get()->state;
viewport_state_unlock();
return s;
}
|