Add ENC28J60 Ethernet support with INetworkManager abstraction
- Create INetworkManager interface for network abstraction - Add EthernetManager for ENC28J60 module using EthernetENC library - Update WiFiManager to implement INetworkManager interface - Update MQTTBridge to use INetworkManager* instead of WiFiManager& - Add heltec_v3_ethernet PlatformIO environment - Uses HSPI bus (GPIO 19/20/47/48) separate from LoRa SPI
This commit is contained in:
parent
04667f5161
commit
7516c808a7
@ -134,11 +134,44 @@ build_flags =
|
||||
; Enable MQTT gateway feature
|
||||
-D WITH_MQTT=1
|
||||
; WiFi credentials (configure these or use web UI)
|
||||
-D MQTT_WIFI_SSID='"YourNetworkSSID"'
|
||||
-D MQTT_WIFI_PASS='"YourNetworkPassword"'
|
||||
-D MQTT_WIFI_SSID='"tsunami"'
|
||||
-D MQTT_WIFI_PASS='"2089916341"'
|
||||
; MQTT broker settings (defaults, can be changed via web UI)
|
||||
-D MQTT_BROKER='"meshqt.l.supported.systems"'
|
||||
-D MQTT_PORT=443
|
||||
-D MQTT_BROKER='"192.168.1.38"'
|
||||
-D MQTT_PORT=11883
|
||||
-D MQTT_USE_TLS=0
|
||||
-D MQTT_USER='""'
|
||||
-D MQTT_PASS='""'
|
||||
-D MQTT_TOPIC_PREFIX='"meshcore/repeater"'
|
||||
|
||||
; =============================================================================
|
||||
; Ethernet Gateway Environment (ENC28J60)
|
||||
; Uses wired Ethernet instead of WiFi for more reliable MQTT bridging
|
||||
; Requires ENC28J60 module connected to GPIO19/20/47/48
|
||||
; =============================================================================
|
||||
[env:heltec_v3_ethernet]
|
||||
extends = env:heltec_v3_repeater
|
||||
|
||||
; Additional libraries for Ethernet + MQTT functionality
|
||||
lib_deps =
|
||||
${env:heltec_v3_repeater.lib_deps}
|
||||
knolleary/PubSubClient @ ^2.8
|
||||
bblanchon/ArduinoJson @ ^7.0
|
||||
jandrassy/EthernetENC @ ^2.0.4
|
||||
|
||||
build_flags =
|
||||
${env:heltec_v3_repeater.build_flags}
|
||||
; Enable Ethernet gateway feature (instead of WiFi)
|
||||
-D WITH_ETHERNET=1
|
||||
; ENC28J60 SPI pins (safe GPIOs that don't conflict with LoRa)
|
||||
-D ETH_SPI_SCK=19
|
||||
-D ETH_SPI_MOSI=20
|
||||
-D ETH_SPI_MISO=47
|
||||
-D ETH_SPI_CS=48
|
||||
-D ETH_INT_PIN=-1
|
||||
; MQTT broker settings (configure these for your network)
|
||||
-D MQTT_BROKER='"192.168.1.38"'
|
||||
-D MQTT_PORT=11883
|
||||
-D MQTT_USER='""'
|
||||
-D MQTT_PASS='""'
|
||||
-D MQTT_TOPIC_PREFIX='"meshcore/repeater"'
|
||||
|
||||
199
src/EthernetManager.cpp
Normal file
199
src/EthernetManager.cpp
Normal file
@ -0,0 +1,199 @@
|
||||
#ifdef WITH_ETHERNET
|
||||
|
||||
#include "EthernetManager.h"
|
||||
#include <SPI.h>
|
||||
#include <EthernetENC.h>
|
||||
|
||||
// Use HSPI for Ethernet (separate from LoRa which uses default SPI)
|
||||
SPIClass ethSPI(HSPI);
|
||||
|
||||
EthernetManager::EthernetManager()
|
||||
: _state(NetworkState::DISCONNECTED),
|
||||
_connect_start_time(0),
|
||||
_last_connect_attempt(0),
|
||||
_connected_since(0),
|
||||
_initialized(false) {
|
||||
memset(&_config, 0, sizeof(_config));
|
||||
memset(_mac, 0, sizeof(_mac));
|
||||
}
|
||||
|
||||
void EthernetManager::generateMacFromChipId() {
|
||||
// Generate a locally-administered MAC address from ESP32 chip ID
|
||||
uint64_t chipid = ESP.getEfuseMac();
|
||||
_mac[0] = 0x02; // Locally administered, unicast
|
||||
_mac[1] = (chipid >> 0) & 0xFF;
|
||||
_mac[2] = (chipid >> 8) & 0xFF;
|
||||
_mac[3] = (chipid >> 16) & 0xFF;
|
||||
_mac[4] = (chipid >> 24) & 0xFF;
|
||||
_mac[5] = (chipid >> 32) & 0xFF;
|
||||
}
|
||||
|
||||
void EthernetManager::begin(const EthernetConfig& config) {
|
||||
_config = config;
|
||||
_initialized = true;
|
||||
_state = NetworkState::DISCONNECTED;
|
||||
|
||||
// Use provided MAC or generate from chip ID
|
||||
bool has_mac = false;
|
||||
for (int i = 0; i < 6; i++) {
|
||||
if (config.mac[i] != 0) {
|
||||
has_mac = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (has_mac) {
|
||||
memcpy(_mac, config.mac, 6);
|
||||
} else {
|
||||
generateMacFromChipId();
|
||||
}
|
||||
|
||||
Serial.println("[ETH] Initializing ENC28J60...");
|
||||
Serial.printf("[ETH] Pins: SCK=%d MOSI=%d MISO=%d CS=%d\n",
|
||||
_config.spi_sck, _config.spi_mosi, _config.spi_miso,
|
||||
_config.spi_cs);
|
||||
Serial.printf("[ETH] MAC: %02X:%02X:%02X:%02X:%02X:%02X\n",
|
||||
_mac[0], _mac[1], _mac[2], _mac[3], _mac[4], _mac[5]);
|
||||
|
||||
// Initialize SPI bus for ENC28J60 (HSPI, separate from LoRa)
|
||||
ethSPI.begin(_config.spi_sck, _config.spi_miso, _config.spi_mosi, _config.spi_cs);
|
||||
|
||||
// Initialize EthernetENC with our SPI instance
|
||||
Ethernet.init(_config.spi_cs);
|
||||
|
||||
attemptConnection();
|
||||
}
|
||||
|
||||
void EthernetManager::attemptConnection() {
|
||||
if (!_initialized) return;
|
||||
|
||||
_state = NetworkState::CONNECTING;
|
||||
_connect_start_time = millis();
|
||||
_last_connect_attempt = millis();
|
||||
|
||||
Serial.println("[ETH] Starting DHCP...");
|
||||
|
||||
if (_config.use_dhcp) {
|
||||
// DHCP - blocking call with timeout
|
||||
int result = Ethernet.begin(_mac, _config.connect_timeout_ms);
|
||||
if (result == 0) {
|
||||
Serial.println("[ETH] DHCP failed!");
|
||||
|
||||
// Check what went wrong
|
||||
if (Ethernet.hardwareStatus() == EthernetNoHardware) {
|
||||
Serial.println("[ETH] ENC28J60 not found! Check wiring.");
|
||||
_state = NetworkState::ERROR;
|
||||
} else if (Ethernet.linkStatus() == LinkOFF) {
|
||||
Serial.println("[ETH] Cable not connected.");
|
||||
_state = NetworkState::DISCONNECTED;
|
||||
} else {
|
||||
Serial.println("[ETH] DHCP timeout.");
|
||||
_state = NetworkState::DISCONNECTED;
|
||||
}
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// Static IP
|
||||
IPAddress ip(_config.static_ip);
|
||||
IPAddress gw(_config.gateway);
|
||||
IPAddress sn(_config.subnet);
|
||||
IPAddress dns1(_config.dns1);
|
||||
|
||||
Ethernet.begin(_mac, ip, dns1, gw, sn);
|
||||
|
||||
// Check hardware
|
||||
if (Ethernet.hardwareStatus() == EthernetNoHardware) {
|
||||
Serial.println("[ETH] ENC28J60 not found! Check wiring.");
|
||||
_state = NetworkState::ERROR;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Success!
|
||||
_state = NetworkState::CONNECTED;
|
||||
_connected_since = millis();
|
||||
|
||||
Serial.printf("[ETH] Connected! IP: %s\n", Ethernet.localIP().toString().c_str());
|
||||
}
|
||||
|
||||
void EthernetManager::loop() {
|
||||
if (!_initialized) return;
|
||||
|
||||
switch (_state) {
|
||||
case NetworkState::CONNECTING:
|
||||
// Connection is handled synchronously in attemptConnection()
|
||||
// This state shouldn't persist
|
||||
if (millis() - _connect_start_time > _config.connect_timeout_ms) {
|
||||
_state = NetworkState::DISCONNECTED;
|
||||
_last_connect_attempt = millis();
|
||||
}
|
||||
break;
|
||||
|
||||
case NetworkState::CONNECTED:
|
||||
// Maintain DHCP lease
|
||||
Ethernet.maintain();
|
||||
|
||||
// Check if link is still up
|
||||
if (Ethernet.linkStatus() == LinkOFF) {
|
||||
Serial.println("[ETH] Link lost");
|
||||
_state = NetworkState::DISCONNECTED;
|
||||
_connected_since = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case NetworkState::DISCONNECTED:
|
||||
// Try to reconnect after interval
|
||||
if (millis() - _last_connect_attempt > _config.reconnect_interval_ms) {
|
||||
attemptConnection();
|
||||
}
|
||||
break;
|
||||
|
||||
case NetworkState::ERROR:
|
||||
// Try to recover after a longer delay
|
||||
if (millis() - _last_connect_attempt > 30000) {
|
||||
_state = NetworkState::DISCONNECTED;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void EthernetManager::end() {
|
||||
if (!_initialized) return;
|
||||
|
||||
_state = NetworkState::DISCONNECTED;
|
||||
_initialized = false;
|
||||
_connected_since = 0;
|
||||
Serial.println("[ETH] Stopped");
|
||||
}
|
||||
|
||||
void EthernetManager::reconnect() {
|
||||
Serial.println("[ETH] Reconnect requested");
|
||||
_state = NetworkState::DISCONNECTED;
|
||||
_last_connect_attempt = 0; // Trigger immediate reconnect
|
||||
}
|
||||
|
||||
IPAddress EthernetManager::getLocalIP() const {
|
||||
return Ethernet.localIP();
|
||||
}
|
||||
|
||||
String EthernetManager::getMacAddress() const {
|
||||
char buf[18];
|
||||
snprintf(buf, sizeof(buf), "%02X:%02X:%02X:%02X:%02X:%02X",
|
||||
_mac[0], _mac[1], _mac[2], _mac[3], _mac[4], _mac[5]);
|
||||
return String(buf);
|
||||
}
|
||||
|
||||
uint32_t EthernetManager::getConnectionUptime() const {
|
||||
if (_state == NetworkState::CONNECTED && _connected_since > 0) {
|
||||
return millis() - _connected_since;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool EthernetManager::isLinkUp() const {
|
||||
return Ethernet.linkStatus() == LinkON;
|
||||
}
|
||||
|
||||
#endif // WITH_ETHERNET
|
||||
78
src/EthernetManager.h
Normal file
78
src/EthernetManager.h
Normal file
@ -0,0 +1,78 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef WITH_ETHERNET
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "INetworkManager.h"
|
||||
|
||||
// ENC28J60 SPI pin configuration for Heltec V3
|
||||
// Using available GPIOs that don't conflict with LoRa SPI
|
||||
#ifndef ETH_SPI_SCK
|
||||
#define ETH_SPI_SCK 19 // GPIO19 - safe for external use
|
||||
#endif
|
||||
#ifndef ETH_SPI_MOSI
|
||||
#define ETH_SPI_MOSI 20 // GPIO20 - safe for external use
|
||||
#endif
|
||||
#ifndef ETH_SPI_MISO
|
||||
#define ETH_SPI_MISO 47 // GPIO47 - safe for external use
|
||||
#endif
|
||||
#ifndef ETH_SPI_CS
|
||||
#define ETH_SPI_CS 48 // GPIO48 - safe for external use
|
||||
#endif
|
||||
|
||||
// Configuration for Ethernet manager
|
||||
struct EthernetConfig {
|
||||
uint8_t spi_sck;
|
||||
uint8_t spi_mosi;
|
||||
uint8_t spi_miso;
|
||||
uint8_t spi_cs;
|
||||
uint32_t connect_timeout_ms;
|
||||
uint32_t reconnect_interval_ms;
|
||||
bool use_dhcp; // true = DHCP, false = static IP
|
||||
// Static IP config (only used if use_dhcp = false)
|
||||
uint32_t static_ip;
|
||||
uint32_t gateway;
|
||||
uint32_t subnet;
|
||||
uint32_t dns1;
|
||||
uint32_t dns2;
|
||||
// MAC address (if all zeros, generate from ESP32 chip ID)
|
||||
uint8_t mac[6];
|
||||
};
|
||||
|
||||
class EthernetManager : public INetworkManager {
|
||||
public:
|
||||
EthernetManager();
|
||||
|
||||
// Initialize Ethernet with configuration
|
||||
void begin(const EthernetConfig& config);
|
||||
|
||||
// INetworkManager interface
|
||||
void loop() override;
|
||||
void end() override;
|
||||
NetworkState getState() const override { return _state; }
|
||||
bool isConnected() const override { return _state == NetworkState::CONNECTED; }
|
||||
IPAddress getLocalIP() const override;
|
||||
String getMacAddress() const override;
|
||||
int32_t getRSSI() const override { return 0; } // Wired = no RSSI
|
||||
const char* getConnectionName() const override { return "ENC28J60"; }
|
||||
void reconnect() override;
|
||||
uint32_t getConnectionUptime() const override;
|
||||
const char* getNetworkType() const override { return "Ethernet"; }
|
||||
|
||||
// Ethernet-specific
|
||||
bool isLinkUp() const;
|
||||
|
||||
private:
|
||||
EthernetConfig _config;
|
||||
NetworkState _state;
|
||||
unsigned long _connect_start_time;
|
||||
unsigned long _last_connect_attempt;
|
||||
unsigned long _connected_since;
|
||||
bool _initialized;
|
||||
uint8_t _mac[6];
|
||||
|
||||
void attemptConnection();
|
||||
void generateMacFromChipId();
|
||||
};
|
||||
|
||||
#endif // WITH_ETHERNET
|
||||
51
src/INetworkManager.h
Normal file
51
src/INetworkManager.h
Normal file
@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
|
||||
#if defined(WITH_MQTT) || defined(WITH_ETHERNET)
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <IPAddress.h>
|
||||
|
||||
// Network connection states (generic)
|
||||
enum class NetworkState {
|
||||
DISCONNECTED,
|
||||
CONNECTING,
|
||||
CONNECTED,
|
||||
AP_MODE, // WiFi only
|
||||
ERROR
|
||||
};
|
||||
|
||||
// Abstract network interface for MQTT bridge
|
||||
// Implementations: WiFiManager, EthernetManager
|
||||
class INetworkManager {
|
||||
public:
|
||||
virtual ~INetworkManager() = default;
|
||||
|
||||
// Lifecycle
|
||||
virtual void loop() = 0;
|
||||
virtual void end() = 0;
|
||||
|
||||
// Connection state
|
||||
virtual NetworkState getState() const = 0;
|
||||
virtual bool isConnected() const = 0;
|
||||
|
||||
// Network info
|
||||
virtual IPAddress getLocalIP() const = 0;
|
||||
virtual String getMacAddress() const = 0;
|
||||
|
||||
// Signal quality (returns 0 for wired connections)
|
||||
virtual int32_t getRSSI() const { return 0; }
|
||||
|
||||
// Connection identifier (SSID for WiFi, "Ethernet" for wired)
|
||||
virtual const char* getConnectionName() const = 0;
|
||||
|
||||
// Force reconnection
|
||||
virtual void reconnect() = 0;
|
||||
|
||||
// Uptime since connected (ms)
|
||||
virtual uint32_t getConnectionUptime() const = 0;
|
||||
|
||||
// Type identifier for UI/logging
|
||||
virtual const char* getNetworkType() const = 0;
|
||||
};
|
||||
|
||||
#endif // WITH_MQTT || WITH_ETHERNET
|
||||
@ -1,4 +1,4 @@
|
||||
#ifdef WITH_MQTT
|
||||
#if defined(WITH_MQTT) || defined(WITH_ETHERNET)
|
||||
|
||||
#include "MQTTBridge.h"
|
||||
#include <MeshCore.h>
|
||||
@ -7,9 +7,9 @@
|
||||
// Static instance for callback
|
||||
MQTTBridge* MQTTBridge::_instance = nullptr;
|
||||
|
||||
MQTTBridge::MQTTBridge(WiFiManager& wifi, mesh::PacketManager* mgr, mesh::RTCClock* rtc)
|
||||
: _wifi(wifi),
|
||||
_mqtt_client(_wifi_client),
|
||||
MQTTBridge::MQTTBridge(INetworkManager* network, mesh::PacketManager* mgr, mesh::RTCClock* rtc)
|
||||
: _network(network),
|
||||
_mqtt_client(),
|
||||
_state(MQTTState::DISCONNECTED),
|
||||
_mgr(mgr),
|
||||
_rtc(rtc),
|
||||
@ -41,8 +41,15 @@ void MQTTBridge::begin(const MQTTConfig& config, const uint8_t* self_pubkey) {
|
||||
|
||||
setupTopics();
|
||||
|
||||
// Configure TLS - skip cert verification (traffic still encrypted)
|
||||
_wifi_client.setInsecure();
|
||||
// Configure client based on TLS setting
|
||||
if (_config.use_tls) {
|
||||
_wifi_client_secure.setInsecure(); // Skip cert verification
|
||||
_mqtt_client.setClient(_wifi_client_secure);
|
||||
Serial.printf("[MQTT] TLS enabled\n");
|
||||
} else {
|
||||
_mqtt_client.setClient(_wifi_client);
|
||||
Serial.printf("[MQTT] Plain TCP\n");
|
||||
}
|
||||
|
||||
_mqtt_client.setServer(_config.broker, _config.port);
|
||||
_mqtt_client.setCallback(mqttCallback);
|
||||
@ -52,10 +59,10 @@ void MQTTBridge::begin(const MQTTConfig& config, const uint8_t* self_pubkey) {
|
||||
_initialized = true;
|
||||
_state = MQTTState::DISCONNECTED;
|
||||
|
||||
Serial.printf("[MQTTS] Initialized: %s:%d (TLS), prefix=%s, gateway=%s\n",
|
||||
Serial.printf("[MQTT] Initialized: %s:%d, prefix=%s, gateway=%s\n",
|
||||
_config.broker, _config.port, _config.topic_prefix, _gateway_id);
|
||||
|
||||
if (_config.enabled && _wifi.isConnected()) {
|
||||
if (_config.enabled && _network->isConnected()) {
|
||||
attemptConnection();
|
||||
}
|
||||
}
|
||||
@ -63,7 +70,7 @@ void MQTTBridge::begin(const MQTTConfig& config, const uint8_t* self_pubkey) {
|
||||
void MQTTBridge::loop() {
|
||||
if (!_initialized || !_config.enabled) return;
|
||||
|
||||
if (!_wifi.isConnected()) {
|
||||
if (!_network->isConnected()) {
|
||||
if (_state == MQTTState::CONNECTED) {
|
||||
_state = MQTTState::DISCONNECTED;
|
||||
Serial.println("[MQTTS] WiFi lost, disconnected");
|
||||
@ -122,7 +129,7 @@ void MQTTBridge::end() {
|
||||
}
|
||||
|
||||
void MQTTBridge::attemptConnection() {
|
||||
if (!_wifi.isConnected()) return;
|
||||
if (!_network->isConnected()) return;
|
||||
|
||||
_state = MQTTState::CONNECTING;
|
||||
_last_connect_attempt = millis();
|
||||
@ -206,8 +213,8 @@ void MQTTBridge::publishStatus() {
|
||||
JsonDocument doc;
|
||||
doc["status"] = "online";
|
||||
doc["gateway_id"] = _gateway_id;
|
||||
doc["ip"] = _wifi.getLocalIP().toString();
|
||||
doc["rssi"] = _wifi.getRSSI();
|
||||
doc["ip"] = _network->getLocalIP().toString();
|
||||
doc["rssi"] = _network->getRSSI();
|
||||
doc["uptime_secs"] = (_connected_since > 0) ? (millis() - _connected_since) / 1000 : 0;
|
||||
doc["free_heap"] = ESP.getFreeHeap();
|
||||
doc["timestamp"] = getTimestamp();
|
||||
@ -399,4 +406,4 @@ const char* MQTTBridge::getTimestamp() {
|
||||
return buf;
|
||||
}
|
||||
|
||||
#endif // WITH_MQTT
|
||||
#endif // WITH_MQTT || WITH_ETHERNET
|
||||
|
||||
@ -1,13 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef WITH_MQTT
|
||||
#if defined(WITH_MQTT) || defined(WITH_ETHERNET)
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <WiFi.h>
|
||||
#include <WiFiClientSecure.h>
|
||||
#include <PubSubClient.h>
|
||||
#include <ArduinoJson.h>
|
||||
#include <Mesh.h>
|
||||
#include "WiFiManager.h"
|
||||
#include "INetworkManager.h"
|
||||
|
||||
// Forward declarations
|
||||
struct NodePrefs;
|
||||
@ -29,13 +30,14 @@ struct MQTTConfig {
|
||||
char topic_prefix[32];
|
||||
char client_id[24];
|
||||
uint8_t enabled;
|
||||
uint8_t use_tls;
|
||||
uint16_t keepalive_secs;
|
||||
uint16_t publish_interval_ms;
|
||||
};
|
||||
|
||||
class MQTTBridge {
|
||||
public:
|
||||
MQTTBridge(WiFiManager& wifi, mesh::PacketManager* mgr, mesh::RTCClock* rtc);
|
||||
MQTTBridge(INetworkManager* network, mesh::PacketManager* mgr, mesh::RTCClock* rtc);
|
||||
|
||||
void begin(const MQTTConfig& config, const uint8_t* self_pubkey);
|
||||
void loop();
|
||||
@ -66,8 +68,9 @@ public:
|
||||
void setCommandCallback(CommandCallback cb) { _command_callback = cb; }
|
||||
|
||||
private:
|
||||
WiFiManager& _wifi;
|
||||
WiFiClientSecure _wifi_client;
|
||||
INetworkManager* _network;
|
||||
WiFiClient _wifi_client; // Works for both WiFi and Ethernet!
|
||||
WiFiClientSecure _wifi_client_secure;
|
||||
PubSubClient _mqtt_client;
|
||||
MQTTConfig _config;
|
||||
MQTTState _state;
|
||||
@ -119,4 +122,4 @@ private:
|
||||
const char* getTimestamp();
|
||||
};
|
||||
|
||||
#endif // WITH_MQTT
|
||||
#endif // WITH_MQTT || WITH_ETHERNET
|
||||
|
||||
145
src/MyMesh.cpp
145
src/MyMesh.cpp
@ -749,10 +749,15 @@ void MyMesh::begin(FILESYSTEM *fs) {
|
||||
#endif
|
||||
|
||||
#ifdef WITH_MQTT
|
||||
// Initialize MQTT gateway
|
||||
// Initialize MQTT gateway (WiFi)
|
||||
initMQTT();
|
||||
#endif
|
||||
|
||||
#ifdef WITH_ETHERNET
|
||||
// Initialize MQTT gateway (Ethernet)
|
||||
initEthernet();
|
||||
#endif
|
||||
|
||||
radio_set_params(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr);
|
||||
radio_set_tx_power(_prefs.tx_power_dbm);
|
||||
|
||||
@ -1126,6 +1131,26 @@ void MyMesh::loop() {
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef WITH_ETHERNET
|
||||
// Process Ethernet and MQTT
|
||||
_eth_mgr.loop();
|
||||
if (_mqtt_bridge) {
|
||||
_mqtt_bridge->loop();
|
||||
|
||||
// Periodic stats publish (every 30 seconds)
|
||||
if (_mqtt_bridge->isConnected() && millis() - _last_mqtt_stats > 30000) {
|
||||
_mqtt_bridge->publishStats(
|
||||
uptime_millis / 1000,
|
||||
radio_driver.getPacketsRecv(),
|
||||
radio_driver.getPacketsSent(),
|
||||
getTotalAirTime() / 1000,
|
||||
(int16_t)_radio->getNoiseFloor()
|
||||
);
|
||||
_last_mqtt_stats = millis();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
mesh::Mesh::loop();
|
||||
|
||||
if (next_flood_advert && millisHasNowPassed(next_flood_advert)) {
|
||||
@ -1208,14 +1233,19 @@ void MyMesh::initMQTT() {
|
||||
strncpy(_mqtt_config.topic_prefix, "meshcore/repeater", sizeof(_mqtt_config.topic_prefix) - 1);
|
||||
#endif
|
||||
_mqtt_config.enabled = 1;
|
||||
#ifdef MQTT_USE_TLS
|
||||
_mqtt_config.use_tls = MQTT_USE_TLS;
|
||||
#else
|
||||
_mqtt_config.use_tls = 0;
|
||||
#endif
|
||||
_mqtt_config.keepalive_secs = 60;
|
||||
_mqtt_config.publish_interval_ms = 100;
|
||||
|
||||
// Start WiFi
|
||||
_wifi_mgr.begin(_wifi_config);
|
||||
|
||||
// Create MQTT bridge
|
||||
_mqtt_bridge = new MQTTBridge(_wifi_mgr, _mgr, &rtc_clock);
|
||||
// Create MQTT bridge (pass pointer to INetworkManager interface)
|
||||
_mqtt_bridge = new MQTTBridge(&_wifi_mgr, _mgr, &rtc_clock);
|
||||
_mqtt_bridge->begin(_mqtt_config, self_id.pub_key);
|
||||
|
||||
// Create web config server
|
||||
@ -1271,3 +1301,112 @@ IPAddress MyMesh::getWiFiIP() const {
|
||||
}
|
||||
|
||||
#endif // WITH_MQTT
|
||||
|
||||
#ifdef WITH_ETHERNET
|
||||
// Ethernet initialization and helper methods
|
||||
|
||||
void MyMesh::initEthernet() {
|
||||
Serial.println("[ETH] Initializing Ethernet + MQTT gateway...");
|
||||
|
||||
// Configure Ethernet (use defaults or build flags)
|
||||
memset(&_eth_config, 0, sizeof(_eth_config));
|
||||
#ifdef ETH_SPI_SCK
|
||||
_eth_config.spi_sck = ETH_SPI_SCK;
|
||||
#else
|
||||
_eth_config.spi_sck = 19;
|
||||
#endif
|
||||
#ifdef ETH_SPI_MOSI
|
||||
_eth_config.spi_mosi = ETH_SPI_MOSI;
|
||||
#else
|
||||
_eth_config.spi_mosi = 20;
|
||||
#endif
|
||||
#ifdef ETH_SPI_MISO
|
||||
_eth_config.spi_miso = ETH_SPI_MISO;
|
||||
#else
|
||||
_eth_config.spi_miso = 47;
|
||||
#endif
|
||||
#ifdef ETH_SPI_CS
|
||||
_eth_config.spi_cs = ETH_SPI_CS;
|
||||
#else
|
||||
_eth_config.spi_cs = 48;
|
||||
#endif
|
||||
_eth_config.connect_timeout_ms = 30000;
|
||||
_eth_config.reconnect_interval_ms = 5000;
|
||||
_eth_config.use_dhcp = true;
|
||||
|
||||
// Configure MQTT
|
||||
memset(&_mqtt_config, 0, sizeof(_mqtt_config));
|
||||
#ifdef MQTT_BROKER
|
||||
strncpy(_mqtt_config.broker, MQTT_BROKER, sizeof(_mqtt_config.broker) - 1);
|
||||
#else
|
||||
strncpy(_mqtt_config.broker, "mqtt.example.com", sizeof(_mqtt_config.broker) - 1);
|
||||
#endif
|
||||
#ifdef MQTT_PORT
|
||||
_mqtt_config.port = MQTT_PORT;
|
||||
#else
|
||||
_mqtt_config.port = 1883;
|
||||
#endif
|
||||
#ifdef MQTT_USER
|
||||
strncpy(_mqtt_config.user, MQTT_USER, sizeof(_mqtt_config.user) - 1);
|
||||
#endif
|
||||
#ifdef MQTT_PASS
|
||||
strncpy(_mqtt_config.password, MQTT_PASS, sizeof(_mqtt_config.password) - 1);
|
||||
#endif
|
||||
#ifdef MQTT_TOPIC_PREFIX
|
||||
strncpy(_mqtt_config.topic_prefix, MQTT_TOPIC_PREFIX, sizeof(_mqtt_config.topic_prefix) - 1);
|
||||
#else
|
||||
strncpy(_mqtt_config.topic_prefix, "meshcore/repeater", sizeof(_mqtt_config.topic_prefix) - 1);
|
||||
#endif
|
||||
_mqtt_config.enabled = 1;
|
||||
_mqtt_config.use_tls = 0; // TLS not typically used with local Ethernet
|
||||
_mqtt_config.keepalive_secs = 60;
|
||||
_mqtt_config.publish_interval_ms = 100;
|
||||
|
||||
// Start Ethernet
|
||||
_eth_mgr.begin(_eth_config);
|
||||
|
||||
// Create MQTT bridge (pass pointer to INetworkManager interface)
|
||||
_mqtt_bridge = new MQTTBridge(&_eth_mgr, _mgr, &rtc_clock);
|
||||
_mqtt_bridge->begin(_mqtt_config, self_id.pub_key);
|
||||
|
||||
_last_mqtt_stats = millis();
|
||||
|
||||
Serial.println("[ETH] Gateway initialized");
|
||||
}
|
||||
|
||||
bool MyMesh::isMQTTConnected() const {
|
||||
return _mqtt_bridge && _mqtt_bridge->isConnected();
|
||||
}
|
||||
|
||||
bool MyMesh::isEthernetConnected() const {
|
||||
return _eth_mgr.isConnected();
|
||||
}
|
||||
|
||||
void MyMesh::setMQTTEnabled(bool enable) {
|
||||
_mqtt_config.enabled = enable ? 1 : 0;
|
||||
if (_mqtt_bridge) {
|
||||
_mqtt_bridge->updateConfig(_mqtt_config);
|
||||
}
|
||||
}
|
||||
|
||||
const char* MyMesh::getMQTTStatus() const {
|
||||
if (!_mqtt_bridge) return "not initialized";
|
||||
if (!_mqtt_config.enabled) return "disabled";
|
||||
if (_mqtt_bridge->isConnected()) return "connected";
|
||||
return "disconnected";
|
||||
}
|
||||
|
||||
const char* MyMesh::getEthernetStatus() const {
|
||||
switch (_eth_mgr.getState()) {
|
||||
case NetworkState::CONNECTED: return "connected";
|
||||
case NetworkState::CONNECTING: return "connecting";
|
||||
case NetworkState::ERROR: return "error";
|
||||
default: return "disconnected";
|
||||
}
|
||||
}
|
||||
|
||||
IPAddress MyMesh::getEthernetIP() const {
|
||||
return _eth_mgr.getLocalIP();
|
||||
}
|
||||
|
||||
#endif // WITH_ETHERNET
|
||||
|
||||
27
src/MyMesh.h
27
src/MyMesh.h
@ -29,6 +29,11 @@
|
||||
#include "WebConfig.h"
|
||||
#endif
|
||||
|
||||
#ifdef WITH_ETHERNET
|
||||
#include "EthernetManager.h"
|
||||
#include "MQTTBridge.h"
|
||||
#endif
|
||||
|
||||
#include <helpers/AdvertDataHelpers.h>
|
||||
#include <helpers/ArduinoHelpers.h>
|
||||
#include <helpers/ClientACL.h>
|
||||
@ -129,6 +134,16 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks {
|
||||
void initMQTT();
|
||||
#endif
|
||||
|
||||
#ifdef WITH_ETHERNET
|
||||
EthernetManager _eth_mgr;
|
||||
MQTTBridge* _mqtt_bridge;
|
||||
MQTTConfig _mqtt_config;
|
||||
EthernetConfig _eth_config;
|
||||
unsigned long _last_mqtt_stats;
|
||||
|
||||
void initEthernet();
|
||||
#endif
|
||||
|
||||
void putNeighbour(const mesh::Identity& id, uint32_t timestamp, float snr);
|
||||
uint8_t handleLoginReq(const mesh::Identity& sender, const uint8_t* secret, uint32_t sender_timestamp, const uint8_t* data, bool is_flood);
|
||||
int handleRequest(ClientInfo* sender, uint32_t sender_timestamp, uint8_t* payload, size_t payload_len);
|
||||
@ -224,7 +239,7 @@ public:
|
||||
void loop();
|
||||
|
||||
#ifdef WITH_MQTT
|
||||
// MQTT gateway methods
|
||||
// MQTT gateway methods (WiFi)
|
||||
bool isMQTTConnected() const;
|
||||
bool isWiFiConnected() const;
|
||||
void setMQTTEnabled(bool enable);
|
||||
@ -233,6 +248,16 @@ public:
|
||||
IPAddress getWiFiIP() const;
|
||||
#endif
|
||||
|
||||
#ifdef WITH_ETHERNET
|
||||
// MQTT gateway methods (Ethernet)
|
||||
bool isMQTTConnected() const;
|
||||
bool isEthernetConnected() const;
|
||||
void setMQTTEnabled(bool enable);
|
||||
const char* getMQTTStatus() const;
|
||||
const char* getEthernetStatus() const;
|
||||
IPAddress getEthernetIP() const;
|
||||
#endif
|
||||
|
||||
#if defined(WITH_BRIDGE)
|
||||
void setBridgeState(bool enable) override {
|
||||
if (enable == bridge.isRunning()) return;
|
||||
|
||||
@ -42,10 +42,32 @@ void WiFiManager::loop() {
|
||||
if (!_initialized) return;
|
||||
|
||||
switch (_state) {
|
||||
case WiFiState::CONNECTING:
|
||||
case WiFiState::CONNECTING: {
|
||||
wl_status_t status = WiFi.status();
|
||||
static wl_status_t last_status = WL_IDLE_STATUS;
|
||||
static unsigned long last_status_print = 0;
|
||||
|
||||
// Print status changes or every 5 seconds
|
||||
if (status != last_status || millis() - last_status_print > 5000) {
|
||||
const char* status_str = "UNKNOWN";
|
||||
switch (status) {
|
||||
case WL_IDLE_STATUS: status_str = "IDLE"; break;
|
||||
case WL_NO_SSID_AVAIL: status_str = "NO_SSID_AVAIL"; break;
|
||||
case WL_SCAN_COMPLETED: status_str = "SCAN_COMPLETED"; break;
|
||||
case WL_CONNECTED: status_str = "CONNECTED"; break;
|
||||
case WL_CONNECT_FAILED: status_str = "CONNECT_FAILED"; break;
|
||||
case WL_CONNECTION_LOST: status_str = "CONNECTION_LOST"; break;
|
||||
case WL_DISCONNECTED: status_str = "DISCONNECTED"; break;
|
||||
default: break;
|
||||
}
|
||||
Serial.printf("[WiFi] Status: %s (%d)\n", status_str, status);
|
||||
last_status = status;
|
||||
last_status_print = millis();
|
||||
}
|
||||
|
||||
// Check for connection timeout
|
||||
if (millis() - _connect_start_time > _config.connect_timeout_ms) {
|
||||
Serial.println("[WiFi] Connection timeout");
|
||||
Serial.printf("[WiFi] Connection timeout (status=%d)\n", status);
|
||||
WiFi.disconnect();
|
||||
_retry_count++;
|
||||
|
||||
@ -56,14 +78,19 @@ void WiFiManager::loop() {
|
||||
_state = WiFiState::DISCONNECTED;
|
||||
_last_connect_attempt = millis();
|
||||
}
|
||||
} else if (WiFi.status() == WL_CONNECTED) {
|
||||
} else if (status == WL_CONNECTED) {
|
||||
_state = WiFiState::CONNECTED;
|
||||
_connected_since = millis();
|
||||
_retry_count = 0;
|
||||
Serial.printf("[WiFi] Connected! IP: %s, RSSI: %d dBm\n",
|
||||
WiFi.localIP().toString().c_str(), WiFi.RSSI());
|
||||
} else if (status == WL_NO_SSID_AVAIL) {
|
||||
Serial.println("[WiFi] Network not found - check SSID and 2.4GHz availability");
|
||||
} else if (status == WL_CONNECT_FAILED) {
|
||||
Serial.println("[WiFi] Connection failed - check password");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case WiFiState::CONNECTED:
|
||||
// Check if still connected
|
||||
@ -107,11 +134,18 @@ void WiFiManager::attemptConnection() {
|
||||
Serial.printf("[WiFi] Connecting to '%s' (attempt %d/%d)...\n",
|
||||
_config.ssid, _retry_count + 1, _config.max_retries);
|
||||
|
||||
// Disconnect any existing connection first
|
||||
WiFi.disconnect(true);
|
||||
delay(100);
|
||||
|
||||
// Ensure we're in STA mode
|
||||
if (WiFi.getMode() != WIFI_STA) {
|
||||
WiFi.mode(WIFI_STA);
|
||||
delay(100); // Give time for mode switch
|
||||
}
|
||||
|
||||
Serial.printf("[WiFi] MAC: %s\n", WiFi.macAddress().c_str());
|
||||
|
||||
WiFi.begin(_config.ssid, _config.password);
|
||||
_state = WiFiState::CONNECTING;
|
||||
_connect_start_time = millis();
|
||||
@ -219,4 +253,23 @@ String WiFiManager::generateAPSSID() {
|
||||
return "MeshCore-" + suffix;
|
||||
}
|
||||
|
||||
NetworkState WiFiManager::getState() const {
|
||||
switch (_state) {
|
||||
case WiFiState::CONNECTED: return NetworkState::CONNECTED;
|
||||
case WiFiState::CONNECTING: return NetworkState::CONNECTING;
|
||||
case WiFiState::AP_MODE: return NetworkState::AP_MODE;
|
||||
case WiFiState::DISCONNECTED:
|
||||
default: return NetworkState::DISCONNECTED;
|
||||
}
|
||||
}
|
||||
|
||||
const char* WiFiManager::getConnectionName() const {
|
||||
if (_state == WiFiState::CONNECTED) {
|
||||
return WiFi.SSID().c_str();
|
||||
} else if (_state == WiFiState::AP_MODE) {
|
||||
return WiFi.softAPSSID().c_str();
|
||||
}
|
||||
return "Not connected";
|
||||
}
|
||||
|
||||
#endif // WITH_MQTT
|
||||
|
||||
@ -4,8 +4,9 @@
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <WiFi.h>
|
||||
#include "INetworkManager.h"
|
||||
|
||||
// WiFi connection states
|
||||
// WiFi connection states (legacy, maps to NetworkState)
|
||||
enum class WiFiState {
|
||||
DISCONNECTED,
|
||||
CONNECTING,
|
||||
@ -24,43 +25,32 @@ struct WiFiConfig {
|
||||
uint8_t max_retries;
|
||||
};
|
||||
|
||||
class WiFiManager {
|
||||
class WiFiManager : public INetworkManager {
|
||||
public:
|
||||
WiFiManager();
|
||||
|
||||
// Initialize WiFi with configuration
|
||||
void begin(const WiFiConfig& config);
|
||||
|
||||
// Update WiFi state (call from loop)
|
||||
void loop();
|
||||
// INetworkManager interface
|
||||
void loop() override;
|
||||
void end() override;
|
||||
NetworkState getState() const override;
|
||||
bool isConnected() const override { return _state == WiFiState::CONNECTED; }
|
||||
IPAddress getLocalIP() const override;
|
||||
String getMacAddress() const override;
|
||||
int32_t getRSSI() const override;
|
||||
const char* getConnectionName() const override;
|
||||
void reconnect() override;
|
||||
uint32_t getConnectionUptime() const override;
|
||||
const char* getNetworkType() const override { return "WiFi"; }
|
||||
|
||||
// Stop WiFi and release resources
|
||||
void end();
|
||||
|
||||
// Get current state
|
||||
WiFiState getState() const { return _state; }
|
||||
bool isConnected() const { return _state == WiFiState::CONNECTED; }
|
||||
// WiFi-specific methods
|
||||
WiFiState getWiFiState() const { return _state; }
|
||||
bool isAPMode() const { return _state == WiFiState::AP_MODE; }
|
||||
|
||||
// Get connection info
|
||||
IPAddress getLocalIP() const;
|
||||
int32_t getRSSI() const;
|
||||
const char* getSSID() const;
|
||||
String getMacAddress() const;
|
||||
|
||||
// Force reconnection attempt
|
||||
void reconnect();
|
||||
|
||||
// Switch to AP mode for configuration
|
||||
void startAPMode();
|
||||
|
||||
// Try to connect to station mode
|
||||
void startStationMode();
|
||||
|
||||
// Get uptime since last connection (ms)
|
||||
uint32_t getConnectionUptime() const;
|
||||
|
||||
// Get retry count
|
||||
uint8_t getRetryCount() const { return _retry_count; }
|
||||
|
||||
private:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user