/ firmware / src / boot / system.cpp
system.cpp
  1  #include <boot/system.h>
  2  
  3  #include <config.h>
  4  #include "provisioning.h"
  5  #include <i2c.h>
  6  #include <storage.h>
  7  #include <networking/wifi.h>
  8  #include "../networking/sntp.h"
  9  #include "../networking/telnet.h"
 10  #include "../networking/ota.h"
 11  #include "../networking/update.h"
 12  #include "../networking/ble.h"
 13  #include "../programs/buttons.h"
 14  #include <led.h>
 15  #include "../power/sleep.h"
 16  #include "../programs/shell/shell.h"
 17  #include "../programs/ssh/ssh_server.h"
 18  #include "../services/http.h"
 19  #include "../services/data_logger.h"
 20  #include "../services/ws_shell.h"
 21  #include <identity.h>
 22  #include <manager.h>
 23  
 24  #include <Arduino.h>
 25  #include <freertos/timers.h>
 26  #include <Wire.h>
 27  
 28  namespace {
 29  
 30  void initialize_hardware(void) {
 31    Serial.println(F("[system] booting..."));
 32  
 33    LED.init();
 34    LED.fadeIn(colors::Gold, 600);
 35  
 36    hardware::i2c::initialize();
 37    hardware::storage::initialize();
 38    power::sleep::initialize();
 39    services::identity::initialize();
 40  }
 41  
 42  void initialize_services_and_programs(void) {
 43    sensors::manager::initialize();
 44  
 45  #if CERATINA_BLE_ENABLED
 46    networking::ble::initialize();
 47  #endif
 48  
 49    programs::shell::initialize();
 50    programs::buttons::initialize();
 51    programs::buttons::onLongPressStart([](uint8_t index) {
 52      Serial.printf("[buttons] long press on GPIO %d — requesting sleep\n", index);
 53      SleepCommand command = {};
 54      power::sleep::requestConfigured(&command);
 55    });
 56    services::data_logger::initialize();
 57  }
 58  
 59  void initialize_storage(void) {
 60    if (hardware::storage::ensureLittleFS()) {
 61      StorageQuery query = {
 62        .kind = StorageKind::LittleFS,
 63        .snapshot = {},
 64      };
 65      if (hardware::storage::accessSnapshot(&query)) {
 66        Serial.printf("[fs] LittleFS: %u/%u KB used\n",
 67                      (unsigned)(query.snapshot.used_bytes / 1024),
 68                      (unsigned)(query.snapshot.total_bytes / 1024));
 69        LED.set(colors::Yellow);
 70      }
 71    }
 72  }
 73  
 74  void run_provisioning_policy(void) {
 75    networking::update::checkSDOnBoot();
 76  
 77    if (boot::provisioning::isEnabled() && !boot::provisioning::isProvisioned()) {
 78      Serial.println(F("[prov] not provisioned — starting BLE provisioning"));
 79      LED.set(colors::Magenta);
 80      boot::provisioning::start();
 81    }
 82  }
 83  
 84  void connect_networking(void) {
 85    networking::wifi::ap::enable();
 86  
 87    if (networking::wifi::sta::connect()) {
 88      LED.set(colors::Green);
 89      Serial.printf("[wifi] connected, heap: %u bytes free\n", ESP.getFreeHeap());
 90    } else {
 91      Serial.println(F("[wifi] STA not connected — AP remains active for provisioning"));
 92      LED.set(colors::DarkOrange);
 93    }
 94  }
 95  
 96  void heartbeat_callback(TimerHandle_t) {
 97    uint32_t heap = ESP.getFreeHeap();
 98    char buf[64];
 99    snprintf(buf, sizeof(buf), "{\"uptime\":%lu,\"heap\":%u}",
100             millis() / 1000, heap);
101    services::http::emitEvent(buf, "heartbeat", millis());
102  
103    if (heap < 20000) {
104      Serial.printf("[WARN] low heap: %u bytes free (min: %u)\n",
105                    heap, ESP.getMinFreeHeap());
106    }
107  }
108  
109  void system_task(void *pvParameters) {
110    (void)pvParameters;
111  
112    initialize_hardware();
113    initialize_services_and_programs();
114    initialize_storage();
115    run_provisioning_policy();
116    connect_networking();
117    boot::system::startServices();
118  
119    TimerHandle_t heartbeat = xTimerCreate("heartbeat", pdMS_TO_TICKS(5000),
120                                            pdTRUE, nullptr, heartbeat_callback);
121    xTimerStart(heartbeat, 0);
122  
123    for (;;) {
124      services::http::service();
125      programs::shell::service();
126      services::ws_shell::service();
127      networking::telnet::service();
128      networking::ota::service();
129      programs::buttons::service();
130      power::sleep::service();
131  
132  #if CERATINA_BLE_ENABLED
133      networking::ble::service();
134  #endif
135  
136      vTaskDelay(pdMS_TO_TICKS(config::system::SHELL_SERVICE_MS));
137    }
138  }
139  
140  }
141  
142  void boot::system::startServices() {
143    static bool sntp_done = false;
144    static bool ssh_done = false;
145    static bool http_done = false;
146    static bool telnet_done = false;
147    static bool ota_done = false;
148  
149    if (!sntp_done) sntp_done = networking::sntp::sync();
150    if (!ssh_done) ssh_done = services::sshd::initialize();
151    if (!http_done) { services::http::initialize(); http_done = true; }
152    if (!telnet_done) { networking::telnet::initialize(); telnet_done = true; }
153    if (!ota_done) { networking::ota::initialize(); ota_done = true; }
154  }
155  
156  void boot::system::startTask() {
157    xTaskCreatePinnedToCore(system_task, "system", config::system::TASK_STACK,
158                            nullptr, 1, nullptr, 1);
159  }