mirror of
https://github.com/richonguzman/LoRa_APRS_iGate.git
synced 2026-10-31 02:51:58 +01:00
Compare commits
13
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
033657836f | ||
|
|
f739b90adc | ||
|
|
5e2341299a | ||
|
|
8ba8981a32 | ||
|
|
e52d4566ba | ||
|
|
c80eb9bd84 | ||
|
|
6b187da9f4 | ||
|
|
ab27c11532 | ||
|
|
43bfff5033 | ||
|
|
8fa10a7813 | ||
|
|
316c746936 | ||
|
|
3618558b21 | ||
|
|
e1eda2e64a |
No files matched your search
+25
-17
@@ -18,7 +18,7 @@ jobs:
|
||||
- name: ttgo-lora32-v21_915
|
||||
chip: esp32
|
||||
- name: ttgo-lora32-v21_915_GPS
|
||||
chip: esp32
|
||||
chip: esp32
|
||||
- name: ttgo_lora32_t3s3_v1_2
|
||||
chip: esp32s3
|
||||
- name: heltec-lora32-v2
|
||||
@@ -37,17 +37,19 @@ jobs:
|
||||
chip: esp32s3
|
||||
- name: heltec_wireless_bridge
|
||||
chip: esp32
|
||||
- name: ESP32_DIY_LoRa
|
||||
- name: ESP32_DIY_LoRa_SX1278
|
||||
chip: esp32
|
||||
- name: ESP32_DIY_LoRa_915
|
||||
- name: ESP32_DIY_LoRa_915_SX1276
|
||||
chip: esp32
|
||||
- name: ESP32_DIY_1W_LoRa
|
||||
- name: ESP32_DIY_1W_LoRa_E22_400M30S
|
||||
chip: esp32
|
||||
- name: ESP32_DIY_1W_LoRa_915
|
||||
- name: ESP32_DIY_1W_LoRa_915_E22_900M30S
|
||||
chip: esp32
|
||||
- name: ESP32_DIY_1W_LoRa_LLCC68
|
||||
- name: ESP32_DIY_1W_LoRa_E220_400M30S
|
||||
chip: esp32
|
||||
- name: ESP32_DIY_1W_LoRa_Mesh_V1_2
|
||||
- name: ESP32_DIY_1W_LoRa_E32_400M30S
|
||||
chip: esp32
|
||||
- name: ESP32_DIY_1W_LoRa_Mesh_V1_2_E22_400M30S
|
||||
chip: esp32
|
||||
- name: ttgo-t-beam-v1_2
|
||||
chip: esp32
|
||||
@@ -69,9 +71,9 @@ jobs:
|
||||
chip: esp32s3
|
||||
- name: ttgo_t_beam_s3_SUPREME_v3
|
||||
chip: esp32s3
|
||||
- name: ESP32_DIY_LoRa_A7670
|
||||
- name: ESP32_DIY_LoRa_A7670_SX1278
|
||||
chip: esp32
|
||||
- name: ESP32_DIY_LoRa_A7670_915
|
||||
- name: ESP32_DIY_LoRa_A7670_915_SX1276
|
||||
chip: esp32
|
||||
- name: heltec_wireless_tracker
|
||||
chip: esp32s3
|
||||
@@ -85,29 +87,35 @@ jobs:
|
||||
chip: esp32s3
|
||||
- name: OE5HWN_MeshCom
|
||||
chip: esp32
|
||||
- name: WEMOS-LOLIN32-OLED-DIY
|
||||
- name: WEMOS_LOLIN32_OLED_DIY_LoRa_SX1278
|
||||
chip: esp32
|
||||
- name: WEMOS-D1-R32-RA02
|
||||
- name: WEMOS_D1_R32_DIY_LoRa_SX1278
|
||||
chip: esp32
|
||||
- name: WEMOS_S2_MINI_DIY_LoRa
|
||||
- name: WEMOS_S2_MINI_DIY_LoRa_SX1278
|
||||
chip: esp32s2
|
||||
- name: esp32c3_DIY_1W_LoRa
|
||||
- name: ESP32C3_DIY_1W_LoRa_E22_400M30S
|
||||
chip: esp32c3
|
||||
- name: esp32c3_DIY_1W_LoRa_915
|
||||
- name: ESP32C3_DIY_1W_LoRa_915_E22_900M30S
|
||||
chip: esp32c3
|
||||
- name: ESP32_C3_OctopusLab_LoRa
|
||||
- name: ESP32C3_OctopusLab_LoRa
|
||||
chip: esp32c3
|
||||
- name: ESP32C3_SuperMini_DIY_LoRa_SX1278
|
||||
chip: esp32c3
|
||||
- name: QRPLabs_LightGateway_1_0
|
||||
chip: esp32s3
|
||||
- name: QRPLabs_LightGateway_Plus_1_0
|
||||
chip: esp32s3
|
||||
- name: RPC_LORA_DIGIGATE_1W
|
||||
chip: esp32
|
||||
chip: esp32
|
||||
- name: XIAO_ESP32S3_WIO_SX1262
|
||||
chip: esp32s3
|
||||
- name: RAK3312
|
||||
chip: esp32s3
|
||||
- name: RAK3112
|
||||
chip: esp32s3
|
||||
- name: TROY_LoRa_APRS
|
||||
chip: esp32
|
||||
- name: ESP32_9M2IBR_1W_LoRa_GPS
|
||||
- name: ESP32_9M2IBR_1W_LoRa_GPS_E22_400M30S
|
||||
chip: esp32
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
@@ -43,16 +43,18 @@ ____________________________________________________
|
||||
|
||||
- HELTEC V2, V3, V3.2, V4, T114, Wireless Stick, Wireless Stick Lite V3/V3.2, HT-CT62, Wireless Tracker, Wireless Paper.
|
||||
|
||||
- RAK Wireless 4631 + 19007(or 19003)
|
||||
- RAK Wireless 4631 + 19007(or 19003), RAK3312 and RAK3112.
|
||||
|
||||
- Faketec V3 (NRF52840 + Heltec HTRA62(SX1262))
|
||||
|
||||
- QRP Labs LightGateway 1.0 and Plus 1.0.
|
||||
|
||||
- ESP32 + SX1278 LoRa Module or Ebyte 400M30S (or 900M30S) 1W LoRa Module for a DIY Versions.
|
||||
- ESP32 + SX1278 LoRa Module or Ebyte 400M30S (or 900M30S) or Ebyte E32 400M30S 1W LoRa Module for a DIY Versions.
|
||||
|
||||
- ESP32C3 + Ebyte 400M30S(or 900M30S) 1W LoRa Module for another DIY version.
|
||||
|
||||
- ESP32C3 SuperMini + SX1278 (RA-01/RA-02) LoRa Module DIY version.
|
||||
|
||||
- ESP32 + 4G/LTE A7670 Modem + SX1278 DIY Version.
|
||||
|
||||
- Wemos Lolin32 Oled + SX1278 DIY Version.
|
||||
@@ -62,6 +64,8 @@ ____________________________________________________
|
||||
<br />
|
||||
|
||||
# Timeline (Versions):
|
||||
- 2026-09-23 APRSPacketLib V1.0.7 update and many code cleaning with this.
|
||||
- 2026-09-17 Real APRS Symbols added to map and TNC2 output as a posibility in TNC mode.
|
||||
- 2026-08-16 New UI (Web) and also Live View over a Map to see Rx stations.
|
||||
- 2026-08-10 CAD (Channel Activity Detection) and DIFS (Distributed Inter-Frame Space) added.
|
||||
- 2026-03-25 More Boards, SDK update, OTA fix, GPS process update.
|
||||
|
||||
+1
-2
@@ -30,7 +30,6 @@ lib_deps =
|
||||
adafruit/Adafruit BME680 Library @ 2.0.6
|
||||
adafruit/Adafruit INA219 @ 1.2.3
|
||||
adafruit/Adafruit Si7021 Library @ 1.5.3
|
||||
arduino-libraries/NTPClient @ 3.2.1
|
||||
ayushsharma82/ElegantOTA @ 3.1.7
|
||||
bblanchon/ArduinoJson @ 7.4.3
|
||||
jgromes/RadioLib @ 7.6.0
|
||||
@@ -38,7 +37,7 @@ lib_deps =
|
||||
ESP32Async/AsyncTCP @ 3.4.10
|
||||
ESP32Async/ESPAsyncWebServer @ 3.11.1
|
||||
mikalhart/TinyGPSPlus @ 1.0.3
|
||||
richonguzman/APRSPacketLib @ 1.0.5
|
||||
richonguzman/APRSPacketLib @ 1.0.7
|
||||
display_libs =
|
||||
adafruit/Adafruit GFX Library @ 1.12.6
|
||||
adafruit/Adafruit SSD1306 @ 2.5.17
|
||||
|
||||
+71
-2
@@ -96,7 +96,7 @@ function loadSettings(settings) {
|
||||
networksContainer.appendChild(networkElement);
|
||||
networkCount++;
|
||||
});
|
||||
document.getElementById("startupDelay").value = settings.startupDelay;
|
||||
document.getElementById("startupDelay").value = settings.other.startupDelay;
|
||||
|
||||
// APRS-IS
|
||||
document.getElementById("aprs_is.active").checked = settings.aprs_is.active;
|
||||
@@ -277,6 +277,8 @@ function loadSettings(settings) {
|
||||
document.getElementById("ntp.server").value = settings.ntp.server;
|
||||
document.getElementById("ntp.gmtCorrection").value = settings.ntp.gmtCorrection;
|
||||
|
||||
clampOutOfRangeInputs();
|
||||
|
||||
updateImage();
|
||||
}
|
||||
|
||||
@@ -1102,4 +1104,71 @@ window.showMap = function () {
|
||||
fetchMapStations();
|
||||
}
|
||||
}, 15000);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------- keeping the form submittable
|
||||
* A control that fails HTML5 validation blocks the whole form, and when it sits in
|
||||
* a panel that is not the visible one the browser cannot show its bubble either:
|
||||
* pressing Save then does nothing at all -- no message, no request, no clue. Two
|
||||
* guards against that: pull stored values back inside the allowed range on load,
|
||||
* and, if validation still fails, open the section holding the offending control
|
||||
* and show what is wrong.
|
||||
*/
|
||||
function clampOutOfRangeInputs() {
|
||||
const fixed = [];
|
||||
|
||||
document.querySelectorAll("#configuration input[type=number]").forEach((el) => {
|
||||
if (el.disabled || el.value === "") return;
|
||||
|
||||
const value = parseFloat(el.value);
|
||||
if (isNaN(value)) return;
|
||||
|
||||
const min = el.min !== "" ? parseFloat(el.min) : null;
|
||||
const max = el.max !== "" ? parseFloat(el.max) : null;
|
||||
|
||||
let clamped = value;
|
||||
if (max !== null && value > max) clamped = max;
|
||||
if (min !== null && value < min) clamped = min;
|
||||
|
||||
if (clamped !== value) {
|
||||
el.value = clamped;
|
||||
fixed.push((el.name || el.id) + ": " + value + " → " + clamped);
|
||||
}
|
||||
});
|
||||
|
||||
if (fixed.length) {
|
||||
showToast("Stored values outside the allowed range, adjusted:<br>" + fixed.join("<br>")
|
||||
+ "<br><small>Save to keep them.</small>");
|
||||
}
|
||||
}
|
||||
|
||||
let invalidFieldReported = false;
|
||||
|
||||
form.addEventListener("invalid", function (event) {
|
||||
const el = event.target;
|
||||
|
||||
if (!invalidFieldReported) { // only the first one: the rest would pile up toasts
|
||||
invalidFieldReported = true;
|
||||
setTimeout(() => { invalidFieldReported = false; }, 500);
|
||||
|
||||
const section = el.closest(".panel-section");
|
||||
if (section) {
|
||||
const link = document.querySelector('.side-link[data-target="' + section.id + '"]');
|
||||
if (link) {
|
||||
link.click(); // reuses the sidebar switcher (also shows the config view)
|
||||
} else {
|
||||
document.querySelectorAll(".panel-section").forEach((s) => s.classList.remove("active"));
|
||||
section.classList.add("active");
|
||||
document.getElementById("configuration").classList.remove("d-none");
|
||||
}
|
||||
}
|
||||
|
||||
showToast("<b>" + (el.name || el.id) + "</b>: " + (el.validationMessage || "invalid value"));
|
||||
|
||||
setTimeout(() => {
|
||||
el.focus();
|
||||
el.scrollIntoView({ block: "center", behavior: "smooth" });
|
||||
}, 0);
|
||||
}
|
||||
}, true);
|
||||
@@ -27,11 +27,10 @@ namespace APRS_IS_Utils {
|
||||
void upload(const String& line);
|
||||
void connect();
|
||||
|
||||
void checkStatus();
|
||||
String checkForStartingBytes(const String& packet);
|
||||
void updateWiFiAPRSISDisplayInfo();
|
||||
|
||||
String buildPacketToUpload(const String& packet);
|
||||
bool processReceivedLoRaMessage(const String& sender, const String& packet, bool thirdParty);
|
||||
bool processReceivedLoRaMessage(const String& sender, const String& packet);
|
||||
void processLoRaPacket(const String& packet);
|
||||
|
||||
String buildPacketToTx(const String& aprsisPacket, uint8_t packetType);
|
||||
|
||||
@@ -24,10 +24,9 @@
|
||||
|
||||
namespace DIGI_Utils {
|
||||
|
||||
String buildPacket(const String& path, const String& packet, bool thirdParty, bool crossFreq);
|
||||
String generateDigipeatedPacket(const String& packet, bool thirdParty);
|
||||
String buildPacket(const String& path, const String& packet, bool crossFreq);
|
||||
String generateDigipeatedPacket(const String& packet);
|
||||
void processLoRaPacket(const String& packet);
|
||||
void checkEcoMode();
|
||||
|
||||
}
|
||||
|
||||
|
||||
+1
-7
@@ -25,14 +25,8 @@
|
||||
namespace GPS_Utils {
|
||||
|
||||
String getiGateLoRaBeaconPacket();
|
||||
char *ax25_base91enc(char *s, uint8_t n, uint32_t v);
|
||||
String encodeGPS(float latitude, float longitude, const String& overlay, const String& symbol);
|
||||
void generateBeaconFirstPart();
|
||||
void generateBeacons();
|
||||
String decodeEncodedGPS(const String& packet);
|
||||
String getReceivedGPS(const String& packet);
|
||||
String getDistanceAndComment(const String& packet);
|
||||
String getDistanceAndCommentFromMicE(const String& packet);
|
||||
String buildDistanceAndComment(float latitude, float longitude, const String& comment);
|
||||
|
||||
void setup();
|
||||
void getData();
|
||||
|
||||
@@ -84,7 +84,6 @@ public:
|
||||
// Ethernet methods
|
||||
bool ethernetConnect(eth_phy_type_t type, uint8_t phy_addr, uint8_t mdc, uint8_t mdio, int power, eth_clock_mode_t clock_mode, bool use_mac_from_efuse = false);
|
||||
bool setEthernetIP(const String& staticIP, const String& gateway, const String& subnet, const String& dns1, const String& dns2);
|
||||
bool ethernetDisconnect();
|
||||
IPAddress getEthernetIP() const;
|
||||
String getEthernetMACAddress() const;
|
||||
|
||||
@@ -97,4 +96,7 @@ public:
|
||||
bool isModemConnected() const;
|
||||
|
||||
bool isWifiAPActive() const;
|
||||
|
||||
// Check if any network interface was started (lwip TCP/IP stack is initialized)
|
||||
bool hasActiveInterface() const;
|
||||
};
|
||||
@@ -29,7 +29,7 @@
|
||||
|
||||
namespace POWER_Utils {
|
||||
|
||||
#ifdef VEXT_CTRL
|
||||
#ifdef VEXT_CTRL_PIN
|
||||
void vext_ctrl_ON();
|
||||
void vext_ctrl_OFF();
|
||||
#endif
|
||||
@@ -46,6 +46,9 @@ namespace POWER_Utils {
|
||||
void activateLoRa();
|
||||
void deactivateLoRa();
|
||||
bool begin(TwoWire &port);
|
||||
#ifdef SOLAR_CHARGE_PIN
|
||||
void checkSolarCharge();
|
||||
#endif
|
||||
void setup();
|
||||
|
||||
}
|
||||
|
||||
@@ -19,12 +19,16 @@
|
||||
#ifndef SYSLOG_H_
|
||||
#define SYSLOG_H_
|
||||
|
||||
#include <APRSPacketLib.h>
|
||||
#include <Arduino.h>
|
||||
|
||||
|
||||
namespace SYSLOG_Utils {
|
||||
|
||||
void log(const uint8_t type ,const String& packet, const int rssi, const float snr, const int freqError);
|
||||
void logLoRaRx(APRSPacket& aprsPacket, const String& packet, const int rssi, const float snr, const int freqError);
|
||||
void logAPRSISTx(const String& packet);
|
||||
void logLoRaTx(const String& packet);
|
||||
void logCRCError(const String& packet, const int rssi, const float snr, const int freqError);
|
||||
void setup();
|
||||
|
||||
}
|
||||
|
||||
+3
-2
@@ -19,9 +19,9 @@
|
||||
#ifndef UTILS_H_
|
||||
#define UTILS_H_
|
||||
|
||||
#include <APRSPacketLib.h>
|
||||
#include <Arduino.h>
|
||||
|
||||
|
||||
class ReceivedPacket {
|
||||
public:
|
||||
String rxTime;
|
||||
@@ -39,7 +39,8 @@ namespace Utils {
|
||||
void checkBeaconInterval();
|
||||
void checkDisplayInterval();
|
||||
void validateFreqs();
|
||||
void typeOfPacket(const String& packet, const uint8_t packetType);
|
||||
void updateLoRaPacketDisplayInfo(APRSPacket& aprsPacket, const uint8_t packetType);
|
||||
void updateAPRSISPacketDisplayInfo(const String& packet);
|
||||
void print(const String& text);
|
||||
void println(const String& text);
|
||||
void checkRebootMode();
|
||||
|
||||
@@ -36,6 +36,7 @@ namespace WX_Utils {
|
||||
String generateHumString(const float sensorHum);
|
||||
String generatePresString(const float sensorPres);
|
||||
String readDataSensor();
|
||||
float readBoardTemperature();
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ ___________________________________________________________________*/
|
||||
#endif
|
||||
|
||||
|
||||
String versionDate = "2026-09-22";
|
||||
String versionDate = "2026-09-23";
|
||||
String versionNumber = "4.0.1";
|
||||
Configuration Config;
|
||||
WiFiClient aprsIsClient;
|
||||
@@ -184,7 +184,7 @@ void loop() {
|
||||
Utils::checkDisplayInterval();
|
||||
Utils::checkBeaconInterval();
|
||||
|
||||
APRS_IS_Utils::checkStatus(); // Need that to update display, maybe split this and send APRSIS status to display func?
|
||||
APRS_IS_Utils::updateWiFiAPRSISDisplayInfo(); // WiFi / APRS-IS status line (wakes display only on status change)
|
||||
|
||||
String packet = "";
|
||||
if (Config.loramodule.rxActive) {
|
||||
|
||||
+34
-43
@@ -33,6 +33,7 @@
|
||||
|
||||
|
||||
extern Configuration Config;
|
||||
extern APRSPacket lastAprsPacket;
|
||||
extern NetworkManager *networkManager;
|
||||
extern WiFiClient aprsIsClient;
|
||||
extern uint32_t lastScreenOn;
|
||||
@@ -92,7 +93,9 @@ namespace APRS_IS_Utils {
|
||||
}
|
||||
}
|
||||
|
||||
void checkStatus() {
|
||||
void updateWiFiAPRSISDisplayInfo() {
|
||||
static String lastWifiState = "";
|
||||
static String lastAprsisState = "";
|
||||
String wifiState, aprsisState;
|
||||
if (networkManager->isWiFiConnected()) {
|
||||
wifiState = "OK";
|
||||
@@ -102,10 +105,6 @@ namespace APRS_IS_Utils {
|
||||
} else {
|
||||
wifiState = "AP";
|
||||
}
|
||||
if (!Config.display.alwaysOn && Config.display.timeout != 0) {
|
||||
displayToggle(true);
|
||||
}
|
||||
lastScreenOn = millis();
|
||||
}
|
||||
|
||||
if (!Config.aprs_is.active) {
|
||||
@@ -124,22 +123,21 @@ namespace APRS_IS_Utils {
|
||||
aprsisState = "--";
|
||||
}
|
||||
#endif
|
||||
if(aprsisState == "--" && !Config.display.alwaysOn && Config.display.timeout != 0) {
|
||||
displayToggle(true);
|
||||
lastScreenOn = millis();
|
||||
}
|
||||
}
|
||||
|
||||
if (wifiState != lastWifiState || aprsisState != lastAprsisState) { // wake display only on status change (not every loop) so display timeout works
|
||||
if (!Config.display.alwaysOn && Config.display.timeout != 0) displayToggle(true);
|
||||
lastScreenOn = millis();
|
||||
lastWifiState = wifiState;
|
||||
lastAprsisState = aprsisState;
|
||||
}
|
||||
|
||||
secondLine = "WiFi: ";
|
||||
secondLine += wifiState;
|
||||
secondLine += " APRS-IS: ";
|
||||
secondLine += aprsisState;
|
||||
}
|
||||
|
||||
String checkForStartingBytes(const String& packet) {
|
||||
int index = packet.indexOf("\x3c\xff\x01");
|
||||
return (index != -1) ? packet.substring(0, index) : packet;
|
||||
}
|
||||
|
||||
String buildPacketToUpload(const String& packet) {
|
||||
int colonIndex = packet.indexOf(":");
|
||||
String packetToUpload = packet.substring(3, colonIndex);
|
||||
@@ -149,11 +147,11 @@ namespace APRS_IS_Utils {
|
||||
packetToUpload += ",qAO,";
|
||||
}
|
||||
packetToUpload += Config.callsign;
|
||||
packetToUpload += checkForStartingBytes(packet.substring(colonIndex));
|
||||
packetToUpload += APRSPacketLib::checkForStartingBytes(packet.substring(colonIndex));
|
||||
return packetToUpload;
|
||||
}
|
||||
|
||||
bool processReceivedLoRaMessage(const String& sender, const String& packet, bool thirdParty) {
|
||||
bool processReceivedLoRaMessage(const String& sender, const String& packet) {
|
||||
String receivedMessage;
|
||||
int leftCurlyBraceIndex = packet.indexOf("{");
|
||||
int colonIndex = packet.indexOf(":");
|
||||
@@ -165,7 +163,7 @@ namespace APRS_IS_Utils {
|
||||
|
||||
String addToBuffer = Config.callsign;
|
||||
addToBuffer += ">APLRG1";
|
||||
if (!thirdParty) addToBuffer += ",RFONLY";
|
||||
if (lastAprsPacket.header == "") addToBuffer += ",RFONLY";
|
||||
if (Config.beacon.path != "") {
|
||||
addToBuffer += ",";
|
||||
addToBuffer += Config.beacon.path;
|
||||
@@ -186,51 +184,44 @@ namespace APRS_IS_Utils {
|
||||
receivedMessage = packet.substring(colonIndex + 1);
|
||||
}
|
||||
if (receivedMessage.indexOf("?") == 0) {
|
||||
if (!Config.display.alwaysOn && Config.display.timeout != 0) {
|
||||
displayToggle(true);
|
||||
}
|
||||
STATION_Utils::addToOutputPacketBuffer(QUERY_Utils::process(receivedMessage, sender, false, thirdParty));
|
||||
if (!Config.display.alwaysOn && Config.display.timeout != 0) displayToggle(true);
|
||||
|
||||
STATION_Utils::addToOutputPacketBuffer(QUERY_Utils::process(receivedMessage, sender, false, lastAprsPacket.header != ""));
|
||||
lastScreenOn = millis();
|
||||
displayShow(firstLine, secondLine, thirdLine, fourthLine, fifthLine, "Callsign = " + sender, "TYPE --> QUERY", 0);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void processLoRaPacket(const String& packet) {
|
||||
if (passcodeValid && (aprsIsClient.connected() || modemLoggedToAPRSIS)) {
|
||||
if (packet.indexOf("NOGATE") == -1 && packet.indexOf("RFONLY") == -1) {
|
||||
int firstColonIndex = packet.indexOf(":");
|
||||
if (firstColonIndex > 5 && firstColonIndex < (packet.length() - 1) && packet[firstColonIndex + 1] != '}' && packet.indexOf("TCPIP") == -1) {
|
||||
const String& Sender = packet.substring(3, packet.indexOf(">"));
|
||||
if (Sender != Config.callsign && Utils::callsignIsValid(Sender)) {
|
||||
STATION_Utils::updateLastHeard(Sender);
|
||||
Utils::typeOfPacket(packet.substring(3), 0); // LoRa-APRS
|
||||
int doubleColonIndex = packet.indexOf("::");
|
||||
const String& AddresseeAndMessage = packet.substring(doubleColonIndex + 2);
|
||||
String Addressee = AddresseeAndMessage.substring(0, AddresseeAndMessage.indexOf(":"));
|
||||
Addressee.trim();
|
||||
if (lastAprsPacket.path.indexOf("NOGATE") == -1 && lastAprsPacket.path.indexOf("RFONLY") == -1) {
|
||||
if (lastAprsPacket.header == "" && lastAprsPacket.path.indexOf("TCPIP") == -1) {
|
||||
if (lastAprsPacket.sender != Config.callsign && Utils::callsignIsValid(lastAprsPacket.sender)) {
|
||||
STATION_Utils::updateLastHeard(lastAprsPacket.sender);
|
||||
Utils::updateLoRaPacketDisplayInfo(lastAprsPacket, 0); // LoRa-APRS
|
||||
bool queryMessage = false;
|
||||
if (doubleColonIndex > 10 && Addressee == Config.callsign) { // its a message for me!
|
||||
queryMessage = processReceivedLoRaMessage(Sender, checkForStartingBytes(AddresseeAndMessage), false);
|
||||
if (lastAprsPacket.type == 1 && lastAprsPacket.addressee == Config.callsign) { // its a message for me!
|
||||
String AddresseeAndMessage = lastAprsPacket.addressee + ":" + lastAprsPacket.payload;
|
||||
queryMessage = processReceivedLoRaMessage(lastAprsPacket.sender, APRSPacketLib::checkForStartingBytes(AddresseeAndMessage));
|
||||
}
|
||||
if (queryMessage) return;
|
||||
|
||||
const String& aprsPacket = buildPacketToUpload(packet);
|
||||
const String& aprsPacketToUpload = buildPacketToUpload(packet);
|
||||
if (!Config.display.alwaysOn && Config.display.timeout != 0) {
|
||||
displayToggle(true);
|
||||
}
|
||||
lastScreenOn = millis();
|
||||
#ifdef HAS_A7670
|
||||
stationBeacon = true;
|
||||
A7670_Utils::uploadToAPRSIS(aprsPacket);
|
||||
A7670_Utils::uploadToAPRSIS(aprsPacketToUpload);
|
||||
stationBeacon = false;
|
||||
#else
|
||||
upload(aprsPacket);
|
||||
upload(aprsPacketToUpload);
|
||||
#endif
|
||||
Utils::println("---> Uploaded to APRS-IS");
|
||||
Utils::println("(Uploaded to APRS-IS)");
|
||||
displayShow(firstLine, secondLine, thirdLine, fourthLine, fifthLine, sixthLine, seventhLine, 0);
|
||||
}
|
||||
}
|
||||
@@ -351,7 +342,7 @@ namespace APRS_IS_Utils {
|
||||
#else
|
||||
upload(queryAnswer);
|
||||
#endif
|
||||
SYSLOG_Utils::log(2, queryAnswer, 0, 0.0, 0); // APRSIS TX
|
||||
SYSLOG_Utils::logAPRSISTx(queryAnswer);
|
||||
fifthLine = "APRS-IS ----> APRS-IS";
|
||||
sixthLine = Config.callsign;
|
||||
for (int j = sixthLine.length();j < 9;j++) {
|
||||
@@ -369,7 +360,7 @@ namespace APRS_IS_Utils {
|
||||
STATION_Utils::addToOutputPacketBuffer(buildPacketToTx(packet, 1));
|
||||
displayToggle(true);
|
||||
lastScreenOn = currentTime;
|
||||
Utils::typeOfPacket(packet, 1); // APRS-LoRa
|
||||
Utils::updateAPRSISPacketDisplayInfo(packet); // APRS-LoRa
|
||||
displayShow(firstLine, secondLine, thirdLine, fourthLine, fifthLine, sixthLine, seventhLine, 0);
|
||||
}
|
||||
}
|
||||
@@ -379,7 +370,7 @@ namespace APRS_IS_Utils {
|
||||
STATION_Utils::addToOutputPacketBuffer(buildPacketToTx(packet, 5));
|
||||
displayToggle(true);
|
||||
lastScreenOn = currentTime;
|
||||
Utils::typeOfPacket(packet, 1); // APRS-LoRa
|
||||
Utils::updateAPRSISPacketDisplayInfo(packet); // APRS-LoRa
|
||||
Serial.println();
|
||||
} else {
|
||||
Serial.println(" ---> Rejected (Time): No Tx");
|
||||
|
||||
@@ -160,7 +160,7 @@ namespace BATTERY_Utils {
|
||||
|
||||
int sampleSum = 0;
|
||||
for (int i = 0; i < adcReadings; i++) {
|
||||
#if defined(ESP32_DIY_LoRa) || defined(ESP32_DIY_LoRa_915) || defined(ESP32_DIY_1W_LoRa) || defined(ESP32_DIY_1W_LoRa_915)
|
||||
#if defined(ESP32_DIY_LoRa_SX1278) || defined(ESP32_DIY_LoRa_915_SX1276) || defined(ESP32_DIY_1W_LoRa_E22_400M30S) || defined(ESP32_DIY_1W_LoRa_915_E22_900M30S) || defined(ESP32_DIY_1W_LoRa_E32_400M30S) || defined(ESP32C3_SuperMini_DIY_LoRa_SX1278)
|
||||
sampleSum = 0;
|
||||
#else
|
||||
#ifdef HAS_ADC_CALIBRATION
|
||||
@@ -203,6 +203,10 @@ namespace BATTERY_Utils {
|
||||
#ifdef LIGHTGATEWAY_PLUS_1_0
|
||||
double inputDivider = (1.0 / (560.0 + 100.0)) * 100.0; // The voltage divider is a 560k + 100k resistor in series, 100k on the low side.
|
||||
return (((sampleSum/adcReadings) * adcReadingTransformation) / inputDivider) + 0.41;
|
||||
#elif defined(RAK3112)
|
||||
return (sampleSum/adcReadings) * adcReadingTransformation * (3 * 1.73 * 1.187 / 3.3); // multiplier from MeshCore RAK3112Board.h
|
||||
#elif defined(RAK3312)
|
||||
return (sampleSum/adcReadings) * adcReadingTransformation * (3 * 1.73 * 1.187 / 3.3) * (1.667 / 2.11); // RAK3112 value scaled by Meshtastic RAK3312/RAK3112 ratio
|
||||
#else
|
||||
return (2 * (sampleSum/adcReadings) * adcReadingTransformation) + voltageDividerCorrection; // raw voltage without mapping
|
||||
#endif
|
||||
|
||||
+25
-36
@@ -16,6 +16,7 @@
|
||||
* along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <APRSPacketLib.h>
|
||||
#include <WiFi.h>
|
||||
#include "configuration.h"
|
||||
#include "station_utils.h"
|
||||
@@ -29,6 +30,7 @@
|
||||
|
||||
extern Configuration Config;
|
||||
extern uint32_t lastScreenOn;
|
||||
extern APRSPacket lastAprsPacket;
|
||||
extern String iGateBeaconPacket;
|
||||
extern String firstLine;
|
||||
extern String secondLine;
|
||||
@@ -99,9 +101,9 @@ namespace DIGI_Utils {
|
||||
return tempPacket + "*" + path.substring(ownTokenEnd);
|
||||
}
|
||||
|
||||
String buildPacket(const String& path, const String& packet, bool thirdParty, bool crossFreq) {
|
||||
String buildPacket(const String& path, const String& packet, bool crossFreq) {
|
||||
String stationCallsign = (Config.tacticalCallsign == "" ? Config.callsign : Config.tacticalCallsign);
|
||||
String suffix = thirdParty ? ":}" : ":";
|
||||
String suffix = (lastAprsPacket.header != "") ? ":}" : ":";
|
||||
int suffixIndex = packet.indexOf(suffix);
|
||||
String packetToRepeat;
|
||||
if (!crossFreq) {
|
||||
@@ -134,13 +136,13 @@ namespace DIGI_Utils {
|
||||
packetToRepeat += stationCallsign;
|
||||
packetToRepeat += "*";
|
||||
}
|
||||
packetToRepeat += APRS_IS_Utils::checkForStartingBytes(packet.substring(suffixIndex));
|
||||
packetToRepeat += APRSPacketLib::checkForStartingBytes(packet.substring(suffixIndex));
|
||||
return packetToRepeat;
|
||||
}
|
||||
|
||||
String generateDigipeatedPacket(const String& packet, bool thirdParty){
|
||||
String generateDigipeatedPacket(const String& packet){
|
||||
String temp;
|
||||
if (thirdParty) { // only header is used
|
||||
if (lastAprsPacket.header != "") { // thirdparty : only header is used
|
||||
const String& header = packet.substring(0, packet.indexOf(":}"));
|
||||
temp = header.substring(header.indexOf(">") + 1);
|
||||
} else {
|
||||
@@ -155,7 +157,7 @@ namespace DIGI_Utils {
|
||||
if (digiMode == 1 || backupDigiMode) {
|
||||
bool hasWide = path.indexOf("WIDE1-1") != -1;
|
||||
if (hasWide || crossFreq) {
|
||||
return buildPacket(path, packet, thirdParty, !hasWide);
|
||||
return buildPacket(path, packet, !hasWide);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
@@ -167,62 +169,49 @@ namespace DIGI_Utils {
|
||||
|
||||
if (hasWide1 && hasWide2 && wide2Index < wide1Index) return ""; // check that WIDE1 before WIDE2
|
||||
|
||||
if (hasWide1 || hasWide2) return buildPacket(path, packet, thirdParty, false); // regular APRS with WIDEn-N
|
||||
if (hasWide1 || hasWide2) return buildPacket(path, packet, false); // regular APRS with WIDEn-N
|
||||
|
||||
if (crossFreq) return buildPacket(path, packet, thirdParty, true); // CrossFreq (without WIDE)
|
||||
if (crossFreq) return buildPacket(path, packet, true); // CrossFreq (without WIDE)
|
||||
|
||||
return "";
|
||||
}
|
||||
if (digiMode == 3) {
|
||||
String stationCallsign = (Config.tacticalCallsign == "" ? Config.callsign : Config.tacticalCallsign);
|
||||
bool containsOwnCall = path.indexOf(stationCallsign) != -1;
|
||||
if (containsOwnCall) return buildPacket(path, packet, thirdParty, false);
|
||||
if (containsOwnCall) return buildPacket(path, packet, false);
|
||||
return "";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
if (commaIndex == -1 && (digiMode == 1 || backupDigiMode || digiMode == 2) && crossFreq) return buildPacket("", packet, thirdParty, true); // no "path" but is CrossFreq Digi
|
||||
if (commaIndex == -1 && (digiMode == 1 || backupDigiMode || digiMode == 2) && crossFreq) return buildPacket("", packet, true); // no "path" but is CrossFreq Digi
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
void processLoRaPacket(const String& packet) {
|
||||
if (packet.indexOf("NOGATE") >= 0) return;
|
||||
if (lastAprsPacket.path.indexOf("NOGATE") >= 0) return;
|
||||
|
||||
bool thirdPartyPacket = false;
|
||||
String temp, Sender;
|
||||
int firstColonIndex = packet.indexOf(":");
|
||||
if (firstColonIndex > 5 && firstColonIndex < (packet.length() - 1) && packet[firstColonIndex + 1] == '}' && packet.indexOf("TCPIP") > 0) { // 3rd Party
|
||||
thirdPartyPacket = true;
|
||||
temp = packet.substring(packet.indexOf(":}") + 2);
|
||||
Sender = temp.substring(0, temp.indexOf(">"));
|
||||
} else {
|
||||
temp = packet.substring(3);
|
||||
Sender = packet.substring(3, packet.indexOf(">"));
|
||||
}
|
||||
String temp = (lastAprsPacket.header != "") ? packet.substring(packet.indexOf(":}") + 2) : packet.substring(3);
|
||||
|
||||
String stationCallsign = Config.tacticalCallsign == "" ? Config.callsign : Config.tacticalCallsign;
|
||||
if (Sender == stationCallsign) return; // Avoid listening to self packets
|
||||
if (!thirdPartyPacket && Config.tacticalCallsign == "" && !Utils::callsignIsValid(Sender)) return; // No thirdParty + no tactical y no valid callsign
|
||||
if (lastAprsPacket.sender == stationCallsign) return; // Avoid listening to self packets
|
||||
if (lastAprsPacket.header == "" && Config.tacticalCallsign == "" && !Utils::callsignIsValid(lastAprsPacket.sender)) return; // No thirdParty + no tactical y no valid callsign
|
||||
|
||||
if (STATION_Utils::isIn25SegHashBuffer(Sender, temp.substring(temp.indexOf(":") + 2))) return;
|
||||
if (STATION_Utils::isIn25SegHashBuffer(lastAprsPacket.sender, temp.substring(temp.indexOf(":") + 2))) return;
|
||||
|
||||
STATION_Utils::updateLastHeard(Sender);
|
||||
Utils::typeOfPacket(temp, 2); // Digi
|
||||
bool queryMessage = false;
|
||||
int doubleColonIndex = temp.indexOf("::");
|
||||
if (doubleColonIndex > 10) { // it's a message
|
||||
String AddresseeAndMessage = temp.substring(doubleColonIndex + 2);
|
||||
String Addressee = AddresseeAndMessage.substring(0, AddresseeAndMessage.indexOf(":"));
|
||||
Addressee.trim();
|
||||
if (Addressee == stationCallsign) { // it's a message for me!
|
||||
queryMessage = APRS_IS_Utils::processReceivedLoRaMessage(Sender, AddresseeAndMessage, thirdPartyPacket);
|
||||
STATION_Utils::updateLastHeard(lastAprsPacket.sender);
|
||||
Utils::updateLoRaPacketDisplayInfo(lastAprsPacket, 1); // Digi
|
||||
bool queryMessage = false;
|
||||
if (lastAprsPacket.type == 1) { // MESSAGE
|
||||
if (lastAprsPacket.addressee == stationCallsign) { // it's a message for me!
|
||||
String AddresseeAndMessage = lastAprsPacket.addressee + ":" + lastAprsPacket.payload;
|
||||
queryMessage = APRS_IS_Utils::processReceivedLoRaMessage(lastAprsPacket.sender, APRSPacketLib::checkForStartingBytes(AddresseeAndMessage));
|
||||
}
|
||||
}
|
||||
if (queryMessage) return; // answer should not be repeated.
|
||||
|
||||
String loraPacket = generateDigipeatedPacket(packet.substring(3), thirdPartyPacket);
|
||||
String loraPacket = generateDigipeatedPacket(packet.substring(3));
|
||||
if (loraPacket != "") {
|
||||
STATION_Utils::addToOutputPacketBuffer(loraPacket);
|
||||
if (Config.digi.ecoMode != 1) displayToggle(true);
|
||||
|
||||
@@ -101,97 +101,6 @@ namespace GPS_Utils {
|
||||
|
||||
}
|
||||
|
||||
String decodeEncodedGPS(const String& packet) {
|
||||
int indexOfExclamation = packet.indexOf(":!");
|
||||
int indexOfEqual = packet.indexOf(":=");
|
||||
|
||||
const uint8_t OFFSET = 3; // Offset for encoded data in the packet
|
||||
String infoGPS;
|
||||
if (indexOfExclamation > 10) {
|
||||
infoGPS = packet.substring(indexOfExclamation + OFFSET);
|
||||
} else if (indexOfEqual > 10) {
|
||||
infoGPS = packet.substring(indexOfEqual + OFFSET);
|
||||
}
|
||||
|
||||
float decodedLatitude = APRSPacketLib::decodeBase91EncodedLatitude(infoGPS.substring(0,4));
|
||||
float decodedLongitude = APRSPacketLib::decodeBase91EncodedLongitude(infoGPS.substring(4,8));
|
||||
|
||||
return buildDistanceAndComment(decodedLatitude, decodedLongitude, infoGPS.substring(12));
|
||||
}
|
||||
|
||||
String getReceivedGPS(const String& packet) {
|
||||
int indexOfExclamation = packet.indexOf(":!");
|
||||
int indexOfEqual = packet.indexOf(":=");
|
||||
int indexOfAt = packet.indexOf(":@");
|
||||
|
||||
String infoGPS;
|
||||
if (indexOfExclamation > 10) {
|
||||
infoGPS = packet.substring(indexOfExclamation + 2);
|
||||
} else if (indexOfEqual > 10) {
|
||||
infoGPS = packet.substring(indexOfEqual + 2);
|
||||
} else if (indexOfAt > 10) {
|
||||
infoGPS = packet.substring(indexOfAt + 9); // 9 = 2+7 (when 7 is timestamp characters)
|
||||
}
|
||||
|
||||
String Latitude = infoGPS.substring(0,8); // First 8 characters are Latitude
|
||||
int latitudeColonIndex = Latitude.indexOf(".");
|
||||
float convertedLatitude = Latitude.substring(0,2).toFloat(); // First 2 digits (Degrees)
|
||||
convertedLatitude += Latitude.substring(2,4).toFloat() / 60; // Next 2 digits (Minutes)
|
||||
convertedLatitude += Latitude.substring(latitudeColonIndex + 1, latitudeColonIndex + 3).toFloat() / (60*100);
|
||||
if (Latitude.endsWith("S")) convertedLatitude = -convertedLatitude; // Handle Southern Hemisphere
|
||||
|
||||
String Longitude = infoGPS.substring(9,18); // Next 9 characters are Longitude
|
||||
int longitudeColonIndex = Longitude.indexOf(".");
|
||||
float convertedLongitude = Longitude.substring(0,3).toFloat(); // First 3 digits (Degrees)
|
||||
convertedLongitude += Longitude.substring(3,5).toFloat() / 60; // Next 2 digits (Minutes)
|
||||
convertedLongitude += Longitude.substring(longitudeColonIndex + 1, longitudeColonIndex + 3).toFloat() / (60*100);
|
||||
if (Longitude.endsWith("W")) convertedLongitude = -convertedLongitude; // Handle Western Hemisphere
|
||||
|
||||
return buildDistanceAndComment(convertedLatitude, convertedLongitude, infoGPS.substring(19));
|
||||
}
|
||||
|
||||
String getDistanceAndComment(const String& packet) {
|
||||
int indexOfAt = packet.indexOf(":@");
|
||||
if (indexOfAt > 10) return getReceivedGPS(packet);
|
||||
|
||||
int indexOfExclamation = packet.indexOf(":!");
|
||||
int indexOfEqual = packet.indexOf(":=");
|
||||
int baseIndex = - 1;
|
||||
if (indexOfExclamation > 10) {
|
||||
baseIndex = indexOfExclamation;
|
||||
} else if (indexOfEqual > 10) {
|
||||
baseIndex = indexOfEqual;
|
||||
}
|
||||
if (baseIndex == -1) return " _ / _ / _ ";
|
||||
|
||||
int packetLength = packet.length();
|
||||
if (baseIndex + 2 >= packetLength) return " _ / _ / _ ";
|
||||
|
||||
// Spec-based: the first character right after the DTI decides the format.
|
||||
// Uncompressed positions always start with a latitude digit.
|
||||
// Compressed positions always start with the symbol table id: '/', '\', or an overlay A-Z/a-j.
|
||||
char firstInfoChar = packet[baseIndex + 2];
|
||||
if (firstInfoChar >= '0' && firstInfoChar <= '9') return getReceivedGPS(packet);
|
||||
if (firstInfoChar == '/' || firstInfoChar == '\\' ||
|
||||
(firstInfoChar >= 'A' && firstInfoChar <= 'Z') ||
|
||||
(firstInfoChar >= 'a' && firstInfoChar <= 'j')) return decodeEncodedGPS(packet);
|
||||
return " _ / _ / _ ";
|
||||
}
|
||||
|
||||
String getDistanceAndCommentFromMicE(const String& packet) {
|
||||
// Mic-E encodes latitude in the AX.25 destination field and longitude
|
||||
// in the info field with its own +28 offset scheme -- nothing like
|
||||
// the non-encoded/base91 formats above. APRSPacketLib::processReceivedPacket
|
||||
// already implements that decode (used for the map upsert in
|
||||
// lora_utils.cpp), so it's reused here instead of duplicating it.
|
||||
if (packet.length() < 4) return " _ / _ / _ ";
|
||||
|
||||
APRSPacket aprsPacket = APRSPacketLib::processReceivedPacket(packet.substring(3), 0, 0, 0);
|
||||
if (aprsPacket.type != 4) return " _ / _ / _ "; // 4 = Mic-E
|
||||
|
||||
return buildDistanceAndComment(aprsPacket.latitude, aprsPacket.longitude, "");
|
||||
}
|
||||
|
||||
void setup() {
|
||||
#ifdef HAS_GPS
|
||||
if (Config.beacon.gpsActive && Config.digi.ecoMode != 1) {
|
||||
|
||||
+18
-12
@@ -65,6 +65,7 @@ int backoffMax = 4; // Max Backoff value (number of CAD slots to wai
|
||||
|
||||
int rssi, freqError;
|
||||
float snr;
|
||||
APRSPacket lastAprsPacket;
|
||||
|
||||
|
||||
namespace LoRa_Utils {
|
||||
@@ -90,10 +91,17 @@ namespace LoRa_Utils {
|
||||
#else
|
||||
SPI.begin(RADIO_SCLK_PIN, RADIO_MISO_PIN, RADIO_MOSI_PIN);
|
||||
#endif
|
||||
#ifdef RADIO_ANT_SW_PIN // RAK3312 antenna switch needs power
|
||||
pinMode(RADIO_ANT_SW_PIN, OUTPUT);
|
||||
digitalWrite(RADIO_ANT_SW_PIN, RADIO_ANT_SW_ON_STATE);
|
||||
#endif
|
||||
float freq = (float)Config.loramodule.rxFreq / 1000000;
|
||||
#if defined(RADIO_HAS_XTAL)
|
||||
radio.XTAL = true;
|
||||
#endif
|
||||
#if (defined(RADIO_RXEN) && defined(RADIO_TXEN)) // before begin() so RF switch is driven from start (Ebyte E22/E32 1W, QRP Labs LightGateway)
|
||||
radio.setRfSwitchPins(RADIO_RXEN, RADIO_TXEN);
|
||||
#endif
|
||||
int state = radio.begin(freq);
|
||||
if (state != RADIOLIB_ERR_NONE) {
|
||||
Utils::println("Starting LoRa failed! State: " + String(state));
|
||||
@@ -121,10 +129,6 @@ namespace LoRa_Utils {
|
||||
radio.setBandwidth(signalBandwidth);
|
||||
radio.setCRC(true);
|
||||
|
||||
#if (defined(RADIO_RXEN) && defined(RADIO_TXEN)) // QRP Labs LightGateway has 400M22S (SX1268)
|
||||
radio.setRfSwitchPins(RADIO_RXEN, RADIO_TXEN);
|
||||
#endif
|
||||
|
||||
/*#ifdef SX126X_DIO2_AS_RF_SWITCH
|
||||
radio.setRfSwitchPins(RADIO_RXEN, RADIOLIB_NC);
|
||||
radio.setDio2AsRfSwitch(true);
|
||||
@@ -230,7 +234,7 @@ namespace LoRa_Utils {
|
||||
transmitFlag = true;
|
||||
if (state == RADIOLIB_ERR_NONE) {
|
||||
if (Config.syslog.active && networkManager->isConnected()) {
|
||||
SYSLOG_Utils::log(3, newPacket, 0, 0.0, 0); // TX
|
||||
SYSLOG_Utils::logLoRaTx(newPacket);
|
||||
}
|
||||
Utils::print("---> LoRa Packet Tx : ");
|
||||
Utils::println(newPacket);
|
||||
@@ -276,9 +280,9 @@ namespace LoRa_Utils {
|
||||
rssi = radio.getRSSI();
|
||||
snr = radio.getSNR();
|
||||
freqError = radio.getFrequencyError();
|
||||
Utils::println("<--- LoRa Packet Rx : " + packet.substring(3));
|
||||
Utils::println("(RSSI:" + String(rssi) + " / SNR:" + String(snr) + " / FreqErr:" + String(freqError) + ")");
|
||||
Utils::println("<--- LoRa Packet Rx (RSSI:" + String(rssi) + " | SNR:" + String(snr) + " | FreqErr:" + String(freqError) + ") : " + packet.substring(3));
|
||||
|
||||
lastAprsPacket = APRSPacketLib::processReceivedPacket(packet.substring(3), rssi, snr, freqError);
|
||||
if (Config.digi.ecoMode == 0) {
|
||||
if (receivedPackets.size() >= 10) {
|
||||
receivedPackets.erase(receivedPackets.begin());
|
||||
@@ -290,14 +294,13 @@ namespace LoRa_Utils {
|
||||
receivedPacket.SNR = snr;
|
||||
receivedPackets.push_back(receivedPacket);
|
||||
|
||||
APRSPacket aprsPacket = APRSPacketLib::processReceivedPacket(packet.substring(3), rssi, snr, freqError);
|
||||
if (aprsPacket.type == 0 || aprsPacket.type == 4) { // 0 = GPS, 4 = Mic-E (only ones with position)
|
||||
MAP_Utils::upsert(aprsPacket.sender, aprsPacket.latitude, aprsPacket.longitude, aprsPacket.path, aprsPacket.overlay + aprsPacket.symbol, aprsPacket.rssi, aprsPacket.snr);
|
||||
if (lastAprsPacket.type == 0 || lastAprsPacket.type == 4) { // 0 = GPS, 4 = Mic-E (only ones with position)
|
||||
MAP_Utils::upsert(lastAprsPacket.sender, lastAprsPacket.latitude, lastAprsPacket.longitude, lastAprsPacket.path, lastAprsPacket.overlay + lastAprsPacket.symbol, lastAprsPacket.rssi, lastAprsPacket.snr);
|
||||
}
|
||||
}
|
||||
|
||||
if (Config.syslog.active && networkManager->isConnected()) {
|
||||
SYSLOG_Utils::log(1, packet, rssi, snr, freqError); // RX
|
||||
SYSLOG_Utils::logLoRaRx(lastAprsPacket, packet, rssi, snr, freqError); // RX
|
||||
}
|
||||
} else {
|
||||
packet = "";
|
||||
@@ -310,7 +313,7 @@ namespace LoRa_Utils {
|
||||
freqError = radio.getFrequencyError();
|
||||
Utils::println(F("CRC error!"));
|
||||
if (Config.syslog.active && networkManager->isConnected()) {
|
||||
SYSLOG_Utils::log(0, packet, rssi, snr, freqError); // CRC
|
||||
SYSLOG_Utils::logCRCError(packet, rssi, snr, freqError);
|
||||
}
|
||||
packet = "";
|
||||
} else {
|
||||
@@ -329,6 +332,9 @@ namespace LoRa_Utils {
|
||||
|
||||
void sleepRadio() {
|
||||
radio.sleep();
|
||||
#ifdef RADIO_ANT_SW_PIN
|
||||
digitalWrite(RADIO_ANT_SW_PIN, !RADIO_ANT_SW_ON_STATE);
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
@@ -339,6 +339,10 @@ bool NetworkManager::isWifiAPActive() const {
|
||||
return _wifiAPmode;
|
||||
}
|
||||
|
||||
bool NetworkManager::hasActiveInterface() const {
|
||||
return _wifiSTAmode || _wifiAPmode || _ethernetMode;
|
||||
}
|
||||
|
||||
// Check if Ethernet is connected
|
||||
bool NetworkManager::isEthernetConnected() const {
|
||||
return _ethernetMode && _ethernetConnected && ETH.linkUp();
|
||||
|
||||
+21
-14
@@ -16,8 +16,7 @@
|
||||
* along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <NTPClient.h>
|
||||
#include <WiFiUdp.h>
|
||||
#include <esp_sntp.h>
|
||||
#include "configuration.h"
|
||||
#include "network_manager.h"
|
||||
#include "ntp_utils.h"
|
||||
@@ -26,18 +25,19 @@
|
||||
|
||||
extern Configuration Config;
|
||||
extern NetworkManager *networkManager;
|
||||
WiFiUDP ntpUDP;
|
||||
NTPClient* timeClient = nullptr;
|
||||
bool ntpStarted = false;
|
||||
bool ntpSynced = false;
|
||||
|
||||
|
||||
namespace NTP_Utils {
|
||||
|
||||
bool setup() {
|
||||
if (networkManager->isConnected() && Config.digi.ecoMode == 0 && Config.callsign != "NOCALL-10") {
|
||||
int gmt = Config.ntp.gmtCorrection * 3600;
|
||||
long gmt = Config.ntp.gmtCorrection * 3600;
|
||||
Serial.println("[NTP] Setting up, TZ offset: " + String(gmt) + " Server: " + Config.ntp.server);
|
||||
timeClient = new NTPClient(ntpUDP, Config.ntp.server.c_str(), gmt, 15 * 60 * 1000); // Update interval 15 min
|
||||
timeClient->begin();
|
||||
sntp_set_sync_interval(3 * 60 * 60 * 1000); // Update interval 3 hours (ESP32 internal clock drift is negligible in that time)
|
||||
configTime(gmt, 0, Config.ntp.server.c_str()); // SNTP runs in background (DNS + retries) and never blocks the loop
|
||||
ntpStarted = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -47,20 +47,27 @@ namespace NTP_Utils {
|
||||
if (!networkManager->isConnected() || Config.digi.ecoMode != 0 || Config.callsign == "NOCALL-10") {
|
||||
return;
|
||||
}
|
||||
if (timeClient == nullptr) {
|
||||
if (!ntpStarted) {
|
||||
if (!setup()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
timeClient->update();
|
||||
if (!ntpSynced) {
|
||||
struct tm timeinfo;
|
||||
if (getLocalTime(&timeinfo, 0)) {
|
||||
ntpSynced = true;
|
||||
Serial.println("[NTP] Time synced: " + getFormatedTime());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String getFormatedTime() {
|
||||
if (networkManager->isConnected() && Config.digi.ecoMode == 0 && timeClient != nullptr) {
|
||||
return timeClient->getFormattedTime();
|
||||
}
|
||||
return "DigiEcoMode Active";
|
||||
if (Config.digi.ecoMode != 0) return "DigiEcoMode Active";
|
||||
struct tm timeinfo;
|
||||
if (!ntpStarted || !getLocalTime(&timeinfo, 0)) return ""; // 0 ms: don't wait (default timeout would block 5 s)
|
||||
char formatedTime[9];
|
||||
strftime(formatedTime, sizeof(formatedTime), "%H:%M:%S", &timeinfo);
|
||||
return String(formatedTime);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "battery_utils.h"
|
||||
#include "board_pinout.h"
|
||||
#include "power_utils.h"
|
||||
#include "wx_utils.h"
|
||||
#include "utils.h"
|
||||
|
||||
#if defined(HAS_AXP192) || defined(HAS_AXP2101)
|
||||
@@ -210,6 +211,23 @@ namespace POWER_Utils {
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef SOLAR_CHARGE_PIN
|
||||
void checkSolarCharge() { // called on each beacon: disable solar charging when too cold for Li-Ion
|
||||
static bool chargingEnabled = true;
|
||||
float boardTemp = WX_Utils::readBoardTemperature();
|
||||
if (isnan(boardTemp)) {
|
||||
chargingEnabled = true; // sensor failure: keep charging (previous behaviour)
|
||||
} else if (chargingEnabled && boardTemp < SOLAR_CHARGE_DISABLE_TEMP) {
|
||||
chargingEnabled = false;
|
||||
Serial.println("[SOLAR] Charging disabled, board temp: " + String(boardTemp, 1) + "C");
|
||||
} else if (!chargingEnabled && boardTemp > SOLAR_CHARGE_ENABLE_TEMP) {
|
||||
chargingEnabled = true;
|
||||
Serial.println("[SOLAR] Charging enabled, board temp: " + String(boardTemp, 1) + "C");
|
||||
}
|
||||
digitalWrite(SOLAR_CHARGE_PIN, chargingEnabled ? SOLAR_CHARGE_ENABLED_STATE : !SOLAR_CHARGE_ENABLED_STATE);
|
||||
}
|
||||
#endif
|
||||
|
||||
void setup() {
|
||||
#ifdef HAS_AXP192
|
||||
Wire.begin(SDA, SCL);
|
||||
@@ -254,6 +272,12 @@ namespace POWER_Utils {
|
||||
pinMode(BATTERY_PIN, INPUT);
|
||||
#endif
|
||||
|
||||
#ifdef SOLAR_CHARGE_PIN
|
||||
pinMode(SOLAR_CHARGE_PIN, OUTPUT);
|
||||
digitalWrite(SOLAR_CHARGE_PIN, SOLAR_CHARGE_ENABLED_STATE); // solar charging enabled by default
|
||||
gpio_hold_dis((gpio_num_t)SOLAR_CHARGE_PIN); // release hold kept during deep sleep (if any)
|
||||
#endif
|
||||
|
||||
#ifdef INTERNAL_LED_PIN
|
||||
pinMode(INTERNAL_LED_PIN, OUTPUT);
|
||||
digitalWrite(INTERNAL_LED_PIN, LOW);
|
||||
|
||||
+132
-97
@@ -16,6 +16,7 @@
|
||||
* along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <APRSPacketLib.h>
|
||||
#include <WiFiUdp.h>
|
||||
#include "configuration.h"
|
||||
#include "network_manager.h"
|
||||
@@ -30,116 +31,150 @@ extern String versionNumber;
|
||||
|
||||
WiFiUDP udpClient;
|
||||
|
||||
namespace {
|
||||
|
||||
String createSyslogStart() {
|
||||
String syslogStartPacket = "<165>1 - ";
|
||||
syslogStartPacket.concat(Config.callsign);
|
||||
syslogStartPacket.concat(" CA2RXU_LoRa_iGate_");
|
||||
syslogStartPacket.concat(versionNumber);
|
||||
syslogStartPacket.concat(" - - - "); //RFC5424 The Syslog Protocol
|
||||
return syslogStartPacket;
|
||||
}
|
||||
|
||||
void sendSyslogPacket(const String& syslogPacket) {
|
||||
udpClient.beginPacket(Config.syslog.server.c_str(), Config.syslog.port);
|
||||
udpClient.write((const uint8_t*)syslogPacket.c_str(), syslogPacket.length());
|
||||
udpClient.endPacket();
|
||||
}
|
||||
|
||||
String formatSignalData(int rssi, float snr, int freqError) {
|
||||
char signalData[35];
|
||||
snprintf(signalData, sizeof(signalData), " / %ddBm / %.2fdB / %dHz", rssi, snr, freqError);
|
||||
return String(signalData);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace SYSLOG_Utils {
|
||||
|
||||
void log(const uint8_t type, const String& packet, const int rssi, const float snr, const int freqError) {
|
||||
void logLoRaRx(APRSPacket& aprsPacket, const String& packet, const int rssi, const float snr, const int freqError) {
|
||||
if (Config.syslog.active && networkManager->isConnected()) {
|
||||
String syslogPacket = "<165>1 - ";
|
||||
syslogPacket.concat(Config.callsign);
|
||||
syslogPacket.concat(" CA2RXU_LoRa_iGate_");
|
||||
syslogPacket.concat(versionNumber);
|
||||
syslogPacket.concat(" - - - "); //RFC5424 The Syslog Protocol
|
||||
String syslogPacket = createSyslogStart();
|
||||
syslogPacket.concat("RX / ");
|
||||
|
||||
char signalData[35];
|
||||
snprintf(signalData, sizeof(signalData), " / %ddBm / %.2fdB / %dHz", rssi, snr, freqError);
|
||||
|
||||
int colonIndex = packet.indexOf(":");
|
||||
char nextChar = packet[colonIndex + 1];
|
||||
int greaterThanIndex = packet.indexOf(">");
|
||||
int telemetryPacketIndex = packet.indexOf(":T#");
|
||||
String sender = packet.substring(3, greaterThanIndex);
|
||||
|
||||
switch (type) {
|
||||
case 0: // CRC
|
||||
syslogPacket.concat("CRC / CRC-ERROR / ");
|
||||
syslogPacket.concat(packet);
|
||||
syslogPacket.concat(signalData);
|
||||
switch (aprsPacket.type) {
|
||||
case 1: // MESSAGE
|
||||
syslogPacket.concat("MESSAGE / ");
|
||||
syslogPacket.concat(aprsPacket.sender);
|
||||
syslogPacket.concat(" ---> ");
|
||||
syslogPacket.concat(aprsPacket.addressee);
|
||||
syslogPacket.concat(":");
|
||||
syslogPacket.concat(aprsPacket.payload);
|
||||
break;
|
||||
case 1: // RX
|
||||
syslogPacket.concat("RX / ");
|
||||
if (nextChar == ':') {
|
||||
syslogPacket.concat("MESSAGE / ");
|
||||
syslogPacket.concat(sender);
|
||||
syslogPacket.concat(" ---> ");
|
||||
syslogPacket.concat(packet.substring(colonIndex + 2));
|
||||
} else if (nextChar == '!' || nextChar == '=' || nextChar == '@') {
|
||||
syslogPacket.concat("GPS / ");
|
||||
syslogPacket.concat(sender);
|
||||
syslogPacket.concat(" / ");
|
||||
if (packet.indexOf("WIDE1-1") > 10) {
|
||||
syslogPacket.concat(packet.substring(greaterThanIndex + 1, packet.indexOf(",")));
|
||||
syslogPacket.concat(" / WIDE1-1");
|
||||
} else {
|
||||
syslogPacket.concat(packet.substring(greaterThanIndex + 1, colonIndex));
|
||||
syslogPacket.concat(" / -");
|
||||
case 0: // GPS
|
||||
syslogPacket.concat("GPS / ");
|
||||
syslogPacket.concat(aprsPacket.sender);
|
||||
syslogPacket.concat(" / ");
|
||||
if (aprsPacket.path.indexOf("WIDE1-1") != -1) {
|
||||
syslogPacket.concat(aprsPacket.tocall);
|
||||
syslogPacket.concat(" / WIDE1-1");
|
||||
} else {
|
||||
syslogPacket.concat(aprsPacket.tocall);
|
||||
if (aprsPacket.path != "") {
|
||||
syslogPacket.concat(",");
|
||||
syslogPacket.concat(aprsPacket.path);
|
||||
}
|
||||
} else if (nextChar == '>') {
|
||||
syslogPacket.concat("STATUS / ");
|
||||
syslogPacket.concat(sender);
|
||||
syslogPacket.concat(" ---> ");
|
||||
syslogPacket.concat(packet.substring(colonIndex + 2));
|
||||
} else if (nextChar == '`' || nextChar == '\'') { // both Mic-E DTIs are valid
|
||||
syslogPacket.concat("MIC-E / ");
|
||||
syslogPacket.concat(sender);
|
||||
syslogPacket.concat(" ---> ");
|
||||
syslogPacket.concat(String(nextChar)); // restore the actual indicator received
|
||||
syslogPacket.concat(packet.substring(colonIndex + 2));
|
||||
syslogPacket.concat(" / ");
|
||||
syslogPacket.concat(GPS_Utils::getDistanceAndCommentFromMicE(packet));
|
||||
} else if (nextChar == ';') {
|
||||
syslogPacket.concat("OBJECT / ");
|
||||
syslogPacket.concat(sender);
|
||||
syslogPacket.concat(" ---> ");
|
||||
syslogPacket.concat(packet.substring(colonIndex + 2));
|
||||
} else if (telemetryPacketIndex >= 10 && packet.indexOf(":=/") == -1) {
|
||||
syslogPacket.concat("TELEMETRY / ");
|
||||
syslogPacket.concat(sender);
|
||||
syslogPacket.concat(" ---> ");
|
||||
syslogPacket.concat(packet.substring(telemetryPacketIndex + 3));
|
||||
} else {
|
||||
syslogPacket.concat(packet);
|
||||
}
|
||||
syslogPacket.concat(signalData);
|
||||
if (nextChar == '!' || nextChar == '=' || nextChar == '@') {
|
||||
syslogPacket.concat(" / ");
|
||||
syslogPacket.concat(GPS_Utils::getDistanceAndComment(packet));
|
||||
syslogPacket.concat(" / -");
|
||||
}
|
||||
break;
|
||||
case 2: // APRSIS TX
|
||||
syslogPacket.concat("APRSIS TX / ");
|
||||
if (nextChar == '>') {
|
||||
syslogPacket.concat("StartUp_Status / ");
|
||||
syslogPacket.concat(packet.substring(colonIndex + 2));
|
||||
} else if (nextChar == ':') {
|
||||
syslogPacket.concat("QUERY / ");
|
||||
syslogPacket.concat(packet);
|
||||
} else {
|
||||
syslogPacket.concat("BEACON / ");
|
||||
syslogPacket.concat(packet);
|
||||
}
|
||||
case 2: // STATUS
|
||||
syslogPacket.concat("STATUS / ");
|
||||
syslogPacket.concat(aprsPacket.sender);
|
||||
syslogPacket.concat(" ---> ");
|
||||
syslogPacket.concat(aprsPacket.payload);
|
||||
break;
|
||||
case 3: // TX
|
||||
syslogPacket.concat("TX / ");
|
||||
if (packet.indexOf("RFONLY") > 10) {
|
||||
syslogPacket.concat("RFONLY / ");
|
||||
syslogPacket.concat(packet);
|
||||
} else if (nextChar == ':') {
|
||||
syslogPacket.concat("MESSAGE / ");
|
||||
syslogPacket.concat(sender);
|
||||
syslogPacket.concat(" ---> ");
|
||||
syslogPacket.concat(packet.substring(colonIndex + 2));
|
||||
} else {
|
||||
syslogPacket.concat(packet);
|
||||
}
|
||||
case 4: // MIC-E
|
||||
syslogPacket.concat("MIC-E / ");
|
||||
syslogPacket.concat(aprsPacket.sender);
|
||||
syslogPacket.concat(" ---> ");
|
||||
syslogPacket.concat(packet.indexOf(":`") != -1 ? "`" : "'"); // restore the actual indicator received
|
||||
break;
|
||||
default:
|
||||
syslogPacket = "<165>1 - ERROR LoRa - - - ERROR / Error in Syslog Packet"; //RFC5424 The Syslog Protocol
|
||||
case 5: // OBJECT
|
||||
syslogPacket.concat("OBJECT / ");
|
||||
syslogPacket.concat(aprsPacket.sender);
|
||||
syslogPacket.concat(" ---> ");
|
||||
syslogPacket.concat(aprsPacket.payload);
|
||||
break;
|
||||
case 3: // TELEMETRY
|
||||
syslogPacket.concat("TELEMETRY / ");
|
||||
syslogPacket.concat(aprsPacket.sender);
|
||||
syslogPacket.concat(" ---> ");
|
||||
syslogPacket.concat(aprsPacket.payload);
|
||||
break;
|
||||
default: // type == 6, unrecognized -- payload is the full, uncut packet
|
||||
syslogPacket.concat(aprsPacket.payload);
|
||||
break;
|
||||
}
|
||||
udpClient.beginPacket(Config.syslog.server.c_str(), Config.syslog.port);
|
||||
udpClient.write((const uint8_t*)syslogPacket.c_str(), syslogPacket.length());
|
||||
udpClient.endPacket();
|
||||
syslogPacket.concat(formatSignalData(rssi, snr, freqError));
|
||||
if (aprsPacket.type == 0 || aprsPacket.type == 4) { // GPS or Mic-E -- both already carry
|
||||
syslogPacket.concat(" / "); // clean lat/lon + comment in aprsPacket
|
||||
syslogPacket.concat(GPS_Utils::buildDistanceAndComment(aprsPacket.latitude, aprsPacket.longitude, aprsPacket.payload));
|
||||
}
|
||||
sendSyslogPacket(syslogPacket);
|
||||
}
|
||||
}
|
||||
|
||||
void logAPRSISTx(const String& packet) {
|
||||
if (Config.syslog.active && networkManager->isConnected()) {
|
||||
String syslogPacket = createSyslogStart();
|
||||
syslogPacket.concat("APRSIS TX / ");
|
||||
|
||||
APRSPacket aprsPacket = APRSPacketLib::processReceivedPacket(packet, 0, 0, 0);
|
||||
if (aprsPacket.type == 2) { // STATUS
|
||||
syslogPacket.concat("StartUp_Status / ");
|
||||
syslogPacket.concat(aprsPacket.payload);
|
||||
} else if (aprsPacket.type == 1) { // MESSAGE
|
||||
syslogPacket.concat("QUERY / ");
|
||||
syslogPacket.concat(packet);
|
||||
} else {
|
||||
syslogPacket.concat("BEACON / ");
|
||||
syslogPacket.concat(packet);
|
||||
}
|
||||
sendSyslogPacket(syslogPacket);
|
||||
}
|
||||
}
|
||||
|
||||
void logLoRaTx(const String& packet) {
|
||||
if (Config.syslog.active && networkManager->isConnected()) {
|
||||
String syslogPacket = createSyslogStart();
|
||||
syslogPacket.concat("TX / ");
|
||||
|
||||
APRSPacket aprsPacket = APRSPacketLib::processReceivedPacket(packet, 0, 0, 0);
|
||||
if (aprsPacket.path.indexOf("RFONLY") != -1) {
|
||||
syslogPacket.concat("RFONLY / ");
|
||||
syslogPacket.concat(packet);
|
||||
} else if (aprsPacket.type == 1) {
|
||||
syslogPacket.concat("MESSAGE / ");
|
||||
syslogPacket.concat(aprsPacket.sender);
|
||||
syslogPacket.concat(" ---> ");
|
||||
syslogPacket.concat(aprsPacket.addressee);
|
||||
syslogPacket.concat(":");
|
||||
syslogPacket.concat(aprsPacket.payload);
|
||||
} else {
|
||||
syslogPacket.concat(packet);
|
||||
}
|
||||
sendSyslogPacket(syslogPacket);
|
||||
}
|
||||
}
|
||||
|
||||
void logCRCError(const String& packet, const int rssi, const float snr, const int freqError) {
|
||||
if (Config.syslog.active && networkManager->isConnected()) {
|
||||
String syslogPacket = createSyslogStart();
|
||||
syslogPacket.concat("CRC / CRC-ERROR / ");
|
||||
syslogPacket.concat(packet);
|
||||
syslogPacket.concat(formatSignalData(rssi, snr, freqError));
|
||||
sendSyslogPacket(syslogPacket);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
-1
@@ -18,6 +18,7 @@
|
||||
|
||||
#include <WiFi.h>
|
||||
#include "ESPmDNS.h"
|
||||
#include "network_manager.h"
|
||||
#include "configuration.h"
|
||||
#include "station_utils.h"
|
||||
#include "kiss_protocol.h"
|
||||
@@ -28,6 +29,7 @@
|
||||
|
||||
|
||||
extern Configuration Config;
|
||||
extern NetworkManager *networkManager;
|
||||
extern WiFiClient aprsIsClient;
|
||||
extern bool passcodeValid;
|
||||
|
||||
@@ -47,7 +49,7 @@ String inputSerialBuffer = "";
|
||||
namespace TNC_Utils {
|
||||
|
||||
void setup() {
|
||||
if (Config.tnc.enableServer && Config.digi.ecoMode == 0) {
|
||||
if (Config.tnc.enableServer && Config.digi.ecoMode == 0 && networkManager->hasActiveInterface()) { // no WiFi STA/AP or Ethernet started = no TCP/IP stack
|
||||
tncServer.stop();
|
||||
tncServer.begin();
|
||||
String host = "igate-" + Config.callsign;
|
||||
|
||||
+68
-40
@@ -25,6 +25,7 @@
|
||||
#include "battery_utils.h"
|
||||
#include "aprs_is_utils.h"
|
||||
#include "board_pinout.h"
|
||||
#include "power_utils.h"
|
||||
#include "syslog_utils.h"
|
||||
#include "A7670_utils.h"
|
||||
#include "lora_utils.h"
|
||||
@@ -89,7 +90,7 @@ namespace Utils {
|
||||
|
||||
if (sendOverAPRSIS) {
|
||||
APRS_IS_Utils::upload(statusPacket);
|
||||
SYSLOG_Utils::log(2, statusPacket, 0, 0.0, 0); // APRSIS TX
|
||||
SYSLOG_Utils::logAPRSISTx(statusPacket);
|
||||
} else {
|
||||
STATION_Utils::addToOutputPacketBuffer(statusPacket, true); // treated also as beacon on Tx Freq
|
||||
}
|
||||
@@ -168,6 +169,9 @@ namespace Utils {
|
||||
#endif
|
||||
|
||||
if (beaconUpdate) {
|
||||
#ifdef SOLAR_CHARGE_PIN
|
||||
POWER_Utils::checkSolarCharge(); // solar boards: enable/disable charging by board temperature
|
||||
#endif
|
||||
if (!Config.display.alwaysOn && Config.display.timeout != 0) displayToggle(true);
|
||||
|
||||
TELEMETRY_Utils::checkEUPInterval();
|
||||
@@ -288,7 +292,7 @@ namespace Utils {
|
||||
#else
|
||||
APRS_IS_Utils::upload(beaconPacket);
|
||||
#endif
|
||||
if (Config.syslog.logBeaconOverTCPIP) SYSLOG_Utils::log(1, "tcp" + beaconPacket, 0, 0.0, 0); // APRSIS TX
|
||||
if (Config.syslog.logBeaconOverTCPIP) SYSLOG_Utils::logAPRSISTx(beaconPacket);
|
||||
}
|
||||
|
||||
if (Config.beacon.sendViaRF || backupDigiMode) {
|
||||
@@ -325,21 +329,65 @@ namespace Utils {
|
||||
}
|
||||
}
|
||||
|
||||
void typeOfPacket(const String& packet, const uint8_t packetType) {
|
||||
void updateLoRaPacketDisplayInfo(APRSPacket& aprsPacket, const uint8_t packetType) {
|
||||
switch (packetType) {
|
||||
case 0: // LoRa-APRS
|
||||
fifthLine = "LoRa Rx ----> APRS-IS";
|
||||
break;
|
||||
case 1: // APRS-LoRa
|
||||
fifthLine = "APRS-IS ----> LoRa Tx";
|
||||
break;
|
||||
case 2: // Digipeater
|
||||
case 1: // Digipeater
|
||||
fifthLine = "LoRa Rx ----> LoRa Tx";
|
||||
break;
|
||||
}
|
||||
|
||||
int firstColonIndex = packet.indexOf(":");
|
||||
char nextChar = packet[firstColonIndex + 1];
|
||||
String sender = aprsPacket.sender;
|
||||
for (int i = sender.length(); i < 9; i++) {
|
||||
sender += " ";
|
||||
}
|
||||
sixthLine = sender;
|
||||
|
||||
switch (aprsPacket.type) {
|
||||
case 1: // MESSAGE
|
||||
sixthLine += "> MESSAGE";
|
||||
break;
|
||||
case 2: // STATUS
|
||||
sixthLine += "> NEW STATUS";
|
||||
break;
|
||||
case 0: // GPS
|
||||
sixthLine += "> GPS BEACON";
|
||||
if (!Config.syslog.active) GPS_Utils::buildDistanceAndComment(aprsPacket.latitude, aprsPacket.longitude, aprsPacket.payload);
|
||||
seventhLine = "RSSI:";
|
||||
seventhLine += String(rssi);
|
||||
seventhLine += "dBm";
|
||||
seventhLine += (rssi <= -100) ? " " : " ";
|
||||
if (distance.indexOf(".") == 1) seventhLine += " ";
|
||||
seventhLine += "D:";
|
||||
seventhLine += distance;
|
||||
seventhLine += "km";
|
||||
break;
|
||||
case 3: // TELEMETRY
|
||||
sixthLine += "> TELEMETRY";
|
||||
break;
|
||||
case 4: // MIC-E
|
||||
sixthLine += "> MIC-E";
|
||||
break;
|
||||
case 5: // OBJECT
|
||||
sixthLine += "> OBJECT";
|
||||
break;
|
||||
default: // unrecognized
|
||||
sixthLine += "> ?????????";
|
||||
break;
|
||||
}
|
||||
if (aprsPacket.type != 0) { // Common assignment for non-GPS cases
|
||||
seventhLine = "RSSI:";
|
||||
seventhLine += String(rssi);
|
||||
seventhLine += "dBm SNR: ";
|
||||
seventhLine += String(snr);
|
||||
seventhLine += "dBm";
|
||||
}
|
||||
}
|
||||
|
||||
void updateAPRSISPacketDisplayInfo(const String& packet) {
|
||||
fifthLine = "APRS-IS ----> LoRa Tx";
|
||||
|
||||
String sender = packet.substring(0,packet.indexOf(">"));
|
||||
for (int i = sender.length(); i < 9; i++) {
|
||||
@@ -347,37 +395,12 @@ namespace Utils {
|
||||
}
|
||||
sixthLine = sender;
|
||||
|
||||
if (nextChar == ':') {
|
||||
if (packet.indexOf("::") > 0) {
|
||||
sixthLine += "> MESSAGE";
|
||||
} else if (nextChar == '>') {
|
||||
sixthLine += "> NEW STATUS";
|
||||
} else if (nextChar == '!' || nextChar == '=' || nextChar == '@') {
|
||||
sixthLine += "> GPS BEACON";
|
||||
if (!Config.syslog.active) GPS_Utils::getDistanceAndComment(packet); // to be checked!!!
|
||||
seventhLine = "RSSI:";
|
||||
seventhLine += String(rssi);
|
||||
seventhLine += "dBm";
|
||||
seventhLine += (rssi <= -100) ? " " : " ";
|
||||
if (distance.indexOf(".") == 1) seventhLine += " ";
|
||||
seventhLine += "D:";
|
||||
seventhLine += distance;
|
||||
seventhLine += "km";
|
||||
} else if (nextChar == '`' || nextChar == '\'') {
|
||||
sixthLine += "> MIC-E";
|
||||
} else if (nextChar == ';') {
|
||||
sixthLine += "> OBJECT";
|
||||
} else if (packet.indexOf(":T#") >= 10 && packet.indexOf(":=/") == -1) {
|
||||
sixthLine += "> TELEMETRY";
|
||||
} else {
|
||||
sixthLine += "> ??????????";
|
||||
}
|
||||
if (nextChar != '!' && nextChar != '=' && nextChar != '@') { // Common assignment for non-GPS cases
|
||||
seventhLine = "RSSI:";
|
||||
seventhLine += String(rssi);
|
||||
seventhLine += "dBm SNR: ";
|
||||
seventhLine += String(snr);
|
||||
seventhLine += "dBm";
|
||||
sixthLine += "> OBJECT";
|
||||
}
|
||||
seventhLine = "";
|
||||
}
|
||||
|
||||
void print(const String& text) {
|
||||
@@ -418,11 +441,16 @@ namespace Utils {
|
||||
displayToggle(false);
|
||||
}
|
||||
#ifdef VEXT_CTRL_PIN
|
||||
#ifndef HELTEC_WSL_V3
|
||||
digitalWrite(VEXT_CTRL_PIN, LOW);
|
||||
#endif
|
||||
digitalWrite(VEXT_CTRL_PIN, !VEXT_CTRL_ON_STATE); // VEXT off before deep sleep (always, regardless of ecoMode)
|
||||
#endif
|
||||
LoRa_Utils::sleepRadio();
|
||||
#ifdef RADIO_VCC_PIN
|
||||
digitalWrite(RADIO_VCC_PIN, LOW); // cut LoRa module power (QRP Labs LightGateway) so it draws nothing while sleeping
|
||||
#endif
|
||||
#ifdef SOLAR_CHARGE_PIN
|
||||
gpio_hold_en((gpio_num_t)SOLAR_CHARGE_PIN); // keep charge on/off decided by temperature while sleeping
|
||||
gpio_deep_sleep_hold_en();
|
||||
#endif
|
||||
transmitFlag = true;
|
||||
delay(100);
|
||||
esp_deep_sleep_start();
|
||||
|
||||
+3
-1
@@ -17,6 +17,7 @@
|
||||
*/
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include "network_manager.h"
|
||||
#include "configuration.h"
|
||||
#include "ota_utils.h"
|
||||
#include "web_utils.h"
|
||||
@@ -26,6 +27,7 @@
|
||||
|
||||
|
||||
extern Configuration Config;
|
||||
extern NetworkManager *networkManager;
|
||||
extern uint32_t lastBeaconTx;
|
||||
extern std::vector<ReceivedPacket> receivedPackets;
|
||||
|
||||
@@ -435,7 +437,7 @@ namespace WEB_Utils {
|
||||
}
|
||||
|
||||
void setup() {
|
||||
if (Config.digi.ecoMode == 0) {
|
||||
if (Config.digi.ecoMode == 0 && networkManager->hasActiveInterface()) { // no WiFi STA/AP or Ethernet started = no TCP/IP stack
|
||||
server.on("/", HTTP_GET, handleHome);
|
||||
server.on("/status", HTTP_GET, handleStatus);
|
||||
server.on("/received-packets.json", HTTP_GET, handleReceivedPackets);
|
||||
|
||||
@@ -99,6 +99,9 @@ namespace WIFI_Utils {
|
||||
if (Config.wifiAutoAP.enabled) {
|
||||
Serial.println("Starting AP fallback...");
|
||||
startAutoAP();
|
||||
} else {
|
||||
Serial.println("AutoAP disabled: no network, WebUI/TNC server not started (reconfigure over USB)");
|
||||
displayShow("", " No WiFi / No AP", " WebUI disabled", " loading ...", 2000);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
+27
-7
@@ -17,11 +17,11 @@
|
||||
*/
|
||||
|
||||
#include <TinyGPS++.h>
|
||||
#ifdef LIGHTGATEWAY_PLUS_1_0
|
||||
#include "Adafruit_SHTC3.h"
|
||||
#endif
|
||||
#include "configuration.h"
|
||||
#include "board_pinout.h"
|
||||
#ifdef HAS_SHTC3
|
||||
#include "Adafruit_SHTC3.h"
|
||||
#endif
|
||||
#include "wx_utils.h"
|
||||
#include "display.h"
|
||||
|
||||
@@ -51,8 +51,9 @@ Adafruit_BMP280 bmp280;
|
||||
Adafruit_BME680 bme680;
|
||||
Adafruit_Si7021 si7021 = Adafruit_Si7021();
|
||||
#endif
|
||||
#ifdef LIGHTGATEWAY_PLUS_1_0
|
||||
#ifdef HAS_SHTC3
|
||||
Adafruit_SHTC3 shtc3 = Adafruit_SHTC3();
|
||||
bool boardTempSensorFound = false; // onboard SHTC3 (solar charge protection), independent of WX telemetry
|
||||
#endif
|
||||
|
||||
|
||||
@@ -66,7 +67,7 @@ namespace WX_Utils {
|
||||
err = SENSOR_I2C_BUS.endTransmission();
|
||||
#else
|
||||
Wire.beginTransmission(addr);
|
||||
#ifdef LIGHTGATEWAY_PLUS_1_0
|
||||
#ifdef HAS_SHTC3
|
||||
Wire.write(0x35);
|
||||
Wire.write(0x17);
|
||||
#endif
|
||||
@@ -90,6 +91,15 @@ namespace WX_Utils {
|
||||
}
|
||||
|
||||
void setup() {
|
||||
#ifdef HAS_SHTC3
|
||||
Wire.beginTransmission(0x70); // SHTC3 wakeup (0x3517): it sleeps between reads and ignores its address while asleep (e.g. after deep sleep)
|
||||
Wire.write(0x35);
|
||||
Wire.write(0x17);
|
||||
Wire.endTransmission();
|
||||
delay(1);
|
||||
boardTempSensorFound = shtc3.begin();
|
||||
Serial.println(boardTempSensorFound ? "init : SHTC3 (onboard) ... done!" : "SHTC3 (onboard) not found!");
|
||||
#endif
|
||||
if (Config.wxsensor.active) {
|
||||
getWxModuleAddres();
|
||||
if (wxModuleAddress != 0x00) {
|
||||
@@ -133,7 +143,7 @@ namespace WX_Utils {
|
||||
wxModuleFound = true;
|
||||
}
|
||||
}
|
||||
#ifdef LIGHTGATEWAY_PLUS_1_0
|
||||
#ifdef HAS_SHTC3
|
||||
else if (wxModuleAddress == 0x70) {
|
||||
if (shtc3.begin()) {
|
||||
Serial.println("SHTC3 sensor found");
|
||||
@@ -238,6 +248,16 @@ namespace WX_Utils {
|
||||
#endif
|
||||
}
|
||||
|
||||
float readBoardTemperature() { // onboard SHTC3 (no WX correction applied), NAN if not available
|
||||
#ifdef HAS_SHTC3
|
||||
if (boardTempSensorFound) {
|
||||
sensors_event_t humidity, temp;
|
||||
if (shtc3.getEvent(&humidity, &temp)) return temp.temperature;
|
||||
}
|
||||
#endif
|
||||
return NAN;
|
||||
}
|
||||
|
||||
String readDataSensor() {
|
||||
switch (wxModuleType) {
|
||||
case 1: // BME280
|
||||
@@ -271,7 +291,7 @@ namespace WX_Utils {
|
||||
break;
|
||||
case 5: // SHTC3
|
||||
{
|
||||
#ifdef LIGHTGATEWAY_PLUS_1_0
|
||||
#ifdef HAS_SHTC3
|
||||
sensors_event_t humidity, temp;
|
||||
shtc3.getEvent(&humidity, &temp);
|
||||
newTemp = temp.temperature;
|
||||
|
||||
File renamed without changes.
+2
-2
@@ -1,4 +1,4 @@
|
||||
[env:esp32c3_DIY_1W_LoRa_915]
|
||||
[env:ESP32C3_DIY_1W_LoRa_915_E22_900M30S]
|
||||
board = esp32-c3-devkitm-1
|
||||
board_build.mcu = esp32c3
|
||||
build_flags =
|
||||
@@ -7,7 +7,7 @@ build_flags =
|
||||
-D RADIOLIB_EXCLUDE_LR11X0=1
|
||||
-D RADIOLIB_EXCLUDE_SX127X=1
|
||||
-D RADIOLIB_EXCLUDE_SX128X=1
|
||||
-D ESP32C3_DIY_1W_LoRa_915
|
||||
-D ESP32C3_DIY_1W_LoRa_915_E22_900M30S
|
||||
lib_deps =
|
||||
${common.lib_deps}
|
||||
${common.display_libs}
|
||||
File renamed without changes.
+2
-2
@@ -1,4 +1,4 @@
|
||||
[env:ESP32_C3_OctopusLab_LoRa]
|
||||
[env:ESP32C3_DIY_1W_LoRa_E22_400M30S]
|
||||
board = esp32-c3-devkitm-1
|
||||
board_build.mcu = esp32c3
|
||||
build_flags =
|
||||
@@ -7,7 +7,7 @@ build_flags =
|
||||
-D RADIOLIB_EXCLUDE_LR11X0=1
|
||||
-D RADIOLIB_EXCLUDE_SX127X=1
|
||||
-D RADIOLIB_EXCLUDE_SX128X=1
|
||||
-D ESP32_C3_OctopusLab_LoRa
|
||||
-D ESP32C3_DIY_1W_LoRa_E22_400M30S
|
||||
lib_deps =
|
||||
${common.lib_deps}
|
||||
${common.display_libs}
|
||||
File renamed without changes.
+2
-2
@@ -1,4 +1,4 @@
|
||||
[env:esp32c3_DIY_1W_LoRa]
|
||||
[env:ESP32C3_OctopusLab_LoRa]
|
||||
board = esp32-c3-devkitm-1
|
||||
board_build.mcu = esp32c3
|
||||
build_flags =
|
||||
@@ -7,7 +7,7 @@ build_flags =
|
||||
-D RADIOLIB_EXCLUDE_LR11X0=1
|
||||
-D RADIOLIB_EXCLUDE_SX127X=1
|
||||
-D RADIOLIB_EXCLUDE_SX128X=1
|
||||
-D ESP32C3_DIY_1W_LoRa
|
||||
-D ESP32C3_OctopusLab_LoRa
|
||||
lib_deps =
|
||||
${common.lib_deps}
|
||||
${common.display_libs}
|
||||
@@ -0,0 +1,46 @@
|
||||
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU
|
||||
*
|
||||
* This file is part of LoRa APRS iGate.
|
||||
*
|
||||
* LoRa APRS iGate is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* LoRa APRS iGate is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef BOARD_PINOUT_H_
|
||||
#define BOARD_PINOUT_H_
|
||||
|
||||
#define HAS_SX1278
|
||||
#define RADIO_SCLK_PIN 4
|
||||
#define RADIO_MISO_PIN 5
|
||||
#define RADIO_MOSI_PIN 6
|
||||
#define RADIO_CS_PIN 7
|
||||
#define RADIO_RST_PIN 3
|
||||
#define RADIO_BUSY_PIN 2
|
||||
#define RADIO_WAKEUP_PIN RADIO_BUSY_PIN
|
||||
#define GPIO_WAKEUP_PIN GPIO_NUM_2
|
||||
|
||||
// Display
|
||||
#define HAS_DISPLAY
|
||||
|
||||
#undef OLED_SDA
|
||||
#undef OLED_SCL
|
||||
#undef OLED_RST
|
||||
|
||||
#define OLED_SDA 8
|
||||
#define OLED_SCL 9
|
||||
#define OLED_RST 10
|
||||
|
||||
// I2C
|
||||
#define USE_WIRE_WITH_OLED_PINS
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,13 @@
|
||||
[env:ESP32C3_SuperMini_DIY_LoRa_SX1278]
|
||||
board = esp32-c3-devkitm-1
|
||||
board_build.mcu = esp32c3
|
||||
build_flags =
|
||||
${common.build_flags}
|
||||
${common.usb_flags}
|
||||
-D RADIOLIB_EXCLUDE_LR11X0=1
|
||||
-D RADIOLIB_EXCLUDE_SX126X=1
|
||||
-D RADIOLIB_EXCLUDE_SX128X=1
|
||||
-D ESP32C3_SuperMini_DIY_LoRa_SX1278
|
||||
lib_deps =
|
||||
${common.lib_deps}
|
||||
${common.display_libs}
|
||||
File renamed without changes.
+2
-2
@@ -1,11 +1,11 @@
|
||||
[env:ESP32_9M2IBR_1W_LoRa_GPS]
|
||||
[env:ESP32_9M2IBR_1W_LoRa_GPS_E22_400M30S]
|
||||
board = esp32dev
|
||||
build_flags =
|
||||
${common.build_flags}
|
||||
-D RADIOLIB_EXCLUDE_LR11X0=1
|
||||
-D RADIOLIB_EXCLUDE_SX127X=1
|
||||
-D RADIOLIB_EXCLUDE_SX128X=1
|
||||
-D ESP32_9M2IBR_1W_LoRa_GPS
|
||||
-D ESP32_9M2IBR_1W_LoRa_GPS_E22_400M30S
|
||||
lib_deps =
|
||||
${common.lib_deps}
|
||||
${common.display_libs}
|
||||
File renamed without changes.
+2
-2
@@ -1,11 +1,11 @@
|
||||
[env:ESP32_DIY_1W_LoRa]
|
||||
[env:ESP32_DIY_1W_LoRa_915_E22_900M30S]
|
||||
board = esp32dev
|
||||
build_flags =
|
||||
${common.build_flags}
|
||||
-D RADIOLIB_EXCLUDE_LR11X0=1
|
||||
-D RADIOLIB_EXCLUDE_SX127X=1
|
||||
-D RADIOLIB_EXCLUDE_SX128X=1
|
||||
-D ESP32_DIY_1W_LoRa
|
||||
-D ESP32_DIY_1W_LoRa_915_E22_900M30S
|
||||
lib_deps =
|
||||
${common.lib_deps}
|
||||
${common.display_libs}
|
||||
File renamed without changes.
+2
-2
@@ -1,11 +1,11 @@
|
||||
[env:ESP32_DIY_1W_LoRa_LLCC68]
|
||||
[env:ESP32_DIY_1W_LoRa_E220_400M30S]
|
||||
board = esp32dev
|
||||
build_flags =
|
||||
${common.build_flags}
|
||||
-D RADIOLIB_EXCLUDE_LR11X0=1
|
||||
-D RADIOLIB_EXCLUDE_SX127X=1
|
||||
-D RADIOLIB_EXCLUDE_SX128X=1
|
||||
-D ESP32_DIY_1W_LoRa_LLCC68
|
||||
-D ESP32_DIY_1W_LoRa_E220_400M30S
|
||||
lib_deps =
|
||||
${common.lib_deps}
|
||||
${common.display_libs}
|
||||
File renamed without changes.
+2
-2
@@ -1,11 +1,11 @@
|
||||
[env:ESP32_DIY_1W_LoRa_Mesh_V1_2]
|
||||
[env:ESP32_DIY_1W_LoRa_E22_400M30S]
|
||||
board = esp32dev
|
||||
build_flags =
|
||||
${common.build_flags}
|
||||
-D RADIOLIB_EXCLUDE_LR11X0=1
|
||||
-D RADIOLIB_EXCLUDE_SX127X=1
|
||||
-D RADIOLIB_EXCLUDE_SX128X=1
|
||||
-D ESP32_DIY_1W_LoRa_Mesh_V1_2
|
||||
-D ESP32_DIY_1W_LoRa_E22_400M30S
|
||||
lib_deps =
|
||||
${common.lib_deps}
|
||||
${common.display_libs}
|
||||
@@ -0,0 +1,52 @@
|
||||
/* Copyright (C) 2026 Ricardo Guzman - CA2RXU
|
||||
*
|
||||
* This file is part of LoRa APRS iGate.
|
||||
*
|
||||
* LoRa APRS iGate is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* LoRa APRS iGate is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef BOARD_PINOUT_H_
|
||||
#define BOARD_PINOUT_H_
|
||||
|
||||
// LoRa Radio
|
||||
#define HAS_SX1278
|
||||
#define RADIO_SCLK_PIN 5
|
||||
#define RADIO_MISO_PIN 19
|
||||
#define RADIO_MOSI_PIN 27
|
||||
#define RADIO_CS_PIN 18
|
||||
#define RADIO_RST_PIN 14
|
||||
#define RADIO_BUSY_PIN 26
|
||||
#define RADIO_WAKEUP_PIN RADIO_BUSY_PIN
|
||||
#define GPIO_WAKEUP_PIN GPIO_SEL_26
|
||||
#define RADIO_RXEN 32 // Ebyte E32 400M30S (SX1278 + PA) antenna switch
|
||||
#define RADIO_TXEN 25
|
||||
|
||||
// I2C
|
||||
#define USE_WIRE_WITH_OLED_PINS
|
||||
|
||||
// Display
|
||||
#define HAS_DISPLAY
|
||||
|
||||
#undef OLED_SDA
|
||||
#undef OLED_SCL
|
||||
#undef OLED_RST
|
||||
|
||||
#define OLED_SDA 21
|
||||
#define OLED_SCL 22
|
||||
#define OLED_RST -1 // Reset pin # (or -1 if sharing Arduino reset pin)
|
||||
|
||||
// Aditional Config
|
||||
#define INTERNAL_LED_PIN 2
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
[env:ESP32_DIY_1W_LoRa_E32_400M30S]
|
||||
board = esp32dev
|
||||
build_flags =
|
||||
${common.build_flags}
|
||||
-D RADIOLIB_EXCLUDE_LR11X0=1
|
||||
-D RADIOLIB_EXCLUDE_SX126X=1
|
||||
-D RADIOLIB_EXCLUDE_SX128X=1
|
||||
-D ESP32_DIY_1W_LoRa_E32_400M30S
|
||||
lib_deps =
|
||||
${common.lib_deps}
|
||||
${common.display_libs}
|
||||
File renamed without changes.
+2
-2
@@ -1,11 +1,11 @@
|
||||
[env:ESP32_DIY_1W_LoRa_915]
|
||||
[env:ESP32_DIY_1W_LoRa_Mesh_V1_2_E22_400M30S]
|
||||
board = esp32dev
|
||||
build_flags =
|
||||
${common.build_flags}
|
||||
-D RADIOLIB_EXCLUDE_LR11X0=1
|
||||
-D RADIOLIB_EXCLUDE_SX127X=1
|
||||
-D RADIOLIB_EXCLUDE_SX128X=1
|
||||
-D ESP32_DIY_1W_LoRa_915
|
||||
-D ESP32_DIY_1W_LoRa_Mesh_V1_2_E22_400M30S
|
||||
lib_deps =
|
||||
${common.lib_deps}
|
||||
${common.display_libs}
|
||||
File renamed without changes.
+2
-2
@@ -1,11 +1,11 @@
|
||||
[env:ESP32_DIY_LoRa_915]
|
||||
[env:ESP32_DIY_LoRa_915_SX1276]
|
||||
board = esp32dev
|
||||
build_flags =
|
||||
${common.build_flags}
|
||||
-D RADIOLIB_EXCLUDE_LR11X0=1
|
||||
-D RADIOLIB_EXCLUDE_SX126X=1
|
||||
-D RADIOLIB_EXCLUDE_SX128X=1
|
||||
-D ESP32_DIY_LoRa_915
|
||||
-D ESP32_DIY_LoRa_915_SX1276
|
||||
lib_deps =
|
||||
${common.lib_deps}
|
||||
${common.display_libs}
|
||||
File renamed without changes.
+2
-2
@@ -1,11 +1,11 @@
|
||||
[env:ESP32_DIY_LoRa_A7670]
|
||||
[env:ESP32_DIY_LoRa_A7670_915_SX1276]
|
||||
board = esp32dev
|
||||
build_flags =
|
||||
${common.build_flags}
|
||||
-D RADIOLIB_EXCLUDE_LR11X0=1
|
||||
-D RADIOLIB_EXCLUDE_SX126X=1
|
||||
-D RADIOLIB_EXCLUDE_SX128X=1
|
||||
-D ESP32_DIY_LoRa_A7670
|
||||
-D ESP32_DIY_LoRa_A7670_915_SX1276
|
||||
lib_deps =
|
||||
${common.lib_deps}
|
||||
${common.display_libs}
|
||||
File renamed without changes.
+2
-2
@@ -1,11 +1,11 @@
|
||||
[env:ESP32_DIY_LoRa_A7670_915]
|
||||
[env:ESP32_DIY_LoRa_A7670_SX1278]
|
||||
board = esp32dev
|
||||
build_flags =
|
||||
${common.build_flags}
|
||||
-D RADIOLIB_EXCLUDE_LR11X0=1
|
||||
-D RADIOLIB_EXCLUDE_SX126X=1
|
||||
-D RADIOLIB_EXCLUDE_SX128X=1
|
||||
-D ESP32_DIY_LoRa_A7670_915
|
||||
-D ESP32_DIY_LoRa_A7670_SX1278
|
||||
lib_deps =
|
||||
${common.lib_deps}
|
||||
${common.display_libs}
|
||||
File renamed without changes.
+2
-2
@@ -1,11 +1,11 @@
|
||||
[env:ESP32_DIY_LoRa]
|
||||
[env:ESP32_DIY_LoRa_SX1278]
|
||||
board = esp32dev
|
||||
build_flags =
|
||||
${common.build_flags}
|
||||
-D RADIOLIB_EXCLUDE_LR11X0=1
|
||||
-D RADIOLIB_EXCLUDE_SX126X=1
|
||||
-D RADIOLIB_EXCLUDE_SX128X=1
|
||||
-D ESP32_DIY_LoRa
|
||||
-D ESP32_DIY_LoRa_SX1278
|
||||
lib_deps =
|
||||
${common.lib_deps}
|
||||
${common.display_libs}
|
||||
@@ -54,5 +54,12 @@
|
||||
#define INTERNAL_LED_PIN 16
|
||||
#define BATTERY_PIN 1
|
||||
#define BUTTON_PIN 0
|
||||
#define SOLAR_CHARGE_PIN 48 // HIGH = solar charging disabled / LOW = enabled
|
||||
#define SOLAR_CHARGE_ENABLED_STATE LOW
|
||||
#define SOLAR_CHARGE_DISABLE_TEMP -4.0 // °C: below this, stop charging (Li-Ion must not charge below 0°C)
|
||||
#define SOLAR_CHARGE_ENABLE_TEMP -3.0 // °C: above this, charge again (1°C hysteresis)
|
||||
|
||||
// Onboard Sensors
|
||||
#define HAS_SHTC3 // temperature/humidity (I2C 0x70), used for solar charge protection
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,39 @@
|
||||
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU
|
||||
*
|
||||
* This file is part of LoRa APRS iGate.
|
||||
*
|
||||
* LoRa APRS iGate is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* LoRa APRS iGate is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef BOARD_PINOUT_H_
|
||||
#define BOARD_PINOUT_H_
|
||||
|
||||
// LoRa Radio
|
||||
#define HAS_SX1262
|
||||
#define HAS_TCXO
|
||||
#define RADIO_SCLK_PIN 5
|
||||
#define RADIO_MISO_PIN 3
|
||||
#define RADIO_MOSI_PIN 6
|
||||
#define RADIO_CS_PIN 7
|
||||
#define RADIO_RST_PIN 8
|
||||
#define RADIO_DIO1_PIN 47
|
||||
#define RADIO_BUSY_PIN 48
|
||||
#define RADIO_WAKEUP_PIN RADIO_DIO1_PIN
|
||||
#define GPIO_WAKEUP_PIN GPIO_SEL_47
|
||||
|
||||
// Aditional Config
|
||||
#define INTERNAL_LED_PIN 46
|
||||
#define BATTERY_PIN 1
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,15 @@
|
||||
[env:RAK3112]
|
||||
board = esp32-s3-devkitc-1
|
||||
board_build.mcu = esp32s3
|
||||
board_build.arduino.memory_type = qio_opi
|
||||
board_upload.flash_size = 16MB
|
||||
build_flags =
|
||||
${common.build_flags}
|
||||
${common.usb_flags}
|
||||
-D RADIOLIB_EXCLUDE_LR11X0=1
|
||||
-D RADIOLIB_EXCLUDE_SX127X=1
|
||||
-D RADIOLIB_EXCLUDE_SX128X=1
|
||||
-D BOARD_HAS_PSRAM
|
||||
-D RAK3112
|
||||
lib_deps =
|
||||
${common.lib_deps}
|
||||
@@ -0,0 +1,52 @@
|
||||
/* Copyright (C) 2025 Ricardo Guzman - CA2RXU
|
||||
*
|
||||
* This file is part of LoRa APRS iGate.
|
||||
*
|
||||
* LoRa APRS iGate is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* LoRa APRS iGate is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with LoRa APRS iGate. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef BOARD_PINOUT_H_
|
||||
#define BOARD_PINOUT_H_
|
||||
|
||||
// LoRa Radio
|
||||
#define HAS_SX1262
|
||||
#define HAS_TCXO
|
||||
#define RADIO_SCLK_PIN 5
|
||||
#define RADIO_MISO_PIN 3
|
||||
#define RADIO_MOSI_PIN 6
|
||||
#define RADIO_CS_PIN 7
|
||||
#define RADIO_RST_PIN 8
|
||||
#define RADIO_DIO1_PIN 47
|
||||
#define RADIO_BUSY_PIN 48
|
||||
#define RADIO_WAKEUP_PIN RADIO_DIO1_PIN
|
||||
#define GPIO_WAKEUP_PIN GPIO_SEL_47
|
||||
|
||||
#define RADIO_ANT_SW_PIN 4 // ANT_SW - antenna switch power enable
|
||||
#define RADIO_ANT_SW_ON_STATE HIGH
|
||||
|
||||
// Base Board Power
|
||||
#define VEXT_CTRL_PIN 14 // 3V3_EN - powers GPS on Base Board
|
||||
#define VEXT_CTRL_ON_STATE HIGH
|
||||
|
||||
// GPS
|
||||
#define HAS_GPS
|
||||
#define GPS_BAUDRATE 9600
|
||||
#define GPS_RX 43
|
||||
#define GPS_TX 44
|
||||
|
||||
// Aditional Config
|
||||
#define INTERNAL_LED_PIN 46
|
||||
#define BATTERY_PIN 1
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,15 @@
|
||||
[env:RAK3312]
|
||||
board = esp32-s3-devkitc-1
|
||||
board_build.mcu = esp32s3
|
||||
board_build.arduino.memory_type = qio_opi
|
||||
board_upload.flash_size = 16MB
|
||||
build_flags =
|
||||
${common.build_flags}
|
||||
${common.usb_flags}
|
||||
-D RADIOLIB_EXCLUDE_LR11X0=1
|
||||
-D RADIOLIB_EXCLUDE_SX127X=1
|
||||
-D RADIOLIB_EXCLUDE_SX128X=1
|
||||
-D BOARD_HAS_PSRAM
|
||||
-D RAK3312
|
||||
lib_deps =
|
||||
${common.lib_deps}
|
||||
File renamed without changes.
+2
-2
@@ -1,11 +1,11 @@
|
||||
[env:WEMOS-D1-R32-RA02]
|
||||
[env:WEMOS_D1_R32_DIY_LoRa_SX1278]
|
||||
board = wemos_d1_uno32
|
||||
build_flags =
|
||||
${common.build_flags}
|
||||
-D RADIOLIB_EXCLUDE_LR11X0=1
|
||||
-D RADIOLIB_EXCLUDE_SX126X=1
|
||||
-D RADIOLIB_EXCLUDE_SX128X=1
|
||||
-D WEMOS_D1_R32_RA02
|
||||
-D WEMOS_D1_R32_DIY_LoRa_SX1278
|
||||
lib_deps =
|
||||
${common.lib_deps}
|
||||
${common.display_libs}
|
||||
File renamed without changes.
+2
-2
@@ -1,11 +1,11 @@
|
||||
[env:WEMOS-LOLIN32-OLED-DIY]
|
||||
[env:WEMOS_LOLIN32_OLED_DIY_LoRa_SX1278]
|
||||
board = lolin32
|
||||
build_flags =
|
||||
${common.build_flags}
|
||||
-D RADIOLIB_EXCLUDE_LR11X0=1
|
||||
-D RADIOLIB_EXCLUDE_SX126X=1
|
||||
-D RADIOLIB_EXCLUDE_SX128X=1
|
||||
-D WEMOS_LOLIN32_OLED_DIY_LoRa
|
||||
-D WEMOS_LOLIN32_OLED_DIY_LoRa_SX1278
|
||||
lib_deps =
|
||||
${common.lib_deps}
|
||||
${common.display_libs}
|
||||
File renamed without changes.
+2
-2
@@ -1,11 +1,11 @@
|
||||
[env:WEMOS_S2_MINI_DIY_LoRa]
|
||||
[env:WEMOS_S2_MINI_DIY_LoRa_SX1278]
|
||||
board = lolin_s2_mini
|
||||
build_flags =
|
||||
${common.build_flags}
|
||||
-D RADIOLIB_EXCLUDE_LR11X0=1
|
||||
-D RADIOLIB_EXCLUDE_SX126X=1
|
||||
-D RADIOLIB_EXCLUDE_SX128X=1
|
||||
-D WEMOS_S2_MINI_DIY_LoRa
|
||||
-D WEMOS_S2_MINI_DIY_LoRa_SX1278
|
||||
lib_deps =
|
||||
${common.lib_deps}
|
||||
${common.display_libs}
|
||||
@@ -14,6 +14,9 @@ board_build.embed_files =
|
||||
data_embed/leaflet.css.gz
|
||||
data_embed/leaflet.js.gz
|
||||
data_embed/favicon.png.gz
|
||||
data_embed/aprs-symbols-24-0.png.gz
|
||||
data_embed/aprs-symbols-24-1.png.gz
|
||||
data_embed/aprs-symbols-24-2.png.gz
|
||||
extra_scripts =
|
||||
pre:tools/compress.py
|
||||
debug_tool = esp-prog
|
||||
|
||||
@@ -14,6 +14,9 @@ board_build.embed_files =
|
||||
data_embed/leaflet.css.gz
|
||||
data_embed/leaflet.js.gz
|
||||
data_embed/favicon.png.gz
|
||||
data_embed/aprs-symbols-24-0.png.gz
|
||||
data_embed/aprs-symbols-24-1.png.gz
|
||||
data_embed/aprs-symbols-24-2.png.gz
|
||||
extra_scripts =
|
||||
pre:tools/compress.py
|
||||
debug_tool = esp-prog
|
||||
|
||||
Reference in new issue
Block a user