Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 37 additions & 18 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,19 +1,38 @@
.direnv
.pio
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch
out/
.direnv/
.DS_Store
.vscode/settings.json
.vscode/extensions.json
.idea
cmake-*
.cache
.ccls
compile_commands.json
.venv/
```
# Compiled and build artifacts
*.o
*.obj
*.a
*.so
*.dll
*.exe
*.out

# Dependencies
.pycache/
__pycache__/
node_modules/
venv/
platformio.local.ini
.venv/
.env
.env.local
*.env.*

# Build directories
build/
dist/
target/

# Logs and temp files
*.log
*.tmp
*.swp

# Editors
.vscode/
.idea/

# System files
.DS_Store
Thumbs.db
```
37 changes: 37 additions & 0 deletions examples/companion_radio/MqttConfig.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#pragma once

// Configuration file for WiFi and MQTT settings
// Edit this file before flashing the device

#ifndef MQTT_CONFIG_H
#define MQTT_CONFIG_H

// ============= WiFi Configuration =============
// Set to your WiFi network credentials
#define WIFI_SSID "your_wifi_ssid"
#define WIFI_PWD "your_wifi_password"

// ============= MQTT Configuration =============
// Enable or disable MQTT feature (true/false)
#define MQTT_ENABLED false

// MQTT Broker settings
#define MQTT_SERVER "mqtt.example.com"
#define MQTT_PORT 1883

// MQTT Authentication (leave empty if not required)
#define MQTT_USER ""
#define MQTT_PASSWORD ""

// MQTT Topic prefix (e.g., "meshcore/device123")
// Messages will be published to:
// - {prefix}/messages/incoming
// - {prefix}/messages/outgoing
// - {prefix}/messages/channel
#define MQTT_TOPIC_PREFIX "meshcore/companion_radio"

// ============= Advanced Settings =============
// Debug logging for MQTT (true/false)
#define MQTT_DEBUG_LOGGING false

#endif // MQTT_CONFIG_H
186 changes: 186 additions & 0 deletions src/helpers/esp32/MqttClient.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
#include "MqttClient.h"
#include <ArduinoJson.h>

MqttClient mqtt_client;

MqttClient::MqttClient() : mqttClient(wifiClient) {
connected = false;
lastReconnectAttempt = 0;
reconnectDelay = 5000;
memset(&config, 0, sizeof(config));
memset(clientId, 0, sizeof(clientId));
}

void MqttClient::begin(const MqttConfig& cfg, const char* nodeId) {
config = cfg;

if (!config.enabled) {
MQTT_DEBUG_PRINTLN("MQTT is disabled in configuration");
return;
}

// Generate client ID from node ID
snprintf(clientId, sizeof(clientId), "meshcore-%s", nodeId);

// Configure MQTT client
mqttClient.setServer(config.mqtt_server, config.mqtt_port);
mqttClient.setBufferSize(2048); // Increase buffer for larger messages

// Start WiFi connection
MQTT_DEBUG_PRINT("Connecting to WiFi: %s ... ", config.wifi_ssid);
WiFi.begin(config.wifi_ssid, config.wifi_password);

int attempts = 0;
while (WiFi.status() != WL_CONNECTED && attempts < 30) {
delay(500);
MQTT_DEBUG_PRINT(".");
attempts++;
}

if (WiFi.status() == WL_CONNECTED) {
MQTT_DEBUG_PRINTLN("\nWiFi connected successfully");
MQTT_DEBUG_PRINT("IP address: ");
MQTT_DEBUG_PRINTLN(WiFi.localIP().toString());
} else {
MQTT_DEBUG_PRINTLN("\nWiFi connection failed!");
return;
}

connected = true;
reconnect();
}

void MqttClient::reconnect() {
if (!connected || mqttClient.connected()) {
return;
}

unsigned long now = millis();
if (now - lastReconnectAttempt < reconnectDelay) {
return;
}

lastReconnectAttempt = now;

MQTT_DEBUG_PRINT("Attempting MQTT connection to %s:%d ... ", config.mqtt_server, config.mqtt_port);

// Attempt to connect
bool result;
if (strlen(config.mqtt_user) > 0) {
result = mqttClient.connect(clientId, config.mqtt_user, config.mqtt_password);
} else {
result = mqttClient.connect(clientId);
}

if (result) {
MQTT_DEBUG_PRINTLN("connected!");
reconnectDelay = 5000; // Reset delay on success
} else {
MQTT_DEBUG_PRINT("failed, rc=%d ", mqttClient.state());
MQTT_DEBUG_PRINT("retrying in %d seconds\n", reconnectDelay / 1000);
reconnectDelay = min(reconnectDelay * 2, 60000UL); // Exponential backoff, max 60s
}
}

void MqttClient::loop() {
if (!connected || !config.enabled) {
return;
}

// Maintain MQTT connection
if (!mqttClient.connected()) {
reconnect();
return;
}

mqttClient.loop();

// Check WiFi connection
if (WiFi.status() != WL_CONNECTED) {
MQTT_DEBUG_PRINTLN("WiFi disconnected, attempting to reconnect...");
connected = false;
WiFi.begin(config.wifi_ssid, config.wifi_password);
}
}

void MqttClient::publishMessage(const char* topic, const char* payload) {
if (!connected || !mqttClient.connected()) {
MQTT_DEBUG_PRINTLN("Cannot publish: not connected to MQTT broker");
return;
}

MQTT_DEBUG_PRINT("Publishing to topic: %s\n", topic);
MQTT_DEBUG_PRINT("Payload: %s\n", payload);

if (mqttClient.publish(topic, payload, false)) {
MQTT_DEBUG_PRINTLN("Published successfully");
} else {
MQTT_DEBUG_PRINTLN("Publish failed");
}
}

void MqttClient::onIncomingMessage(const char* fromName, const char* fromPubKeyPrefix, const char* text, float snr, float rssi) {
if (!config.enabled) return;

StaticJsonDocument<512> doc;
doc["type"] = "incoming_message";
doc["from_name"] = fromName;
doc["from_pubkey_prefix"] = fromPubKeyPrefix;
doc["text"] = text;
doc["snr"] = snr;
doc["rssi"] = rssi;
doc["timestamp"] = millis();

char jsonBuffer[1024];
serializeJson(doc, jsonBuffer);

char topic[256];
snprintf(topic, sizeof(topic), "%s/messages/incoming", config.mqtt_topic_prefix);
publishMessage(topic, jsonBuffer);
}

void MqttClient::onOutgoingMessage(const char* toName, const char* toPubKeyPrefix, const char* text) {
if (!config.enabled) return;

StaticJsonDocument<512> doc;
doc["type"] = "outgoing_message";
doc["to_name"] = toName;
doc["to_pubkey_prefix"] = toPubKeyPrefix;
doc["text"] = text;
doc["timestamp"] = millis();

char jsonBuffer[1024];
serializeJson(doc, jsonBuffer);

char topic[256];
snprintf(topic, sizeof(topic), "%s/messages/outgoing", config.mqtt_topic_prefix);
publishMessage(topic, jsonBuffer);
}

void MqttClient::onChannelMessage(const char* channelName, const char* senderName, const char* text, float snr, float rssi) {
if (!config.enabled) return;

StaticJsonDocument<512> doc;
doc["type"] = "channel_message";
doc["channel"] = channelName;
doc["sender_name"] = senderName;
doc["text"] = text;
doc["snr"] = snr;
doc["rssi"] = rssi;
doc["timestamp"] = millis();

char jsonBuffer[1024];
serializeJson(doc, jsonBuffer);

char topic[256];
snprintf(topic, sizeof(topic), "%s/messages/channel", config.mqtt_topic_prefix);
publishMessage(topic, jsonBuffer);
}

void MqttClient::disconnect() {
if (mqttClient.connected()) {
mqttClient.disconnect();
}
connected = false;
MQTT_DEBUG_PRINTLN("MQTT disconnected");
}
57 changes: 57 additions & 0 deletions src/helpers/esp32/MqttClient.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#pragma once

#include <Arduino.h>
#include <WiFi.h>
#include <PubSubClient.h>

// Configuration structure for WiFi and MQTT
struct MqttConfig {
char wifi_ssid[33]; // WiFi SSID (max 32 chars + null)
char wifi_password[65]; // WiFi password (max 64 chars + null)
char mqtt_server[129]; // MQTT broker address (max 128 chars + null)
uint16_t mqtt_port; // MQTT broker port
char mqtt_user[65]; // MQTT username (optional, max 64 chars + null)
char mqtt_password[65]; // MQTT password (optional, max 64 chars + null)
char mqtt_topic_prefix[65]; // Base topic prefix (max 64 chars + null)
bool enabled; // Enable/disable MQTT feature
};

class MqttClient {
private:
WiFiClient wifiClient;
PubSubClient mqttClient;
MqttConfig config;
bool connected;
unsigned long lastReconnectAttempt;
unsigned long reconnectDelay;
char clientId[32];

void publishMessage(const char* topic, const char* payload);
void reconnect();

public:
MqttClient();

void begin(const MqttConfig& cfg, const char* nodeId);
void loop();
bool isConnected() const { return connected && mqttClient.connected(); }

// Message publishing methods
void onIncomingMessage(const char* fromName, const char* fromPubKeyPrefix, const char* text, float snr, float rssi);
void onOutgoingMessage(const char* toName, const char* toPubKeyPrefix, const char* text);
void onChannelMessage(const char* channelName, const char* senderName, const char* text, float snr, float rssi);

// Connection management
void disconnect();
};

extern MqttClient mqtt_client;

#if MQTT_DEBUG_LOGGING && ARDUINO
#include <Arduino.h>
#define MQTT_DEBUG_PRINT(F, ...) Serial.printf("MQTT: " F, ##__VA_ARGS__)
#define MQTT_DEBUG_PRINTLN(F, ...) Serial.printf("MQTT: " F "\n", ##__VA_ARGS__)
#else
#define MQTT_DEBUG_PRINT(...) {}
#define MQTT_DEBUG_PRINTLN(...) {}
#endif