2026-06-18 15:04:18 +02:00
2026-06-18 15:04:18 +02:00
2026-06-18 15:04:18 +02:00
2026-06-18 15:04:18 +02:00
2026-06-18 15:04:18 +02:00
2026-06-18 15:04:18 +02:00
2026-06-18 15:04:18 +02:00
2026-06-18 15:04:18 +02:00
2026-06-18 15:04:18 +02:00
2026-06-18 15:04:18 +02:00

/**

  • @file main.cpp
  • @brief Marine Navigation Display — application entry point.
  • Boot sequence
  • ─────────────
    1. Initialise Serial, NeoPixel LED, LittleFS config.
    1. Check for AP-mode button combo held at boot.
  • If combo detected (or first boot): start WiFi AP + web server.
    1. Otherwise start BLE scanning for the Marine Gateway.
    1. main loop:
  • a. Poll touch buttons.
  • b. Handle page navigation events.
  • c. Check BLE status → update LED.
  • d. Render display (partial refresh where possible).
  • e. Run WiFi server tasks.
  • WiFi AP ↔ BLE co-existence
  • ──────────────────────────
  • The ESP32-S3 radio is shared. We run in one of two mutually exclusive modes:
  • MODE_BLE — BLE client active, WiFi off.
  • MODE_WIFI — WiFi AP active, BLE off (configuration / OTA mode).
  • The user switches modes via the NEXT+PREV button combo (hold 3 s). */

#include <Arduino.h> #include "Config.h" #include "ConfigManager.h" #include "BleManager.h" #include "DisplayManager.h" #include "WifiManager.h" #include "TouchManager.h" #include "StatusLed.h"

// ============================================================================= // Application state // =============================================================================

enum class AppMode { BLE, WIFI_AP }; static AppMode _mode = AppMode::BLE; static bool _otaInProgress = false;

// Cached WiFi status for LED static AppState::WifiStatus _wifiStatus = AppState::WifiStatus::OFF;

// ============================================================================= // Forward declarations // ============================================================================= static void enterBleMode(); static void enterApMode(); static void handleTouchEvents(); static void updateDisplay();

// ============================================================================= // setup() // =============================================================================

void setup() { Serial.begin(115200); delay(200); Serial.println("\n╔═══════════════════════════════╗"); Serial.println("║ Marine Navigation Display ║"); Serial.printf( "║ v%-28s║\n", APP_VERSION); Serial.println("╚═══════════════════════════════╝");

// ── Status LED ────────────────────────────────────────────────────────────
statusLed.begin();
statusLed.setColor(0xFFFFFF, 20);  // brief white flash on boot

// ── Persistent config ─────────────────────────────────────────────────────
if (!configManager.begin()) {
    Serial.println("[MAIN] FATAL: Config init failed");
    // Continue with defaults — non-fatal
}

// ── Display ───────────────────────────────────────────────────────────────
displayManager.begin();  // Shows splash screen

// ── Touch ─────────────────────────────────────────────────────────────────
touchManager.begin();

// ── Detect boot-time AP combo (hold NEXT+PREV while powering on) ──────────
// Sample touch pads for 500 ms at boot
uint32_t comboStart = millis();
bool comboDetected  = false;
while (millis() - comboStart < 500) {
    if (touchRead(Pins::TOUCH_NEXT)   < Touch::THRESHOLD &&
        touchRead(Pins::TOUCH_PREV)   < Touch::THRESHOLD) {
        comboDetected = true;
        break;
    }
    delay(10);
}

if (comboDetected) {
    Serial.println("[MAIN] Boot combo detected → entering AP mode");
    enterApMode();
} else {
    enterBleMode();
}

}

// ============================================================================= // loop() // =============================================================================

void loop() { // ── Touch input ─────────────────────────────────────────────────────────── touchManager.update(); handleTouchEvents();

// ── Status LED ────────────────────────────────────────────────────────────
statusLed.update(bleManager.status(), _wifiStatus, _otaInProgress);

// ── BLE mode tasks ────────────────────────────────────────────────────────
if (_mode == AppMode::BLE) {
    bleManager.update();

    // Show BLE status banner when not connected
    static AppState::BleStatus prevBleStatus = AppState::BleStatus::IDLE;
    AppState::BleStatus curStatus = bleManager.status();
    if (curStatus != prevBleStatus) {
        prevBleStatus = curStatus;
        if (curStatus != AppState::BleStatus::CONNECTED) {
            displayManager.showBleStatus(curStatus);
        }
    }
}

// ── WiFi mode tasks ───────────────────────────────────────────────────────
if (_mode == AppMode::WIFI_AP) {
    wifiManager.update();
}

// ── Display render ────────────────────────────────────────────────────────
if (_mode == AppMode::BLE && !_otaInProgress) {
    updateDisplay();
}

// Small yield to let background tasks run
delay(20);

}

// ============================================================================= // Mode transitions // =============================================================================

static void enterBleMode() { Serial.println("[MAIN] Entering BLE mode"); _mode = AppMode::BLE; _wifiStatus = AppState::WifiStatus::OFF;

if (wifiManager.isActive()) wifiManager.stop();

bleManager.begin();
displayManager.forceFullRefresh();

}

static void enterApMode() { Serial.println("[MAIN] Entering WiFi AP mode"); _mode = AppMode::WIFI_AP; _wifiStatus = AppState::WifiStatus::AP_ACTIVE;

// BLE and WiFi share the radio; stop BLE first (NimBLE deinit is implicit
// when WiFi takes over, but we force a clean state)
// Note: NimBLEDevice::deinit(true) could be called here if needed.

const AppConfig& cfg = configManager.config();
wifiManager.begin(cfg.apSSID, cfg.apPassword);
displayManager.showApScreen(cfg.apSSID, WifiManager::AP_IP);

}

// ============================================================================= // Touch event dispatcher // =============================================================================

static void handleTouchEvents() { // AP combo (NEXT + PREV held 3 s) if (touchManager.apCombo()) { if (_mode == AppMode::BLE) { enterApMode(); } else { enterBleMode(); } return; }

if (_mode == AppMode::BLE) {
    if (touchManager.nextPressed()) {
        configManager.nextPage();
        displayManager.forceFullRefresh();
        Serial.printf("[MAIN] Page → %u\n", configManager.activePage());
    }
    if (touchManager.prevPressed()) {
        configManager.prevPage();
        displayManager.forceFullRefresh();
        Serial.printf("[MAIN] Page ← %u\n", configManager.activePage());
    }
    if (touchManager.actionPressed()) {
        // Force a full e-ink refresh (clears ghosting)
        displayManager.forceFullRefresh();
        Serial.println("[MAIN] Manual full refresh");
    }
}

}

// ============================================================================= // Display update // =============================================================================

static void updateDisplay() { BoatState state; bleManager.getBoatState(state);

const AppConfig&  cfg  = configManager.config();
const PageConfig& page = cfg.pages[cfg.activePage];

displayManager.render(state, page);

}

S
Description
No description provided
Readme
92 KiB
Languages
C++ 99.1%
Python 0.9%