Compare commits

...

6 Commits

Author SHA1 Message Date
richonguzman
92572245cd beacon Freq on digi mod 2025-10-12 21:45:23 -03:00
richonguzman
152217e71c startup delay added 2025-10-12 14:21:51 -03:00
richonguzman
d334164b6f Beacon Freq on Digi Mode 2025-10-11 18:27:06 -03:00
richonguzman
61409ce683 index correction 2025-10-11 15:57:29 -03:00
richonguzman
742a6b6477 readme update 2025-10-11 13:23:39 -03:00
richonguzman
dd2fce3908 igate beacon over mqtt 2025-10-11 13:22:08 -03:00
13 changed files with 155 additions and 41 deletions

View File

@@ -52,7 +52,7 @@ ____________________________________________________
# Timeline (Versions):
- 2025-10-11 User defined NTP server added.
- 2025-10-11 User defined NTP server and send beacon over MQTT added.
- 2025-10-10 Changed Wiki into a pdf manual.
- 2025-09-26 Heltec Wireless Bridge support added.
- 2025-09-09 MQTT added (pub+sub), Status defined by Op now and many fixes more.

View File

@@ -35,7 +35,8 @@
"blacklist": "",
"digi": {
"mode": 0,
"ecoMode": 0
"ecoMode": 0,
"beaconOnRxFreq": false
},
"lora": {
"txFreq": 433775000,
@@ -86,7 +87,8 @@
"topic": "",
"username": "",
"password": "",
"port": 1883
"port": 1883,
"beaconOverMqtt": false
},
"ota": {
"username": "",
@@ -108,6 +110,7 @@
"rememberStationTime": 30,
"backupDigiMode": false,
"rebootMode": false,
"rebootModeTime": 6
"rebootModeTime": 6,
"startupDelay": 0
}
}

View File

@@ -344,6 +344,28 @@
</button>
</div>
</div>
<div class="col-6">
<label
for="startupDelay"
class="form-label"
>Startup Delay<small>(To Allow Router/Modem to start WiFiAP before connection)</small></label
>
<div class="input-group">
<input
type="number"
name="startupDelay"
id="startupDelay"
placeholder="0"
class="form-control"
step="1"
min="0"
max="5"
/>
<span class="input-group-text"
>minutes</span
>
</div>
</div>
</div>
</div>
<hr>
@@ -716,6 +738,28 @@
</option>
</select>
</div>
<div class="col-12 mt-3">
<label
for="digi.beaconOnRxFreq"
class="form-label"
>Digipeater Beacon Frequency
<small
>(If Rx Freq different from Tx Freq).</small
></label
>
<select
class="form-select form-select"
name="digi.beaconOnRxFreq"
id="digi.beaconOnRxFreq"
>
<option value="false">
Beacon on Tx Freq
</option>
<option value="true">
Beacon on Rx Freq
</option>
</select>
</div>
</div>
</div>
</div>
@@ -1641,6 +1685,21 @@
Default is <strong>1883</strong>
</div>
</div>
<div class="col-12 mt-3">
<div class="form-check form-switch">
<input
type="checkbox"
name="mqtt.beaconOverMqtt"
id="mqtt.beaconOverMqtt"
class="form-check-input"
/>
<label
for="mqtt.beaconOverMqtt"
class="form-label"
>Send iGate Beacon to MQTT</label
>
</div>
</div>
</div>
</div>
</div>

View File

@@ -95,6 +95,7 @@ function loadSettings(settings) {
networksContainer.appendChild(networkElement);
networkCount++;
});
document.getElementById("startupDelay").value = settings.startupDelay;
// APRS-IS
document.getElementById("aprs_is.active").checked = settings.aprs_is.active;
@@ -134,6 +135,7 @@ function loadSettings(settings) {
// Digi
document.getElementById("digi.mode").value = settings.digi.mode;
document.getElementById("digi.ecoMode").value = settings.digi.ecoMode;
document.getElementById("digi.beaconOnRxFreq").value = settings.digi.beaconOnRxFreq;
// LoRa
document.getElementById("lora.txFreq").value = settings.lora.txFreq;
@@ -207,12 +209,14 @@ function loadSettings(settings) {
document.getElementById("mqtt.username").value = settings.mqtt.username;
document.getElementById("mqtt.password").value = settings.mqtt.password;
document.getElementById("mqtt.port").value = settings.mqtt.port;
MqttCheckbox.checked = settings.mqtt.active;
MqttServer.disabled = !MqttCheckbox.check;
MqttTopic.disabled = !MqttCheckbox.check;
MqttUsername.disabled = !MqttCheckbox.check;
MqttPassword.disabled = !MqttCheckbox.check;
MqttPort.disabled = !MqttCheckbox.check;
document.getElementById("mqtt.beaconOverMqtt").value = settings.mqtt.beaconOverMqtt;
MqttCheckbox.checked = settings.mqtt.active;
MqttServer.disabled = !MqttCheckbox.check;
MqttTopic.disabled = !MqttCheckbox.check;
MqttUsername.disabled = !MqttCheckbox.check;
MqttPassword.disabled = !MqttCheckbox.check;
MqttPort.disabled = !MqttCheckbox.check;
MqttBeaconOverMqtt.disabled = !MqttCheckbox.check;
// Reboot
document.getElementById("other.rebootMode").checked = settings.other.rebootMode;
@@ -380,12 +384,14 @@ const MqttTopic = document.querySelector('input[name="mqtt.topic
const MqttUsername = document.querySelector('input[name="mqtt.username"]');
const MqttPassword = document.querySelector('input[name="mqtt.password"]');
const MqttPort = document.querySelector('input[name="mqtt.port"]');
const MqttBeaconOverMqtt = document.querySelector('input[name="mqtt.beaconOverMqtt"]');
MqttCheckbox.addEventListener("change", function () {
MqttServer.disabled = !this.checked;
MqttTopic.disabled = !this.checked;
MqttUsername.disabled = !this.checked;
MqttPassword.disabled = !this.checked;
MqttPort.disabled = !this.checked;
MqttBeaconOverMqtt.disabled = !this.checked;
});
// Reboot Switches

View File

@@ -68,6 +68,7 @@ class DIGI {
public:
int mode;
int ecoMode; // 0 = Not Active | 1 = Ultra EcoMode | 2 = Not Active (WiFi OFF, Serial ON)
bool beaconOnRxFreq;
};
class LoraModule {
@@ -158,6 +159,7 @@ public:
String username;
String password;
int port;
bool beaconOverMqtt;
};
class Configuration {
@@ -167,6 +169,7 @@ public:
bool backupDigiMode;
bool rebootMode;
int rebootModeTime;
int startupDelay;
String personalNote;
String blacklist;
std::vector<WiFi_AP> wifiAPs;

View File

@@ -23,12 +23,6 @@
#include <Arduino.h>
struct Packet25SegBuffer {
uint32_t receivedTime;
String station;
String payload;
};
struct LastHeardStation {
uint32_t lastHeardTime;
String station;
@@ -47,7 +41,7 @@ namespace STATION_Utils {
bool check25SegBuffer(const String& station, const String& textMessage);
void processOutputPacketBufferUltraEcoMode();
void processOutputPacketBuffer();
void addToOutputPacketBuffer(const String& packet);
void addToOutputPacketBuffer(const String& packet, bool flag = false);
}

View File

@@ -35,7 +35,7 @@ namespace Utils {
void processStatus();
String getLocalIP();
void setupDisplay();
void activeStations();
void showActiveStations();
void checkBeaconInterval();
void checkDisplayInterval();
void validateFreqs();
@@ -46,6 +46,7 @@ namespace Utils {
void checkRebootTime();
void checkSleepByLowBatteryVoltage(uint8_t mode);
bool checkValidCallsign(const String& callsign);
void startupDelay();
}

View File

@@ -67,7 +67,7 @@ ___________________________________________________________________*/
#endif
String versionDate = "2025-10-11";
String versionDate = "2025-10-12";
String versionNumber = "3.1.3";
Configuration Config;
WiFiClient aprsIsClient;
@@ -97,7 +97,6 @@ bool modemLoggedToAPRSIS = false;
std::vector<ReceivedPacket> receivedPackets;
String firstLine, secondLine, thirdLine, fourthLine, fifthLine, sixthLine, seventhLine;
//#define STARTUP_DELAY 5 //min
void setup() {
@@ -108,12 +107,7 @@ void setup() {
Utils::validateFreqs();
GPS_Utils::setup();
STATION_Utils::loadBlacklistAndManagers();
#ifdef STARTUP_DELAY // (TEST) just to wait for WiFi init of Routers
displayShow("", " STARTUP DELAY ...", "", "", 0);
delay(STARTUP_DELAY * 60 * 1000);
#endif
Utils::startupDelay();
SLEEP_Utils::setup();
WIFI_Utils::setup();
NTP_Utils::setup();

View File

@@ -45,6 +45,8 @@ bool Configuration::writeFile() {
}
}
data["other"]["startupDelay"] = startupDelay;
data["wifi"]["autoAP"]["password"] = wifiAutoAP.password;
data["wifi"]["autoAP"]["timeout"] = wifiAutoAP.timeout;
@@ -85,6 +87,7 @@ bool Configuration::writeFile() {
#if defined(HAS_A7670)
if (digi.ecoMode == 1) data["digi"]["ecoMode"] = 2;
#endif
data["digi"]["beaconOnRxFreq"] = digi.beaconOnRxFreq;
data["lora"]["rxFreq"] = loramodule.rxFreq;
data["lora"]["txFreq"] = loramodule.txFreq;
@@ -131,6 +134,7 @@ bool Configuration::writeFile() {
data["mqtt"]["username"] = mqtt.username;
data["mqtt"]["password"] = mqtt.password;
data["mqtt"]["port"] = mqtt.port;
data["mqtt"]["beaconOverMqtt"] = mqtt.beaconOverMqtt;
data["ota"]["username"] = ota.username;
data["ota"]["password"] = ota.password;
@@ -184,6 +188,9 @@ bool Configuration::readFile() {
wifiAPs.push_back(wifiap);
}
if (!data["other"].containsKey("startupDelay")) needsRewrite = true;
startupDelay = data["other"]["startupDelay"] | 0;
if (!data["wifi"]["autoAP"].containsKey("password") ||
!data["wifi"]["autoAP"].containsKey("timeout")) needsRewrite = true;
wifiAutoAP.password = data["wifi"]["autoAP"]["password"] | "1234567890";
@@ -241,10 +248,12 @@ bool Configuration::readFile() {
blacklist = data["blacklist"] | "station callsign";
if (!data["digi"].containsKey("mode") ||
!data["digi"].containsKey("ecoMode")) needsRewrite = true;
!data["digi"].containsKey("ecoMode") ||
!data["digi"].containsKey("beaconOnRxFreq")) needsRewrite = true;
digi.mode = data["digi"]["mode"] | 0;
digi.ecoMode = data["digi"]["ecoMode"] | 0;
if (digi.ecoMode == 1) shouldSleepStop = false;
digi.beaconOnRxFreq = data["digi"]["beaconOnRxFreq"] | false;
#if defined(HAS_A7670)
if (digi.ecoMode == 1) digi.ecoMode = 2;
@@ -323,13 +332,15 @@ bool Configuration::readFile() {
!data["mqtt"].containsKey("topic") ||
!data["mqtt"].containsKey("username") ||
!data["mqtt"].containsKey("password") ||
!data["mqtt"].containsKey("port")) needsRewrite = true;
!data["mqtt"].containsKey("port") ||
!data["mqtt"].containsKey("beaconOverMqtt")) needsRewrite = true;
mqtt.active = data["mqtt"]["active"] | false;
mqtt.server = data["mqtt"]["server"] | "";
mqtt.topic = data["mqtt"]["topic"] | "aprs-igate";
mqtt.username = data["mqtt"]["username"] | "";
mqtt.password = data["mqtt"]["password"] | "";
mqtt.port = data["mqtt"]["port"] | 1883;
mqtt.beaconOverMqtt = data["mqtt"]["beaconOverMqtt"] | false;
if (!data["ota"].containsKey("username") ||
!data["ota"].containsKey("password")) needsRewrite = true;
@@ -395,6 +406,8 @@ void Configuration::setDefaultValues() {
wifiAPs.push_back(wifiap);
startupDelay = 0;
wifiAutoAP.password = "1234567890";
wifiAutoAP.timeout = 10;
@@ -430,6 +443,7 @@ void Configuration::setDefaultValues() {
digi.mode = 0;
digi.ecoMode = 0;
digi.beaconOnRxFreq = false;
loramodule.txFreq = 433775000;
loramodule.rxFreq = 433775000;
@@ -476,6 +490,7 @@ void Configuration::setDefaultValues() {
mqtt.username = "";
mqtt.password = "";
mqtt.port = 1883;
mqtt.beaconOverMqtt = false;
ota.username = "";
ota.password = "";

View File

@@ -30,6 +30,7 @@
extern Configuration Config;
extern uint32_t lastRxTime;
extern bool packetIsBeacon;
extern std::vector<ReceivedPacket> receivedPackets;
@@ -143,7 +144,9 @@ namespace LoRa_Utils {
if (!Config.loramodule.txActive) return;
if (Config.loramodule.txFreq != Config.loramodule.rxFreq) {
changeFreqTx();
if (!packetIsBeacon || (packetIsBeacon && !Config.digi.beaconOnRxFreq)) {
changeFreqTx();
}
}
#ifdef INTERNAL_LED_PIN
@@ -165,7 +168,9 @@ namespace LoRa_Utils {
if (Config.digi.ecoMode != 1) digitalWrite(INTERNAL_LED_PIN, LOW); // disabled in Ultra Eco Mode
#endif
if (Config.loramodule.txFreq != Config.loramodule.rxFreq) {
changeFreqRx();
if (!packetIsBeacon || (packetIsBeacon && !Config.digi.beaconOnRxFreq)) {
changeFreqRx();
}
}
}

View File

@@ -33,13 +33,26 @@ extern bool shouldSleepLowVoltage;
uint32_t lastTxTime = millis();
std::vector<LastHeardStation> lastHeardStations;
std::vector<String> outputPacketBuffer;
std::vector<Packet25SegBuffer> packet25SegBuffer;
std::vector<String> blacklist;
std::vector<String> managers;
std::vector<LastHeardStation> lastHeardObjects;
struct OutputPacketBuffer {
String packet;
bool isBeacon;
};
std::vector<OutputPacketBuffer> outputPacketBuffer;
struct Packet25SegBuffer {
uint32_t receivedTime;
String station;
String payload;
};
std::vector<Packet25SegBuffer> packet25SegBuffer;
bool saveNewDigiEcoModeConfig = false;
bool packetIsBeacon = false;
namespace STATION_Utils {
@@ -138,7 +151,7 @@ namespace STATION_Utils {
}
}
if (!stationHeard) lastHeardStations.emplace_back(LastHeardStation{millis(), station});
Utils::activeStations();
Utils::showActiveStations();
}
bool wasHeard(const String& station) {
@@ -171,7 +184,9 @@ namespace STATION_Utils {
size_t currentIndex = 0;
while (currentIndex < outputPacketBuffer.size()) { // this sends all packets from output buffer
delay(3000); // and cleans buffer to avoid sending packets with time offset
LoRa_Utils::sendNewPacket(outputPacketBuffer[currentIndex]); // next time it wakes up
if (outputPacketBuffer[currentIndex].isBeacon) packetIsBeacon = true;
LoRa_Utils::sendNewPacket(outputPacketBuffer[currentIndex].packet); // next time it wakes up
if (outputPacketBuffer[currentIndex].isBeacon) packetIsBeacon = false;
currentIndex++;
}
outputPacketBuffer.clear();
@@ -190,13 +205,17 @@ namespace STATION_Utils {
uint32_t lastRx = millis() - lastRxTime;
uint32_t lastTx = millis() - lastTxTime;
if (outputPacketBuffer.size() > 0 && lastTx > timeToWait && lastRx > timeToWait) {
LoRa_Utils::sendNewPacket(outputPacketBuffer[0]);
if (outputPacketBuffer[0].isBeacon) packetIsBeacon = true;
LoRa_Utils::sendNewPacket(outputPacketBuffer[0].packet);
if (outputPacketBuffer[0].isBeacon) packetIsBeacon = false;
outputPacketBuffer.erase(outputPacketBuffer.begin());
lastTxTime = millis();
}
if (shouldSleepLowVoltage) {
while (outputPacketBuffer.size() > 0) {
LoRa_Utils::sendNewPacket(outputPacketBuffer[0]);
if (outputPacketBuffer[0].isBeacon) packetIsBeacon = true;
LoRa_Utils::sendNewPacket(outputPacketBuffer[0].packet);
if (outputPacketBuffer[0].isBeacon) packetIsBeacon = false;
outputPacketBuffer.erase(outputPacketBuffer.begin());
delay(4000);
}
@@ -209,8 +228,12 @@ namespace STATION_Utils {
}
}
void addToOutputPacketBuffer(const String& packet) {
outputPacketBuffer.push_back(packet);
void addToOutputPacketBuffer(const String& packet, bool flag) {
OutputPacketBuffer entry;
entry.packet = packet;
entry.isBeacon = flag;
outputPacketBuffer.push_back(entry);
}
}

View File

@@ -127,7 +127,7 @@ namespace Utils {
seventhLine = " listening...";
}
void activeStations() {
void showActiveStations() {
char buffer[30]; // Adjust size as needed
sprintf(buffer, "Stations (%dmin) = %2d", Config.rememberStationTime, lastHeardStations.size());
fourthLine = buffer;
@@ -159,7 +159,7 @@ namespace Utils {
STATION_Utils::deleteNotHeard();
activeStations();
showActiveStations();
beaconPacket = iGateBeaconPacket;
secondaryBeaconPacket = iGateLoRaBeaconPacket;
@@ -259,7 +259,7 @@ namespace Utils {
Utils::println("-- Sending Beacon to RF --");
displayShow(firstLine, secondLine, thirdLine, fourthLine, fifthLine, sixthLine, "SENDING DIGI BEACON", 0);
seventhLine = " listening...";
STATION_Utils::addToOutputPacketBuffer(secondaryBeaconPacket);
STATION_Utils::addToOutputPacketBuffer(secondaryBeaconPacket, true);
}
lastBeaconTx = millis();
@@ -436,4 +436,11 @@ namespace Utils {
return true;
}
void startupDelay() {
if (Config.startupDelay > 0) {
displayShow("", " STARTUP DELAY ...", "", "", 0);
delay(Config.startupDelay * 60 * 1000);
}
}
}

View File

@@ -157,6 +157,8 @@ namespace WEB_Utils {
Config.wifiAPs.push_back(wifiap);
}
Config.startupDelay = getParamIntSafe("startupDelay", Config.startupDelay);
Config.callsign = getParamStringSafe("callsign", Config.callsign);
Config.wifiAutoAP.password = getParamStringSafe("wifi.autoAP.password", Config.wifiAutoAP.password);
@@ -196,6 +198,7 @@ namespace WEB_Utils {
Config.digi.mode = getParamIntSafe("digi.mode", Config.digi.mode);
Config.digi.ecoMode = getParamIntSafe("digi.ecoMode", Config.digi.ecoMode);
Config.digi.beaconOnRxFreq = request->hasParam("digi.beaconOnRxFreq", true);
Config.loramodule.txFreq = getParamIntSafe("lora.txFreq", Config.loramodule.txFreq);
Config.loramodule.rxFreq = getParamIntSafe("lora.rxFreq", Config.loramodule.rxFreq);
@@ -261,6 +264,7 @@ namespace WEB_Utils {
Config.mqtt.username = getParamStringSafe("mqtt.username", Config.mqtt.username);
Config.mqtt.password = getParamStringSafe("mqtt.password", Config.mqtt.password);
Config.mqtt.port = getParamIntSafe("mqtt.port", Config.mqtt.port);
Config.mqtt.beaconOverMqtt = request->hasParam("mqtt.beaconOverMqtt", true);
}