src.nth.io/

summaryrefslogtreecommitdiff
path: root/main/app_main.c
blob: c131fe0c7559d6eec86a67953b7dd38de35bce23 (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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
#include "chip_temp.h"
#include "display.h"
#include "http_api.h"
#include "jpeg_decoder.h"
#include "local_screens.h"
#include "mdns_service.h"
#include "net_eth.h"
#include "nvs_config.h"
#include "ota.h"
#include "state_client.h"
#include "state_machine.h"
#include "stream_server.h"
#include "touch.h"
#include "viewport_state.h"

#include "esp_app_desc.h"
#include "esp_event.h"
#include "esp_log.h"
#include "esp_netif.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "nvs_flash.h"

static const char *TAG = "viewport";

// Log a status line for one subsystem and accumulate a one-letter flag into
// `status` (uppercase = up, lowercase = degraded/unavailable). Lets the
// boot log end with a single line summarizing what's actually live, so
// running with no LAN / no panel is a self-describing degraded mode rather
// than a panic.
static inline void mark(esp_err_t err, char up, char *slot)
{
    *slot = (err == ESP_OK) ? up : (char)(up + 0x20);  // 'A' -> 'a' on failure
}

static void display_setup_task(void *arg);

void app_main(void)
{
    ESP_ERROR_CHECK(nvs_flash_init());
    ESP_ERROR_CHECK(esp_netif_init());
    ESP_ERROR_CHECK(esp_event_loop_create_default());

    viewport_state_init();
    nvs_config_load();  // apply persisted config over defaults (best-effort)
    chip_temp_init();   // best-effort; readers get NAN if unavailable
    // PROJECT_VER comes from CMakeLists.txt — git short hash + dirty
    // marker. Logged in the very first line so any captured boot log
    // tells us exactly which build is running.
    const esp_app_desc_t *desc = esp_app_get_description();
    ESP_LOGI(TAG, "Scrypted Viewport boot (v%s build=%s)",
             VIEWPORT_VERSION, desc ? desc->version : "?");

    // ------------------------------------------------------------------
    // Networking. Driver starts even with no cable plugged in; we just
    // don't get a DHCP lease. mDNS + HTTP advertise / bind anyway and will
    // start serving the moment the link comes up.
    // ------------------------------------------------------------------
    char flags[5] = { '-','-','-','-', 0 };  // E M H S (display-task
                                             // subsystems D J T report
                                             // separately in dsp_init)
    esp_err_t eth_err = net_eth_init();
    mark(eth_err, 'E', &flags[0]);

    bool got_ip = false;
    if (eth_err == ESP_OK) {
        got_ip = (net_eth_wait_for_ip(15 * 1000) == ESP_OK);
        if (got_ip) {
            ESP_LOGI(TAG, "online at %s", net_eth_get_ip_str());
        } else {
            ESP_LOGW(TAG, "no DHCP lease after 15s — Ethernet driver will keep "
                          "retrying in the background; mDNS + HTTP up anyway");
        }
    }

    ESP_ERROR_CHECK(state_machine_init());
    ESP_ERROR_CHECK(state_client_init());
    mark(mdns_service_start(), 'M', &flags[1]);
    mark(http_api_start(),     'H', &flags[2]);
    // Data plane: raw TCP on :81 for back-to-back JPEG streaming,
    // bypassing per-frame HTTP overhead. /frame HTTP stays alive for
    // the snapshot one-shot and for curl debugging.
    mark(stream_server_start(81), 'S', &flags[3]);

    // If this image is still PENDING_VERIFY from a fresh OTA, mark it
    // valid after 30 s of healthy uptime so the bootloader stops
    // considering it revertible. No-op once already marked valid.
    ota_arm_healthy_timer();

    // ------------------------------------------------------------------
    // Display + I²C-bound peripherals run on their own task. ESP-IDF's
    // esp_lcd_new_dsi_bus() spins forever in a PHY-PLL busy-wait if the
    // config is even slightly off; keeping that off the main task means
    // a misconfigured panel can't take down networking + /state. The
    // task also brings up the JPEG decoder and touch (touch shares the
    // panel I²C bus and also handles long-press → info overlay, since
    // the board's BOOT button is wired to a strap pin (GPIO35) that the
    // EMAC owns at runtime).
    // ------------------------------------------------------------------
    ESP_LOGI(TAG, "boot complete — net subsystems [%s] ip=%s; "
                  "display init deferred to dsp_init task",
             flags, got_ip ? net_eth_get_ip_str() : "(no link)");

    xTaskCreate(display_setup_task, "dsp_init", 4096, NULL, 1, NULL);
}

static void display_setup_task(void *arg)
{
    char flags[4] = {'-', '-', '-', 0}; // D J T + NUL
    esp_err_t dsp_err = display_init();
    mark(dsp_err, 'D', &flags[0]);

    if (dsp_err == ESP_OK) {
        local_screens_init();
    }

    mark(jpeg_decoder_init(), 'J', &flags[1]);

    if (dsp_err == ESP_OK) {
        mark(touch_init(), 'T', &flags[2]);
    } else {
        ESP_LOGI(TAG, "touch skipped (display not up)");
    }

    ESP_LOGI(TAG, "display subsystems [%s] up", flags);

    // Briefly wake the display on boot so the FT5426 touch IC can come
    // out of its initial unresponsive state — empirically it only
    // reports contacts when the LCD has been actively streaming with
    // the backlight on at least once since power-up. A 600 ms flash is
    // enough; we then sleep so an idle device doesn't burn the
    // backlight until the idle timer fires.
    if (dsp_err == ESP_OK) {
        state_machine_set(VIEWPORT_STATE_AWAKE);
        vTaskDelay(pdMS_TO_TICKS(600));
        state_machine_set(VIEWPORT_STATE_ASLEEP);
    }

    vTaskDelete(NULL);
}