/* 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 . */ /*___________________________________________________________________ ██╗ ██████╗ ██████╗ █████╗ █████╗ ██████╗ ██████╗ ███████╗ ██║ ██╔═══██╗██╔══██╗██╔══██╗ ██╔══██╗██╔══██╗██╔══██╗██╔════╝ ██║ ██║ ██║██████╔╝███████║ ███████║██████╔╝██████╔╝███████╗ ██║ ██║ ██║██╔══██╗██╔══██║ ██╔══██║██╔═══╝ ██╔══██╗╚════██║ ███████╗╚██████╔╝██║ ██║██║ ██║ ██║ ██║██║ ██║ ██║███████║ ╚══════╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝╚══════╝ ██╗ ██████╗ █████╗ ████████╗███████╗ ██║██╔════╝ ██╔══██╗╚══██╔══╝██╔════╝ ██║██║ ███╗███████║ ██║ █████╗ ██║██║ ██║██╔══██║ ██║ ██╔══╝ ██║╚██████╔╝██║ ██║ ██║ ███████╗ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚══════╝ Ricardo Guzman - CA2RXU https://github.com/richonguzman/LoRa_APRS_iGate (donations : http://paypal.me/richonguzman) ___________________________________________________________________*/ #include #include #include #include #include #include "configuration.h" #include "aprs_is_utils.h" #include "station_utils.h" #include "battery_utils.h" #include "board_pinout.h" #include "syslog_utils.h" #include "power_utils.h" #include "sleep_utils.h" #include "mqtt_utils.h" #include "lora_utils.h" #include "wifi_utils.h" #include "digi_utils.h" #include "gps_utils.h" #include "web_utils.h" #include "tnc_utils.h" #include "ntp_utils.h" #include "wx_utils.h" #include "display.h" #include "utils.h" #ifdef HAS_A7670 #include "A7670_utils.h" #endif String versionDate = "2025-10-12"; String versionNumber = "3.1.3"; Configuration Config; WiFiClient aprsIsClient; WiFiClient mqttClient; #ifdef HAS_GPS HardwareSerial gpsSerial(1); TinyGPSPlus gps; uint32_t gpsSatelliteTime = 0; bool gpsInfoToggle = false; #endif uint8_t myWiFiAPIndex = 0; int myWiFiAPSize = Config.wifiAPs.size(); WiFi_AP *currentWiFi = &Config.wifiAPs[myWiFiAPIndex]; bool isUpdatingOTA = false; uint32_t lastBatteryCheck = 0; bool backUpDigiMode = false; bool modemLoggedToAPRSIS = false; #ifdef HAS_EPAPER uint32_t lastEpaperTime = 0; extern String lastEpaperText; #endif std::vector receivedPackets; String firstLine, secondLine, thirdLine, fourthLine, fifthLine, sixthLine, seventhLine; void setup() { Serial.begin(115200); POWER_Utils::setup(); Utils::setupDisplay(); LoRa_Utils::setup(); Utils::validateFreqs(); GPS_Utils::setup(); STATION_Utils::loadBlacklistAndManagers(); Utils::startupDelay(); SLEEP_Utils::setup(); WIFI_Utils::setup(); NTP_Utils::setup(); SYSLOG_Utils::setup(); WX_Utils::setup(); WEB_Utils::setup(); TNC_Utils::setup(); MQTT_Utils::setup(); #ifdef HAS_A7670 A7670_Utils::setup(); #endif Utils::checkRebootMode(); APRS_IS_Utils::firstConnection(); SLEEP_Utils::checkSerial(); } void loop() { if (Config.digi.ecoMode == 1) { SLEEP_Utils::checkWakeUpFlag(); Utils::checkBeaconInterval(); STATION_Utils::processOutputPacketBufferUltraEcoMode(); Utils::checkSleepByLowBatteryVoltage(1); SLEEP_Utils::startSleeping(); } else { WIFI_Utils::checkAutoAPTimeout(); if (isUpdatingOTA) { ElegantOTA.loop(); return; // Don't process IGate and Digi during OTA update } #ifdef HAS_GPS if (Config.beacon.gpsActive) { if (millis() - gpsSatelliteTime > 5000) { gpsInfoToggle = !gpsInfoToggle; gpsSatelliteTime = millis(); } if (gpsInfoToggle) { thirdLine = "Satellite(s): "; String gpsData = String(gps.satellites.value()); if (gpsData.length() < 2) gpsData = "0" + gpsData; // Ensure two-digit formatting thirdLine += gpsData; } else { thirdLine = Utils::getLocalIP(); } } else { thirdLine = Utils::getLocalIP(); } #else thirdLine = Utils::getLocalIP(); #endif #ifdef HAS_A7670 if (Config.aprs_is.active && !modemLoggedToAPRSIS) A7670_Utils::APRS_IS_connect(); #else WIFI_Utils::checkWiFi(); if (Config.aprs_is.active && (WiFi.status() == WL_CONNECTED) && !aprsIsClient.connected()) APRS_IS_Utils::connect(); if (Config.mqtt.active && (WiFi.status() == WL_CONNECTED) && !mqttClient.connected()) MQTT_Utils::connect(); #endif NTP_Utils::update(); TNC_Utils::loop(); MQTT_Utils::loop(); Utils::checkDisplayInterval(); Utils::checkBeaconInterval(); APRS_IS_Utils::checkStatus(); // Need that to update display, maybe split this and send APRSIS status to display func? String packet = ""; if (Config.loramodule.rxActive) { packet = LoRa_Utils::receivePacket(); // We need to fetch LoRa packet above APRSIS and Digi } if (packet != "") { if (Config.aprs_is.active) { // If APRSIS enabled APRS_IS_Utils::processLoRaPacket(packet); // Send received packet to APRSIS } if (Config.loramodule.txActive && (Config.digi.mode == 2 || Config.digi.mode == 3 || backUpDigiMode)) { // If Digi enabled STATION_Utils::clean25SegBuffer(); DIGI_Utils::processLoRaPacket(packet); // Send received packet to Digi } if (Config.tnc.enableServer) TNC_Utils::sendToClients(packet); // Send received packet to TNC KISS if (Config.tnc.enableSerial) TNC_Utils::sendToSerial(packet); // Send received packet to Serial KISS if (Config.mqtt.active) MQTT_Utils::sendToMqtt(packet); // Send received packet to MQTT } if (Config.aprs_is.active) APRS_IS_Utils::listenAPRSIS(); // listen received packet from APRSIS STATION_Utils::processOutputPacketBuffer(); #ifdef HAS_EPAPER // Only consider updating every 10 seconds (when data to show is different from before) if(lastEpaperTime == 0 || millis() - lastEpaperTime > 10000) { String posibleEpaperText = firstLine + secondLine + thirdLine + fourthLine + fifthLine + sixthLine + seventhLine; if (lastEpaperText != posibleEpaperText) { displayShow(firstLine, secondLine, thirdLine, fourthLine, fifthLine, sixthLine, seventhLine, 0); lastEpaperText = posibleEpaperText; lastEpaperTime = millis(); } } #else displayShow(firstLine, secondLine, thirdLine, fourthLine, fifthLine, sixthLine, seventhLine, 0); #endif Utils::checkRebootTime(); Utils::checkSleepByLowBatteryVoltage(1); } }