/ firmware / src / networking / ota.cpp
ota.cpp
  1  #include "ota.h"
  2  
  3  #if CERATINA_OTA_ENABLED
  4  
  5  #include <Arduino.h>
  6  #include <ArduinoOTA.h>
  7  #include <WiFi.h>
  8  
  9  static bool started = false;
 10  static bool updating = false;
 11  
 12  void networking::ota::initialize(void) {
 13    if (started) return;
 14  
 15    ArduinoOTA.setHostname(config::HOSTNAME);
 16    ArduinoOTA.setPort(config::ota::PORT);
 17  
 18    if (config::ota::PASSWORD[0] != '\0')
 19      ArduinoOTA.setPassword(config::ota::PASSWORD);
 20  
 21    ArduinoOTA
 22      .onStart([]() {
 23        updating = true;
 24        const char *type = (ArduinoOTA.getCommand() == U_FLASH)
 25            ? "firmware" : "filesystem";
 26        Serial.printf("[ota] start updating %s\n", type);
 27      })
 28      .onEnd([]() {
 29        updating = false;
 30        Serial.println(F("\n[ota] update complete"));
 31      })
 32      .onProgress([](unsigned int progress, unsigned int total) {
 33        Serial.printf("[ota] %u%%\r", progress / (total / 100));
 34      })
 35      .onError([](ota_error_t error) {
 36        const char *msg = "unknown";
 37        switch (error) {
 38          case OTA_AUTH_ERROR:    msg = "auth failed"; break;
 39          case OTA_BEGIN_ERROR:   msg = "begin failed"; break;
 40          case OTA_CONNECT_ERROR: msg = "connect failed"; break;
 41          case OTA_RECEIVE_ERROR: msg = "receive failed"; break;
 42          case OTA_END_ERROR:     msg = "end failed"; break;
 43        }
 44        Serial.printf("[ota] error: %s\n", msg);
 45      });
 46  
 47    ArduinoOTA.begin();
 48    started = true;
 49    Serial.printf("[ota] listening on port %d\n", config::ota::PORT);
 50  }
 51  
 52  void networking::ota::service(void) {
 53    if (!started) return;
 54    ArduinoOTA.handle();
 55  }
 56  
 57  bool networking::ota::isInProgress(void) {
 58    return updating;
 59  }
 60  
 61  #else
 62  
 63  void networking::ota::initialize(void) {}
 64  void networking::ota::service(void) {}
 65  bool networking::ota::isInProgress(void) { return false; }
 66  
 67  #endif
 68  
 69  // ─────────────────────────────────────────────────────────────────────────────
 70  //  Tests
 71  // ─────────────────────────────────────────────────────────────────────────────
 72  #ifdef PIO_UNIT_TESTING
 73  
 74  
 75  #include "ota.h"
 76  #include <testing/utils.h>
 77  
 78  namespace networking::ota { void test(void); }
 79  
 80  #include <Arduino.h>
 81  
 82  static void test_ota_config(void) {
 83    GIVEN("OTA is configured");
 84    THEN("the configuration is valid");
 85  
 86    TEST_ASSERT_GREATER_THAN_UINT16_MESSAGE(0, config::ota::PORT,
 87      "device: OTA port must be > 0");
 88  
 89  #if CERATINA_OTA_ENABLED
 90    TEST_PRINTF("OTA enabled on port %d", config::ota::PORT);
 91  #else
 92    TEST_MESSAGE("OTA is disabled (CERATINA_OTA_ENABLED=0)");
 93  
 94  #endif
 95  }
 96  
 97  static void test_ota_noop_when_disabled(void) {
 98    WHEN("OTA functions are called while disabled");
 99    THEN("they complete as no-ops");
100  #if !CERATINA_OTA_ENABLED
101    networking::ota::initialize();
102    networking::ota::service();
103  #else
104    TEST_IGNORE_MESSAGE("OTA is enabled — test not applicable");
105  #endif
106  }
107  
108  void networking::ota::test(void) {
109    MODULE("OTA");
110    RUN_TEST(test_ota_config);
111    RUN_TEST(test_ota_noop_when_disabled);
112  }
113  
114  #endif