From 7c23c2f1f4291ec815c94edb92fa040eeecc1059 Mon Sep 17 00:00:00 2001 From: Peter Buchegger Date: Mon, 12 Oct 2020 00:09:34 +0200 Subject: [PATCH] big update: * add better timer functions * add better configuration * add DIGI mode --- data/is-cfg.json | 46 +++++--- src/LoRa_APRS_iGate.cpp | 248 ++++++++++++++++++++++++++++++---------- src/configuration.cpp | 202 +++++++++++--------------------- src/configuration.h | 89 ++++++++++---- 4 files changed, 346 insertions(+), 239 deletions(-) diff --git a/data/is-cfg.json b/data/is-cfg.json index 4219de8..1b3e2d3 100644 --- a/data/is-cfg.json +++ b/data/is-cfg.json @@ -1,24 +1,36 @@ { - "Wifi": + "version":1, + "callsign":"NOCALL-10", + "wifi": { - "Name":"", - "Password":"" + "active":false, + "AP": [ + { "SSID":"", "password":"" } + ] }, - "IS": + "beacon": { - "Call":"NOCALL-10", - "Password":"", - "Server":"austria.aprs2.net", - "Port":14580 - }, - "Beacon": - { - "Message":"LoRa IGATE (RX only), Info: github.com/peterus/LoRa_APRS_iGate", - "Pos": + "message":"LoRa iGATE & Digi, Info: github.com/peterus/LoRa_APRS_iGate", + "position": { - "Lat":"4819.82N", - "Long":"01418.68E" - }, - "Timeout":15 + "latitude":0.000000, + "longitude":0.000000 + } + }, + "aprs_is": + { + "active":false, + "password":"", + "server":"euro.aprs2.net", + "port":14580, + "beacon":true, + "beacon_timeout":15 + }, + "digi": + { + "active":false, + "forward_timeout":5, + "beacon":true, + "beacon_timeout":30 } } diff --git a/src/LoRa_APRS_iGate.cpp b/src/LoRa_APRS_iGate.cpp index d00795f..6ffd5cd 100644 --- a/src/LoRa_APRS_iGate.cpp +++ b/src/LoRa_APRS_iGate.cpp @@ -1,3 +1,5 @@ +#include + #include #include #include @@ -10,24 +12,29 @@ #include "pins.h" #include "settings.h" #include "display.h" -#include "power_management.h" #include "configuration.h" +#if defined(ARDUINO_T_Beam) && !defined(ARDUINO_T_Beam_V0_7) +#include "power_management.h" +PowerManagement powerManagement; +#endif + +portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED; +hw_timer_t * timer = NULL; +volatile uint secondsSinceLastAPRSISBeacon = 0; +volatile uint secondsSinceLastDigiBeacon = 0; +volatile uint secondsSinceStartup = 0; + WiFiMulti WiFiMulti; WiFiUDP ntpUDP; NTPClient timeClient(ntpUDP, 60*60); -Configuration * Config = 0; +Configuration Config; APRS_IS * aprs_is = 0; -#if defined(ARDUINO_T_Beam) && !defined(ARDUINO_T_Beam_V0_7) -PowerManagement powerManagement; -#endif LoRa_APRS lora_aprs; +std::shared_ptr BeaconMsg; -portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED; -hw_timer_t * timer = NULL; -volatile bool timerTick = false; - -int next_update = -1; +String create_lat_aprs(double lat); +String create_long_aprs(double lng); void load_config(); void setup_wifi(); @@ -37,7 +44,7 @@ void setup_ntp(); void setup_aprs_is(); void setup_timer(); -String BeaconMsg; +std::map> lastMessages; // cppcheck-suppress unusedFunction void setup() @@ -60,47 +67,58 @@ void setup() setup_display(); delay(500); - Serial.println("[INFO] LoRa APRS iGate by OE5BPA (Peter Buchegger)"); - show_display("OE5BPA", "LoRa APRS iGate", "by Peter Buchegger", 3000); + Serial.println("[INFO] LoRa APRS iGate & Digi by OE5BPA (Peter Buchegger)"); + show_display("OE5BPA", "LoRa APRS iGate & Digi", "by Peter Buchegger", 3000); load_config(); - setup_wifi(); - setup_ota(); + if(Config.wifi.active) setup_wifi(); + if(Config.wifi.active) setup_ota(); setup_lora(); - setup_ntp(); - setup_aprs_is(); + if(Config.wifi.active) setup_ntp(); + if(Config.aprs_is.active) setup_aprs_is(); setup_timer(); delay(500); + Serial.println("setup done..."); } // cppcheck-suppress unusedFunction void loop() { - if(timerTick) + static bool beacon_aprs_is = Config.aprs_is.active && Config.aprs_is.beacon; + static bool beacon_digi = Config.digi.active && Config.digi.beacon; + + if(Config.aprs_is.active && Config.aprs_is.beacon && secondsSinceLastAPRSISBeacon >= (Config.aprs_is.beaconTimeout*60)) { portENTER_CRITICAL(&timerMux); - timerTick = false; + secondsSinceLastAPRSISBeacon -= (Config.aprs_is.beaconTimeout*60); portEXIT_CRITICAL(&timerMux); - next_update--; + beacon_aprs_is = true; } - timeClient.update(); - ArduinoOTA.handle(); - if(WiFiMulti.run() != WL_CONNECTED) + if(Config.digi.active && Config.digi.beacon && secondsSinceLastDigiBeacon >= (Config.digi.beaconTimeout*60)) + { + portENTER_CRITICAL(&timerMux); + secondsSinceLastDigiBeacon -= (Config.digi.beaconTimeout*60); + portEXIT_CRITICAL(&timerMux); + beacon_digi = true; + } + + if(Config.wifi.active) ArduinoOTA.handle(); + if(Config.wifi.active && WiFiMulti.run() != WL_CONNECTED) { Serial.println("[ERROR] WiFi not connected!"); show_display("ERROR", "WiFi not connected!"); delay(1000); return; } - if(!aprs_is->connected()) + if(Config.aprs_is.active && !aprs_is->connected()) { Serial.print("[INFO] connecting to server: "); - Serial.print(Config->getIsServer()); + Serial.print(Config.aprs_is.server); Serial.print(" on port: "); - Serial.println(Config->getIsPort()); + Serial.println(Config.aprs_is.port); show_display("INFO", "Connecting to server"); - if(!aprs_is->connect(Config->getIsServer(), Config->getIsPort())) + if(!aprs_is->connect(Config.aprs_is.server, Config.aprs_is.port)) { Serial.println("[ERROR] Connection failed."); Serial.println("[INFO] Waiting 5 seconds before retrying..."); @@ -110,30 +128,17 @@ void loop() } Serial.println("[INFO] Connected to server!"); } - if(next_update < 0) - { - show_display(Config->getIsCall(), "Beacon to Server..."); - Serial.print("[" + timeClient.getFormattedTime() + "] "); - Serial.print(BeaconMsg); - aprs_is->sendMessage(BeaconMsg); - next_update = Config->getBeaconTimeout() * 60; - } - if(aprs_is->available() > 0) + if(Config.aprs_is.active && aprs_is->available() > 0) { String str = aprs_is->getMessage(); Serial.print("[" + timeClient.getFormattedTime() + "] "); Serial.println(str); -#ifdef SEND_MESSAGES_FROM_IS_TO_LORA - std::shared_ptr msg = std::shared_ptr(new APRSMessage()); - msg->decode(str); - lora_aprs.sendMessage(msg); -#endif } if(lora_aprs.hasMessage()) { std::shared_ptr msg = lora_aprs.getMessage(); - show_display(Config->getIsCall(), timeClient.getFormattedTime() + " LoRa", "RSSI: " + String(lora_aprs.getMessageRssi()) + ", SNR: " + String(lora_aprs.getMessageSnr()), msg->toString()); + show_display(Config.callsign, timeClient.getFormattedTime() + " LoRa", "RSSI: " + String(lora_aprs.getMessageRssi()) + ", SNR: " + String(lora_aprs.getMessageSnr()), msg->toString()); Serial.print("[" + timeClient.getFormattedTime() + "] "); Serial.print(" Received packet '"); Serial.print(msg->toString()); @@ -142,32 +147,122 @@ void loop() Serial.print(" and SNR "); Serial.println(lora_aprs.getMessageSnr()); - aprs_is->sendMessage(msg->encode()); + if(Config.aprs_is.active) + { + aprs_is->sendMessage(msg->encode()); + } + if(Config.digi.active) + { + if(msg->getSource().indexOf(Config.callsign) != -1) + { + Serial.print("Message already received as repeater: '"); + Serial.print(msg->toString()); + Serial.print("' with RSSI "); + Serial.print(lora_aprs.getMessageRssi()); + Serial.print(" and SNR "); + Serial.println(lora_aprs.getMessageSnr()); + return; + } + + // lets try not to flood the LoRa frequency in limiting the same messages: + std::map>::iterator foundMsg = std::find_if(lastMessages.begin(), lastMessages.end(), [&](std::pair > & old_msg) + { + if(msg->getSource() == old_msg.second->getSource() && + msg->getDestination() == old_msg.second->getDestination() && + msg->getAPRSBody()->getData() == old_msg.second->getAPRSBody()->getData()) + { + return true; + } + return false; + }); + + if(foundMsg == lastMessages.end()) + { + show_display(Config.callsign, "RSSI: " + String(lora_aprs.getMessageRssi()) + ", SNR: " + String(lora_aprs.getMessageSnr()), msg->toString(), 0); + Serial.print("Received packet '"); + Serial.print(msg->toString()); + Serial.print("' with RSSI "); + Serial.print(lora_aprs.getMessageRssi()); + Serial.print(" and SNR "); + Serial.println(lora_aprs.getMessageSnr()); + msg->setPath(String(Config.callsign) + "*"); + lora_aprs.sendMessage(msg); + lastMessages.insert({secondsSinceStartup, msg}); + } + else + { + Serial.print("Message already received (timeout): '"); + Serial.print(msg->toString()); + Serial.print("' with RSSI "); + Serial.print(lora_aprs.getMessageRssi()); + Serial.print(" and SNR "); + Serial.println(lora_aprs.getMessageSnr()); + } + return; + } } - static int _next_update = 0; - if(next_update != _next_update) + if(Config.digi.active) { - show_display(Config->getIsCall(), "Time to next beaconing: " + String(next_update)); + for(std::map>::iterator iter = lastMessages.begin(); iter != lastMessages.end(); ) + { + if(secondsSinceStartup >= iter->first + Config.digi.forwardTimeout*60) + { + iter = lastMessages.erase(iter); + } + else + { + iter++; + } + } + } + if(beacon_digi) + { + beacon_digi = false; + show_display(Config.callsign, "Beacon to HF..."); + Serial.print("[" + timeClient.getFormattedTime() + "] "); + Serial.print(BeaconMsg->encode()); + lora_aprs.sendMessage(BeaconMsg); + Serial.println("finished TXing..."); + } + if(beacon_aprs_is) + { + beacon_aprs_is = false; + show_display(Config.callsign, "Beacon to APRS IS Server..."); + Serial.print("[" + timeClient.getFormattedTime() + "] "); + Serial.print(BeaconMsg->encode()); + aprs_is->sendMessage(BeaconMsg); } } void load_config() { - Config = new Configuration("/is-cfg.json"); - if(Config->getIsCall() == "NOCALL-10" || Config->getWifiName() == "") + ConfigurationManagement confmg("/is-cfg.json"); + Config = confmg.readConfiguration(); + if(Config.callsign == "NOCALL-10") { Serial.println("[ERROR] You have to change your settings in 'data/is-cfg.json' and upload it via \"Upload File System image\"!"); show_display("ERROR", "You have to change your settings in 'data/is-cfg.json' and upload it via \"Upload File System image\"!"); while (true) {} } + + if(Config.aprs_is.active && !Config.wifi.active) + { + Serial.println("[ERROR] You have to activate Wifi for APRS IS to work, please check your settings!"); + show_display("ERROR", "You have to activate Wifi for APRS IS to work, please check your settings!"); + while (true) + {} + } } void setup_wifi() { WiFi.config(INADDR_NONE, INADDR_NONE, INADDR_NONE); - WiFi.setHostname(Config->getIsCall().c_str()); - WiFiMulti.addAP(Config->getWifiName().c_str(), Config->getWifiPassword().c_str()); + WiFi.setHostname(Config.callsign.c_str()); + for(Configuration::Wifi::AP ap : Config.wifi.APs) + { + WiFiMulti.addAP(ap.SSID.c_str(), ap.password.c_str()); + } Serial.print("[INFO] Waiting for WiFi"); show_display("INFO", "Waiting for WiFi"); while(WiFiMulti.run() != WL_CONNECTED) @@ -218,15 +313,13 @@ void setup_ota() else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed"); else if (error == OTA_END_ERROR) Serial.println("End Failed"); }); - ArduinoOTA.setHostname(Config->getIsCall().c_str()); + ArduinoOTA.setHostname(Config.callsign.c_str()); ArduinoOTA.begin(); } void setup_lora() { - Serial.print("[INFO] frequency: "); - Serial.println(LORA_RX_FREQUENCY); - + lora_aprs.tx_frequency = LORA_RX_FREQUENCY; if (!lora_aprs.begin()) { Serial.println("[ERROR] Starting LoRa failed!"); @@ -235,6 +328,13 @@ void setup_lora() } Serial.println("[INFO] LoRa init done!"); show_display("INFO", "LoRa init done!", 2000); + + BeaconMsg = std::shared_ptr(new APRSMessage()); + BeaconMsg->setSource(Config.callsign); + BeaconMsg->setDestination("APLG0"); + String lat = create_lat_aprs(Config.beacon.positionLatitude); + String lng = create_long_aprs(Config.beacon.positionLongitude); + BeaconMsg->getAPRSBody()->setData(String("=") + lat + "I" + lng + "&" + Config.beacon.message); } void setup_ntp() @@ -251,19 +351,15 @@ void setup_ntp() void setup_aprs_is() { - aprs_is = new APRS_IS(Config->getIsCall(), Config->getIsPassword() , "ESP32-APRS-IS", "0.1"); - - APRSMessage msg; - msg.setSource(Config->getIsCall()); - msg.setDestination("APLG0"); - msg.getAPRSBody()->setData(String("=") + Config->getBeaconPosLat() + "I" + Config->getBeaconPosLong() + "&" + Config->getBeaconMessage()); - BeaconMsg = msg.encode(); + aprs_is = new APRS_IS(Config.callsign, Config.aprs_is.password , "ESP32-APRS-IS", "0.1"); } void IRAM_ATTR onTimer() { portENTER_CRITICAL_ISR(&timerMux); - timerTick = true; + secondsSinceLastAPRSISBeacon++; + secondsSinceLastDigiBeacon++; + secondsSinceStartup++; portEXIT_CRITICAL_ISR(&timerMux); } @@ -274,3 +370,31 @@ void setup_timer() timerAttachInterrupt(timer, &onTimer, true); timerAlarmEnable(timer); } + +String create_lat_aprs(double lat) +{ + char str[20]; + char n_s = 'N'; + if(lat < 0) + { + n_s = 'S'; + } + lat = std::abs(lat); + sprintf(str, "%02d%05.2f%c", (int)lat, (lat - (double)((int)lat)) * 60.0, n_s); + String lat_str(str); + return lat_str; +} + +String create_long_aprs(double lng) +{ + char str[20]; + char e_w = 'E'; + if(lng < 0) + { + e_w = 'W'; + } + lng = std::abs(lng); + sprintf(str, "%03d%05.2f%c", (int)lng, (lng - (double)((int)lng)) * 60.0, e_w); + String lng_str(str); + return lng_str; +} diff --git a/src/configuration.cpp b/src/configuration.cpp index d5c5364..620f7b8 100644 --- a/src/configuration.cpp +++ b/src/configuration.cpp @@ -4,8 +4,8 @@ #include "settings.h" -Configuration::Configuration(String FilePath) - : mFilePath(FilePath), mData(1024) +ConfigurationManagement::ConfigurationManagement(String FilePath) + : mFilePath(FilePath) { if(!SPIFFS.begin(true)) { @@ -17,150 +17,55 @@ Configuration::Configuration(String FilePath) return; } } - if(SPIFFS.exists(mFilePath)) + if(!SPIFFS.exists(mFilePath)) { - readFile(); - } - else - { - mData["Wifi"]["Name"] = WIFI_NAME; - mData["Wifi"]["Password"] = WIFI_KEY; - mData["IS"]["Call"] = USER; - mData["IS"]["Password"] = PASS; - mData["IS"]["Server"] = SERVER; - mData["IS"]["Port"] = PORT; - mData["Beacon"]["Message"] = BEACON_MESSAGE; - mData["Beacon"]["Pos"]["Lat"] = BEACON_LAT_POS; - mData["Beacon"]["Pos"]["Long"] = BEACON_LONG_POS; - mData["Beacon"]["Timeout"] = BEACON_TIMEOUT; - writeFile(); + Configuration conf; + writeConfiguration(conf); } } -String Configuration::getWifiName() const -{ - return mData["Wifi"]["Name"]; -} - -// cppcheck-suppress unusedFunction -void Configuration::setWifiName(String WifiName) -{ - mData["Wifi"]["Name"] = WifiName; -} - -String Configuration::getWifiPassword() const -{ - return mData["Wifi"]["Password"]; -} - -// cppcheck-suppress unusedFunction -void Configuration::setWifiPassword(String WifiPassword) -{ - mData["Wifi"]["Password"] = WifiPassword; -} - -String Configuration::getIsCall() const -{ - return mData["IS"]["Call"]; -} - -// cppcheck-suppress unusedFunction -void Configuration::setIsCall(String IsCall) -{ - mData["IS"]["Call"] = IsCall; -} - -String Configuration::getIsPassword() const -{ - return mData["IS"]["Password"]; -} - -// cppcheck-suppress unusedFunction -void Configuration::setIsPassword(String IsPassword) -{ - mData["IS"]["Password"] = IsPassword; -} - -String Configuration::getIsServer() const -{ - return mData["IS"]["Server"]; -} - -// cppcheck-suppress unusedFunction -void Configuration::setIsServer(String IsServer) -{ - mData["IS"]["Server"] = IsServer; -} - -int Configuration::getIsPort() const -{ - return mData["IS"]["Port"]; -} - -// cppcheck-suppress unusedFunction -void Configuration::setIsPort(int IsPort) -{ - mData["IS"]["Port"] = IsPort; -} - -String Configuration::getBeaconMessage() const -{ - return mData["Beacon"]["Message"]; -} - -// cppcheck-suppress unusedFunction -void Configuration::setBeaconMessage(String BeaconMessage) -{ - mData["Beacon"]["Message"] = BeaconMessage; -} - -String Configuration::getBeaconPosLat() const -{ - return mData["Beacon"]["Pos"]["Lat"]; -} - -// cppcheck-suppress unusedFunction -void Configuration::setBeaconPosLat(String BeaconPosLat) -{ - mData["Beacon"]["Pos"]["Lat"] = BeaconPosLat; -} - -String Configuration::getBeaconPosLong() const -{ - return mData["Beacon"]["Pos"]["Long"]; -} - -// cppcheck-suppress unusedFunction -void Configuration::setBeaconPosLong(String BeaconPosLong) -{ - mData["Beacon"]["Pos"]["Long"] = BeaconPosLong; -} - -int Configuration::getBeaconTimeout() const -{ - return mData["Beacon"]["Timeout"]; -} - -// cppcheck-suppress unusedFunction -void Configuration::setBeaconTimeout(int BeaconTimeout) -{ - mData["Beacon"]["Timeout"] = BeaconTimeout; -} - - -void Configuration::readFile() +Configuration ConfigurationManagement::readConfiguration() { File file = SPIFFS.open(mFilePath); if(!file) { Serial.println("Failed to open file for reading..."); - return; + return Configuration(); } - deserializeJson(mData, file); + DynamicJsonDocument data(1024); + deserializeJson(data, file); file.close(); + + Configuration conf; + conf.version = data["version"]; + conf.callsign = data["callsign"].as(); + conf.wifi.active = data["wifi"]["active"]; + JsonArray aps = data["wifi"]["AP"].as(); + for(JsonVariant v : aps) + { + Configuration::Wifi::AP ap; + ap.SSID = v["SSID"].as(); + ap.password = v["password"].as(); + conf.wifi.APs.push_back(ap); + } + conf.beacon.message = data["beacon"]["message"].as(); + conf.beacon.positionLatitude = data["beacon"]["position"]["latitude"]; + conf.beacon.positionLongitude = data["beacon"]["position"]["longitude"]; + conf.aprs_is.active = data["aprs_is"]["active"]; + conf.aprs_is.password = data["aprs_is"]["password"].as(); + conf.aprs_is.server = data["aprs_is"]["server"].as(); + conf.aprs_is.port = data["aprs_is"]["port"]; + conf.aprs_is.beacon = data["aprs_is"]["beacon"]; + conf.aprs_is.beaconTimeout = data["aprs_is"]["beacon_timeout"]; + conf.digi.active = data["digi"]["active"]; + conf.digi.forwardTimeout = data["digi"]["forward_timeout"]; + conf.digi.beacon = data["digi"]["beacon"]; + conf.digi.beaconTimeout = data["digi"]["beacon_timeout"]; + + return conf; } -void Configuration::writeFile() +void ConfigurationManagement::writeConfiguration(Configuration conf) { File file = SPIFFS.open(mFilePath, "w"); if(!file) @@ -168,8 +73,35 @@ void Configuration::writeFile() Serial.println("Failed to open file for writing..."); return; } - serializeJson(mData, file); - serializeJson(mData, Serial); + DynamicJsonDocument data(1024); + + data["version"] = conf.version; + data["callsign"] = conf.callsign; + data["wifi"]["active"] = conf.wifi.active; + JsonArray aps = data["wifi"]["AP"].to(); + for(Configuration::Wifi::AP ap : conf.wifi.APs) + { + JsonVariant v; + v["SSID"] = ap.SSID; + v["password"] = ap.password; + aps.add(v); + } + data["beacon"]["message"] = conf.beacon.message; + data["beacon"]["position"]["latitude"] = conf.beacon.positionLatitude; + data["beacon"]["position"]["longitude"] = conf.beacon.positionLongitude; + data["aprs_is"]["active"] = conf.aprs_is.active; + data["aprs_is"]["password"] = conf.aprs_is.password; + data["aprs_is"]["server"] = conf.aprs_is.server; + data["aprs_is"]["port"] = conf.aprs_is.port; + data["aprs_is"]["beacon"] = conf.aprs_is.beacon; + data["aprs_is"]["beacon_timeout"] = conf.aprs_is.beaconTimeout; + data["digi"]["active"] = conf.digi.active; + data["digi"]["forward_timeout"] = conf.digi.forwardTimeout; + data["digi"]["beacon"] = conf.digi.beacon; + data["digi"]["beacon_timeout"] = conf.digi.beaconTimeout; + + serializeJson(data, file); + serializeJson(data, Serial); Serial.println(); file.close(); } diff --git a/src/configuration.h b/src/configuration.h index 52903d2..5536129 100644 --- a/src/configuration.h +++ b/src/configuration.h @@ -1,45 +1,84 @@ #ifndef CONFIGURATION_H_ #define CONFIGURATION_H_ +#include + #include #include class Configuration { public: - explicit Configuration(String FilePath); + class Wifi + { + public: + class AP + { + public: + String SSID; + String password; + }; - void readFile(); - void writeFile(); + Wifi() : active(false) {} - String getWifiName() const; - void setWifiName(String WifiName); - String getWifiPassword() const; - void setWifiPassword(String WifiPassword); + bool active; + std::list APs; + }; - String getIsCall() const; - void setIsCall(String IsCall); - String getIsPassword() const; - void setIsPassword(String IsPassword); - String getIsServer() const; - void setIsServer(String IsServer); - int getIsPort() const; - void setIsPort(int IsPort); + class Beacon + { + public: + Beacon() : message("LoRa iGATE & Digi, Info: github.com/peterus/LoRa_APRS_iGate") {} - String getBeaconMessage() const; - void setBeaconMessage(String BeaconMessage); - String getBeaconPosLat() const; - void setBeaconPosLat(String BeaconPosLat); - String getBeaconPosLong() const; - void setBeaconPosLong(String BeaconPosLong); - int getBeaconTimeout() const; - void setBeaconTimeout(int BeaconTimeout); + String message; + double positionLatitude; + double positionLongitude; + }; + class APRS_IS + { + public: + APRS_IS() : active(false), server("euro.aprs2.net"), port(14580), beacon(true), beaconTimeout(15) {} + + bool active; + String password; + String server; + int port; + bool beacon; + int beaconTimeout; + }; + + class Digi + { + public: + Digi() : active(false), forwardTimeout(5), beacon(true), beaconTimeout(30) {} + + bool active; + int forwardTimeout; + bool beacon; + int beaconTimeout; + }; + + Configuration() : version(1), callsign("NOCALL-10") {}; + + int version; + String callsign; + Wifi wifi; + Beacon beacon; + APRS_IS aprs_is; + Digi digi; +}; + +class ConfigurationManagement +{ +public: + explicit ConfigurationManagement(String FilePath); + + Configuration readConfiguration(); + void writeConfiguration(Configuration conf); private: const String mFilePath; - - DynamicJsonDocument mData; }; #endif