#pragma once #ifdef WITH_MQTT_UPLINK #include #include #include #include "JWTHelper.h" #include "MQTTPrefs.h" #if defined(ESP_PLATFORM) #include #endif struct MQTTStatusSnapshot { int battery_mv; uint32_t uptime_secs; uint16_t error_flags; uint16_t queue_len; int noise_floor; uint32_t tx_air_secs; uint32_t rx_air_secs; uint32_t recv_errors; float radio_freq; float radio_bw; uint8_t radio_sf; uint8_t radio_cr; }; class MQTTUplink { public: explicit MQTTUplink(mesh::RTCClock& rtc, mesh::LocalIdentity& identity); void begin(FILESYSTEM* fs); void end(); void loop(const MQTTStatusSnapshot& snapshot); void publishPacket(const mesh::Packet& packet, bool is_tx, int rssi, float snr, int score = -1, int duration = -1); bool isRunning() const { return _running; } bool isActive() const; void formatStatusReply(char* reply, size_t reply_size) const; bool setEndpointEnabled(uint8_t bit, bool enabled); bool isEndpointEnabled(uint8_t bit) const; bool setPacketsEnabled(bool enabled); bool isPacketsEnabled() const { return _prefs.packets_enabled != 0; } bool setRawEnabled(bool enabled); bool isRawEnabled() const { return _prefs.raw_enabled != 0; } bool setStatusEnabled(bool enabled); bool isStatusEnabled() const { return _prefs.status_enabled != 0; } bool setTxEnabled(bool enabled); bool isTxEnabled() const { return _prefs.tx_enabled != 0; } bool setIata(const char* iata); const char* getIata() const { return _prefs.iata; } void setNodeNameSource(const char* node_name) { _node_name = node_name; } bool setOwnerPublicKey(const char* owner_public_key); const char* getOwnerPublicKey() const { return _prefs.owner_public_key; } bool setOwnerEmail(const char* owner_email); const char* getOwnerEmail() const { return _prefs.owner_email; } bool sendStatusNow(); bool isAnyBrokerConnected() const; void setNetworkStateProvider(NetworkStateProvider* network) { _network = network; } private: #if defined(ESP_PLATFORM) struct BrokerSpec { const char* key; const char* label; const char* host; const char* uri; uint8_t bit; }; struct BrokerState { const BrokerSpec* spec; esp_mqtt_client_handle_t client; bool connected; bool connect_announced; bool reconnect_pending; unsigned long last_connect_attempt; unsigned long next_connect_attempt; unsigned long connected_since_ms; time_t token_expires_at; uint8_t reconnect_failures; char username[70]; char* token; char client_id[48]; char status_topic[128]; char offline_payload[256]; }; #endif FILESYSTEM* _fs; mesh::RTCClock* _rtc; mesh::LocalIdentity* _identity; MQTTPrefs _prefs; bool _running; unsigned long _last_status_publish; MQTTStatusSnapshot _last_status; char _device_id[65]; const char* _node_name; NetworkStateProvider* _network; #if defined(ESP_PLATFORM) static constexpr uint8_t kEastmeshBit = 0x01; static constexpr uint8_t kLetsmeshEuBit = 0x02; static constexpr uint8_t kLetsmeshUsBit = 0x04; static constexpr uint8_t kMaxEnabledBrokers = 2; static const BrokerSpec kBrokerSpecs[3]; BrokerState _brokers[3]; static void handleMqttEvent(void* handler_args, esp_event_base_t base, int32_t event_id, void* event_data); bool hasEnabledBroker() const; static uint8_t normalizeEnabledMask(uint8_t mask); void formatTopic(char* dst, size_t dst_size, const char* leaf) const; void refreshIdentityStrings(); void refreshBrokerIdentity(BrokerState& broker); void refreshBrokerState(BrokerState& broker); void ensureBroker(BrokerState& broker, bool allow_new_connect); void destroyBroker(BrokerState& broker, bool reset_retry_state = true); bool refreshToken(BrokerState& broker); void publishStatus(bool online); void publishOnlineStatus(BrokerState& broker); void queuePublish(BrokerState& broker, const char* topic, const char* payload, bool retain); int buildStatusJson(char* buffer, size_t buffer_size, bool online) const; int buildPacketJson(char* buffer, size_t buffer_size, const mesh::Packet& packet, bool is_tx, int rssi, float snr, int score, int duration) const; int buildRawJson(char* buffer, size_t buffer_size, const mesh::Packet& packet, bool is_tx, int rssi, float snr) const; static void escapeJsonString(const char* input, char* output, size_t output_size); static void makeSafeToken(const char* input, char* output, size_t output_size); static void bytesToHexUpper(const uint8_t* src, size_t len, char* dst, size_t dst_size); static void formatIsoTimestamp(time_t ts, char* dst, size_t dst_size); #endif bool savePrefs(); }; #endif