#pragma once /** * @file BleManager.h * @brief BLE Central (client) manager using NimBLE-Arduino. * * Responsibilities: * - Scan for the Marine Gateway peripheral ("MarineGateway") * - Establish a secure, bonded connection (passkey entry via Serial/Web) * - Subscribe to all five NOTIFY characteristics and parse incoming JSON * - Provide thread-safe access to the aggregated BoatState * - Send autopilot and admin commands * - Manage automatic reconnection */ #include "Config.h" #include class BleManager : public NimBLEClientCallbacks, public NimBLEScanCallbacks { public: /** * @brief Initialise the NimBLE stack and start scanning. * Must be called once from setup(). */ void begin(); /** * @brief Call from loop() — handles reconnect scheduling and * dispatches any pending state changes. */ void update(); // ── Data access (thread-safe) ───────────────────────────────────────────── /** * @brief Copy the current BoatState snapshot into @p dst. * Protected by an internal mutex. */ void getBoatState(BoatState& dst) const; /** @brief Returns true when actively connected to the gateway. */ bool isConnected() const; /** @brief Current BLE status for status LED / UI. */ AppState::BleStatus status() const { return _status; } // ── Command senders ─────────────────────────────────────────────────────── /** @brief Send an autopilot command (e.g. "adjust+1"). */ bool sendAutopilotCmd(const char* command); /** @brief Send a raw admin JSON command string. */ bool sendAdminCmd(const char* jsonCmd); // Convenience admin helpers bool sendRestart(); bool sendWifiSta(const char* ssid, const char* password); bool sendWifiAp(const char* ssid, const char* password); // ── Passkey (pairing) ───────────────────────────────────────────────────── /** * @brief Provide the passkey entered by the user. * Called by the WiFi web UI when the user submits the PIN. */ void providePasskey(uint32_t pin); private: // BLE callback trampolines declared in BleManager.cpp friend void navNotifyCB(NimBLERemoteCharacteristic*, uint8_t*, size_t, bool); friend void windNotifyCB(NimBLERemoteCharacteristic*, uint8_t*, size_t, bool); friend void autopilotNotifyCB(NimBLERemoteCharacteristic*, uint8_t*, size_t, bool); friend void perfNotifyCB(NimBLERemoteCharacteristic*, uint8_t*, size_t, bool); friend void adminNotifyCB(NimBLERemoteCharacteristic*, uint8_t*, size_t, bool); // ── NimBLEClientCallbacks ───────────────────────────────────────────────── void onConnect(NimBLEClient* pClient) override; void onDisconnect(NimBLEClient* pClient, int reason) override; void onConfirmPasskey(NimBLEConnInfo& connInfo, uint32_t pin) override; void onAuthenticationComplete(NimBLEConnInfo& connInfo) override; // ── NimBLEScanCallbacks ─────────────────────────────────────────────────── void onResult(const NimBLEAdvertisedDevice* device) override; void onScanEnd(const NimBLEScanResults& results, int reason) override; // ── Internal helpers ────────────────────────────────────────────────────── bool _connectToServer(const NimBLEAddress& addr); bool _subscribeAll(); void _startScan(); void _scheduleReconnect(); // Notification callbacks (one per characteristic) void _onNavNotify (NimBLERemoteCharacteristic*, uint8_t*, size_t, bool); void _onWindNotify (NimBLERemoteCharacteristic*, uint8_t*, size_t, bool); void _onAutopilotNotify(NimBLERemoteCharacteristic*, uint8_t*, size_t, bool); void _onPerfNotify (NimBLERemoteCharacteristic*, uint8_t*, size_t, bool); void _onAdminNotify (NimBLERemoteCharacteristic*, uint8_t*, size_t, bool); // JSON parsers void _parseNav (const char* json); void _parseWind (const char* json); void _parseAutopilot(const char* json); void _parsePerf (const char* json); void _parseAdmin (const char* json); // ── State ───────────────────────────────────────────────────────────────── NimBLEClient* _pClient = nullptr; NimBLEAddress _targetAddr; bool _targetFound = false; bool _doConnect = false; bool _subscribePending = false; bool _subscribed = false; uint32_t _reconnectAt = 0; ///< millis() when next reconnect is due AppState::BleStatus _status = AppState::BleStatus::IDLE; mutable portMUX_TYPE _mux = portMUX_INITIALIZER_UNLOCKED; BoatState _state; // Pending passkey from the web UI volatile bool _passkeyPending = false; volatile uint32_t _passkey = 0; }; // Singleton accessor extern BleManager bleManager;