- + + + + + + - + diff --git a/data_embed/js.js b/data_embed/js.js index 1835e0c..68a547b 100644 --- a/data_embed/js.js +++ b/data_embed/js.js @@ -113,4 +113,8 @@ function updateFileUpload(event) { xhr.open('POST', '/update'); xhr.send(data); +} + +function confirmAction(actionName) { + return window.confirm('Are you shure want to ' + actionName); } \ No newline at end of file diff --git a/include/taskGPS.h b/include/taskGPS.h index 5588a6c..f192fd8 100644 --- a/include/taskGPS.h +++ b/include/taskGPS.h @@ -2,4 +2,5 @@ #include extern TinyGPSPlus gps; -void taskGPS(void *parameter); + +[[noreturn]] void taskGPS(void *parameter); diff --git a/include/taskTNC.h b/include/taskTNC.h index c938cc5..3f9884d 100644 --- a/include/taskTNC.h +++ b/include/taskTNC.h @@ -1,5 +1,6 @@ #include #include +#include #if defined(ENABLE_BLUETOOTH) #include "BluetoothSerial.h" @@ -7,6 +8,7 @@ #endif #if defined(ENABLE_WIFI) #include "taskWebServer.h" + #include "wifi_clients.h" #endif extern QueueHandle_t tncToSendQueue; extern QueueHandle_t tncReceivedQueue; diff --git a/include/taskWebServer.h b/include/taskWebServer.h index 8ed5554..cb0dce9 100644 --- a/include/taskWebServer.h +++ b/include/taskWebServer.h @@ -12,6 +12,7 @@ extern BG_RF95 rf95; #ifdef KISS_PROTOCOL extern WiFiServer tncServer; #endif +extern WiFiServer gpsServer; typedef struct { String callsign; } tWebServerCfg; diff --git a/include/wifi_clients.h b/include/wifi_clients.h new file mode 100644 index 0000000..18487e7 --- /dev/null +++ b/include/wifi_clients.h @@ -0,0 +1,13 @@ +// +// Created by Admin on 11.06.2021. +// + +#ifndef TTGO_T_BEAM_LORA_APRS_WIFI_CLIENTS_H +#define TTGO_T_BEAM_LORA_APRS_WIFI_CLIENTS_H +#include +#include + +typedef void (*f_connectedClientCallback_t) (WiFiClient *, int, const String *); +void iterateWifiClients(f_connectedClientCallback_t callback, const String *data, WiFiClient * wifiClients[], int maxWifiClients); +void check_for_new_clients(WiFiServer *wiFiServer, WiFiClient * wifiClients[], int maxWifiClients); +#endif //TTGO_T_BEAM_LORA_APRS_WIFI_CLIENTS_H diff --git a/lib/BG_RF95/BG_RF95.cpp b/lib/BG_RF95/BG_RF95.cpp index 67158e5..a3d77b8 100644 --- a/lib/BG_RF95/BG_RF95.cpp +++ b/lib/BG_RF95/BG_RF95.cpp @@ -116,7 +116,7 @@ bool BG_RF95::init() // An innocuous ISM frequency, same as RF22's setFrequency(433.800); // Lowish power - setTxPower(20); + setTxPower(23); return true; } @@ -392,7 +392,7 @@ void BG_RF95::setTxPower(int8_t power, bool useRFO) // for the transmitter output if (useRFO) { - if (power > 14) power = 14; + if (power > 19) power = 19; if (power < -1) power = -1; spiWrite(BG_RF95_REG_09_PA_CONFIG, BG_RF95_MAX_POWER | (power + 1)); } else { @@ -407,7 +407,7 @@ void BG_RF95::setTxPower(int8_t power, bool useRFO) spiWrite(BG_RF95_REG_0B_OCP, ( BG_RF95_OCP_ON | BG_RF95_OCP_TRIM ) ); // Trim max current tp 240mA spiWrite(BG_RF95_REG_4D_PA_DAC, BG_RF95_PA_DAC_ENABLE); //power -= 3; - power = 20; // and set PA_DAC_ENABLE + power = 23; // and set PA_DAC_ENABLE } else { spiWrite(BG_RF95_REG_4D_PA_DAC, BG_RF95_PA_DAC_DISABLE); diff --git a/lib/KISS_TO_TNC2/KISS_TO_PKT.cpp b/lib/KISS_TO_TNC2/KISS_TO_PKT.cpp new file mode 100644 index 0000000..1647892 --- /dev/null +++ b/lib/KISS_TO_TNC2/KISS_TO_PKT.cpp @@ -0,0 +1,124 @@ +#include "KISS_TO_PKT.h" + +bool validateTNC2Frame_pkt(const String &tnc2FormattedFrame); +bool validateKISSFrame_pkt(const String &kissFormattedFrame); +String decapsulateKISS_pkt(const String &frame); + +/* + * https://ham.zmailer.org/oh2mqk/aprx/PROTOCOLS + + After successfull login, communication carries "TNC2" format + APRS messages. Namely text encoding of AX.25 UI frames in + what became known as "TNC2 monitor style": + + SOURCE>DESTIN:payload + SOURCE>DESTIN,VIA,VIA:payload + + The SOURCE, DESTIN, and VIA fields are AX.25 address fields, + and have "-SSID" value annexed if the SSID is not zero. + Also in VIA-fields, if the "HAS BEEN DIGIPEATED" bit is set + (AX.25 v2 protocol feature) a star ('*') character is appended. + VIA-fields are separated by comma (',') from DESTIN, and each + other. + + A double-colon (':') separates address data from payload. + The payload is passed _AS_IS_ without altering any message + content bytes, however ending at first CR or LF character + encountered in the packet. + + */ + + +String encode_kiss_pkt(const String &tnc2FormattedFrame, bool &pktFrame) +{ + String TNC2Frame = ""; +// Serial.print(tnc2FormattedFrame); + pktFrame = tnc2FormattedFrame.charAt(1) == R_PKT; + if (pktFrame) { + String ax25Frame = encapsulateKISS_pkt(tnc2FormattedFrame, CMD_DATA); + TNC2Frame += ax25Frame; + } + return TNC2Frame; +} + +String encapsulateKISS_pkt(const String &ax25Frame, uint8_t TNCCmd) +{ + String kissFrame = ""; + kissFrame += (char) FEND; // start of frame + kissFrame += (char) (0x0f & TNCCmd); // TNC0, cmd + for (int i = 0; i < ax25Frame.length() ; ++i) + { + char currentChar = ax25Frame.charAt(i); + if (currentChar == (char) FEND) + { + kissFrame += (char) FESC; + kissFrame += (char) TFEND; + } + else if (currentChar == (char) FESC) + { + kissFrame += (char) FESC; + kissFrame += (char) TFESC; + } + else + { + kissFrame += currentChar; + } + } + kissFrame += (char) FEND; // end of frame + return kissFrame; +} + + +String decapsulateKISS_pkt(const String &frame, uint8_t TNCCmd) +{ + String ax25Frame = ""; + for (int i = 2; i < frame.length() - 1; ++i) + { + char currentChar = frame.charAt(i); + if (currentChar == (char)FESC) + { + char nextChar = frame.charAt(i + 1); + if (nextChar == (char)TFEND) + { + ax25Frame += (char)FEND; + } + else if (nextChar == (char)TFESC) + { + ax25Frame += (char)FESC; + } + ++i; + } + else + { + ax25Frame += currentChar; + } + } + return ax25Frame; +} + +String decode_kiss_pkt(const String &inputKISSTNCFrame, bool &dataFrame, bool &pktFrame) { + String TNC2Frame = ""; + if (validateKISSFrame_pkt(inputKISSTNCFrame)) { + dataFrame = inputKISSTNCFrame.charAt(1) == CMD_DATA; + // Auto switch to packet mode if find cmd data. + if (inputKISSTNCFrame.charAt(2) == T_PKT) { pktFrame = true;} + else { pktFrame = false; } + //END + if (dataFrame && pktFrame){ + String ax25Frame = decapsulateKISS_pkt(inputKISSTNCFrame, CMD_DATA); + delay(250); + TNC2Frame += ax25Frame; + } + } + return TNC2Frame; +} + +bool validateTNC2Frame_pkt(const String &tnc2FormattedFrame) { + return (tnc2FormattedFrame.indexOf(':') != -1) && + (tnc2FormattedFrame.indexOf('>') != -1); +} + +bool validateKISSFrame_pkt(const String &kissFormattedFrame) { + return kissFormattedFrame.charAt(0) == (char) FEND && + kissFormattedFrame.charAt(kissFormattedFrame.length() - 1) == (char) FEND; +} diff --git a/lib/KISS_TO_TNC2/KISS_TO_PKT.h b/lib/KISS_TO_TNC2/KISS_TO_PKT.h new file mode 100644 index 0000000..2ec6325 --- /dev/null +++ b/lib/KISS_TO_TNC2/KISS_TO_PKT.h @@ -0,0 +1,12 @@ +#include +#include "KISS.h" + +//Data control frames, ported from Direwolf +#define T_PKT 0xaa +#define R_PKT 0xa6 +//END +//PACKET MODE +String encode_kiss_pkt(const String& tnc2FormattedFrame, bool &pktFrame); +String decode_kiss_pkt(const String &inputKISSTNCFrame, bool &dataFrame, bool &pktFrame); + +String encapsulateKISS_pkt(const String &ax25Frame, uint8_t TNCCmd); \ No newline at end of file diff --git a/lib/KISS_TO_TNC2/KISS_TO_TNC2.cpp b/lib/KISS_TO_TNC2/KISS_TO_TNC2.cpp index b429a81..dd9ff3c 100644 --- a/lib/KISS_TO_TNC2/KISS_TO_TNC2.cpp +++ b/lib/KISS_TO_TNC2/KISS_TO_TNC2.cpp @@ -7,7 +7,7 @@ String decode_address_ax25(const String &ax25Address, bool &isLast, bool isRelay bool validateKISSFrame(const String &kissFormattedFrame); -String decapsulateKISS(const String& frame); +String decapsulateKISS(const String &frame); /* * https://ham.zmailer.org/oh2mqk/aprx/PROTOCOLS @@ -33,100 +33,111 @@ String decapsulateKISS(const String& frame); */ -String encode_kiss(const String& tnc2FormattedFrame) { - String ax25Frame = ""; - - if (validateTNC2Frame(tnc2FormattedFrame)){ - String address = ""; - bool dst_addres_written = false; - for (int p=0;p<=tnc2FormattedFrame.indexOf(':');p++){ - char currentChar = tnc2FormattedFrame.charAt(p); - if (currentChar == ':' || currentChar == '>' || currentChar == ','){ - if (!dst_addres_written && (currentChar == ',' || currentChar == ':')){ - // ax25 frame DST SRC - // tnc2 frame SRC DST - ax25Frame = encode_address_ax25(address) + ax25Frame; - dst_addres_written = true; - } else { - ax25Frame += encode_address_ax25(address); - } - address = ""; - } else { - address += currentChar; - } +String encode_kiss(const String &tnc2FormattedFrame) { + String ax25Frame = ""; + + if (validateTNC2Frame(tnc2FormattedFrame)) { + String address = ""; + bool dst_addres_written = false; + for (int p = 0; p <= tnc2FormattedFrame.indexOf(':'); p++) { + char currentChar = tnc2FormattedFrame.charAt(p); + if (currentChar == ':' || currentChar == '>' || currentChar == ',') { + if (!dst_addres_written && (currentChar == ',' || currentChar == ':')) { + // ax25 frame DST SRC + // tnc2 frame SRC DST + ax25Frame = encode_address_ax25(address) + ax25Frame; + dst_addres_written = true; + } else { + ax25Frame += encode_address_ax25(address); } - auto lastAddressChar = (uint8_t)ax25Frame.charAt(ax25Frame.length() - 1); - ax25Frame.setCharAt(ax25Frame.length() - 1, (char)(lastAddressChar | IS_LAST_ADDRESS_POSITION_MASK)); - ax25Frame += (char)APRS_CONTROL_FIELD; - ax25Frame += (char)APRS_INFORMATION_FIELD; - ax25Frame += tnc2FormattedFrame.substring(tnc2FormattedFrame.indexOf(':')+1); + address = ""; + } else { + address += currentChar; + } } - - String kissFrame = encapsulateKISS(ax25Frame, CMD_DATA); - return kissFrame; + auto lastAddressChar = (uint8_t) ax25Frame.charAt(ax25Frame.length() - 1); + ax25Frame.setCharAt(ax25Frame.length() - 1, (char) (lastAddressChar | IS_LAST_ADDRESS_POSITION_MASK)); + ax25Frame += (char) APRS_CONTROL_FIELD; + ax25Frame += (char) APRS_INFORMATION_FIELD; + ax25Frame += tnc2FormattedFrame.substring(tnc2FormattedFrame.indexOf(':') + 1); + } + + String kissFrame = encapsulateKISS(ax25Frame, CMD_DATA); + return kissFrame; } String encapsulateKISS(const String &ax25Frame, uint8_t TNCCmd) { - String kissFrame = ""; - kissFrame += (char)FEND; // start of frame - kissFrame += (char)(0x0f & TNCCmd); // TNC0, cmd - for (int i = 0; i < ax25Frame.length(); ++i) { - char currentChar = ax25Frame.charAt(i); - if (currentChar == (char)FEND){ - kissFrame += (char)FESC; - kissFrame += (char)TFEND; - } else if (currentChar == (char)FESC){ - kissFrame += (char)FESC; - kissFrame += (char)TFESC; - } else { - kissFrame += currentChar; - } + String kissFrame = ""; + kissFrame += (char) FEND; // start of frame + kissFrame += (char) (0x0f & TNCCmd); // TNC0, cmd + for (int i = 0; i < ax25Frame.length(); ++i) { + char currentChar = ax25Frame.charAt(i); + if (currentChar == (char) FEND) { + kissFrame += (char) FESC; + kissFrame += (char) TFEND; + } else if (currentChar == (char) FESC) { + kissFrame += (char) FESC; + kissFrame += (char) TFESC; + } else { + kissFrame += currentChar; } - kissFrame += (char)FEND; // end of frame - return kissFrame; + } + kissFrame += (char) FEND; // end of frame + return kissFrame; } -String decapsulateKISS(const String& frame) { - String ax25Frame = ""; - for (int i = 2; i < frame.length() - 1; ++i) { - char currentChar = frame.charAt(i); - if (currentChar == (char)FESC){ - char nextChar = frame.charAt(i+1); - if (nextChar == (char)TFEND){ - ax25Frame += (char)FEND; - } else if (nextChar == (char)TFESC){ - ax25Frame += (char)FESC; - } - i++; - } else { - ax25Frame += currentChar; - } +String decapsulateKISS(const String &frame) { + String ax25Frame = ""; + for (int i = 2; i < frame.length() - 1; ++i) { + char currentChar = frame.charAt(i); + if (currentChar == (char) FESC) { + char nextChar = frame.charAt(i + 1); + if (nextChar == (char) TFEND) { + ax25Frame += (char) FEND; + } else if (nextChar == (char) TFESC) { + ax25Frame += (char) FESC; + } + i++; + } else { + ax25Frame += currentChar; } + } - return ax25Frame; + return ax25Frame; } -String decode_kiss(const String& kissFormattedFrame) { - String TNC2Frame = ""; - - if (validateKISSFrame(kissFormattedFrame)){ - String ax25Frame = decapsulateKISS(kissFormattedFrame); - bool isLast = false; - String dst_addr = decode_address_ax25(ax25Frame.substring(0, 7), isLast, false); - String src_addr = decode_address_ax25(ax25Frame.substring(7, 14), isLast, false); - TNC2Frame = src_addr + ">" + dst_addr; - int digi_info_index = 14; - while (!isLast && digi_info_index + 7 < ax25Frame.length()){ - String digi_addr = decode_address_ax25(ax25Frame.substring(digi_info_index, digi_info_index + 7), isLast, true); - TNC2Frame += ',' + digi_addr; - digi_info_index += 7; - } - TNC2Frame += ':'; - TNC2Frame += ax25Frame.substring(digi_info_index + 2); +/** + * + * @param inputKISSTNCFrame + * @param dataFrame + * @return Decapsulated TNC2KISS APRS data frame, or raw command data frame + */ +String decode_kiss(const String &inputKISSTNCFrame, bool &dataFrame) { + String TNC2Frame = ""; + + if (validateKISSFrame(inputKISSTNCFrame)) { + dataFrame = inputKISSTNCFrame.charAt(1) == CMD_DATA; + if (dataFrame){ + String ax25Frame = decapsulateKISS(inputKISSTNCFrame); + bool isLast = false; + String dst_addr = decode_address_ax25(ax25Frame.substring(0, 7), isLast, false); + String src_addr = decode_address_ax25(ax25Frame.substring(7, 14), isLast, false); + TNC2Frame = src_addr + ">" + dst_addr; + int digi_info_index = 14; + while (!isLast && digi_info_index + 7 < ax25Frame.length()) { + String digi_addr = decode_address_ax25(ax25Frame.substring(digi_info_index, digi_info_index + 7), isLast, true); + TNC2Frame += ',' + digi_addr; + digi_info_index += 7; + } + TNC2Frame += ':'; + TNC2Frame += ax25Frame.substring(digi_info_index + 2); + } else { + // command frame, currently ignored + TNC2Frame += inputKISSTNCFrame; } - - return TNC2Frame; + } + return TNC2Frame; } /** @@ -135,32 +146,32 @@ String decode_kiss(const String& kissFormattedFrame) { * @return */ String encode_address_ax25(String tnc2Address) { - bool hasBeenDigipited = tnc2Address.indexOf('*') != -1; + bool hasBeenDigipited = tnc2Address.indexOf('*') != -1; - if (tnc2Address.indexOf('-') == -1){ - if (hasBeenDigipited){ - // ex. TCPIP* in tnc2Address - // so we skip last char - tnc2Address = tnc2Address.substring(0, tnc2Address.length()-1); - } - tnc2Address += "-0"; + if (tnc2Address.indexOf('-') == -1) { + if (hasBeenDigipited) { + // ex. TCPIP* in tnc2Address + // so we skip last char + tnc2Address = tnc2Address.substring(0, tnc2Address.length() - 1); } - - int separatorIndex = tnc2Address.indexOf('-'); - int ssid = tnc2Address.substring(separatorIndex + 1).toInt(); - // TODO: SSID should not be > 16 - String kissAddress = ""; - for (int i = 0; i < 6; ++i) { - char addressChar; - if (tnc2Address.length() > i && i < separatorIndex){ - addressChar = tnc2Address.charAt(i); - } else { - addressChar = ' '; - } - kissAddress += (char)(addressChar << 1); + tnc2Address += "-0"; + } + + int separatorIndex = tnc2Address.indexOf('-'); + int ssid = tnc2Address.substring(separatorIndex + 1).toInt(); + // TODO: SSID should not be > 16 + String kissAddress = ""; + for (int i = 0; i < 6; ++i) { + char addressChar; + if (tnc2Address.length() > i && i < separatorIndex) { + addressChar = tnc2Address.charAt(i); + } else { + addressChar = ' '; } - kissAddress += (char)((ssid << 1) | 0b01100000 | (hasBeenDigipited ? HAS_BEEN_DIGIPITED_MASK : 0)); - return kissAddress; + kissAddress += (char) (addressChar << 1); + } + kissAddress += (char) ((ssid << 1) | 0b01100000 | (hasBeenDigipited ? HAS_BEEN_DIGIPITED_MASK : 0)); + return kissAddress; } /** @@ -169,30 +180,35 @@ String encode_address_ax25(String tnc2Address) { * @return */ String decode_address_ax25(const String &ax25Address, bool &isLast, bool isRelay) { - String TNCAddress = ""; - for (int i = 0; i < 6; ++i) { - uint8_t currentCharacter = ax25Address.charAt(i); - currentCharacter >>= 1; - if (currentCharacter != ' '){ - TNCAddress += (char)currentCharacter; - } - } - auto ssid_char = (uint8_t) ax25Address.charAt(6); - bool hasBeenDigipited = ssid_char & HAS_BEEN_DIGIPITED_MASK; - isLast = ssid_char & IS_LAST_ADDRESS_POSITION_MASK; - ssid_char >>= 1; - - int ssid = 0b1111 & ssid_char; - if (ssid){ - TNCAddress += '-'; - TNCAddress += ssid; + String TNCAddress = ""; + for (int i = 0; i < 6; ++i) { + uint8_t currentCharacter = ax25Address.charAt(i); + currentCharacter >>= 1; + if (currentCharacter != ' ') { + TNCAddress += (char) currentCharacter; } - if (isRelay && hasBeenDigipited){ - TNCAddress += '*'; - } - return TNCAddress; + } + auto ssid_char = (uint8_t) ax25Address.charAt(6); + bool hasBeenDigipited = ssid_char & HAS_BEEN_DIGIPITED_MASK; + isLast = ssid_char & IS_LAST_ADDRESS_POSITION_MASK; + ssid_char >>= 1; + + int ssid = 0b1111 & ssid_char; + if (ssid) { + TNCAddress += '-'; + TNCAddress += ssid; + } + if (isRelay && hasBeenDigipited) { + TNCAddress += '*'; + } + return TNCAddress; } -bool validateTNC2Frame(const String &tnc2FormattedFrame) { return (tnc2FormattedFrame.indexOf(':') != -1) && (tnc2FormattedFrame.indexOf('>') != -1); } +bool validateTNC2Frame(const String &tnc2FormattedFrame) { + return (tnc2FormattedFrame.indexOf(':') != -1) && (tnc2FormattedFrame.indexOf('>') != -1); +} -bool validateKISSFrame(const String &kissFormattedFrame) { return kissFormattedFrame.charAt(0) == (char)FEND && kissFormattedFrame.charAt(kissFormattedFrame.length() - 1) == (char)FEND; } +bool validateKISSFrame(const String &kissFormattedFrame) { + return kissFormattedFrame.charAt(0) == (char) FEND && + kissFormattedFrame.charAt(kissFormattedFrame.length() - 1) == (char) FEND; +} \ No newline at end of file diff --git a/lib/KISS_TO_TNC2/KISS_TO_TNC2.h b/lib/KISS_TO_TNC2/KISS_TO_TNC2.h index ecf447a..87aa7a9 100644 --- a/lib/KISS_TO_TNC2/KISS_TO_TNC2.h +++ b/lib/KISS_TO_TNC2/KISS_TO_TNC2.h @@ -8,6 +8,6 @@ #define IS_LAST_ADDRESS_POSITION_MASK 0b1 String encode_kiss(const String& tnc2FormattedFrame); -String decode_kiss(const String& kissFormattedFrame); +String decode_kiss(const String &inputKISSTNCFrame, bool &dataFrame); String encapsulateKISS(const String &ax25Frame, uint8_t TNCCmd); \ No newline at end of file diff --git a/lib/PSRAMJsonDocument/PSRAMJsonDocument.h b/lib/PSRAMJsonDocument/PSRAMJsonDocument.h new file mode 100644 index 0000000..da0087b --- /dev/null +++ b/lib/PSRAMJsonDocument/PSRAMJsonDocument.h @@ -0,0 +1,27 @@ +// +// Created by Admin on 14.06.2021. +// + +#ifndef TTGO_T_BEAM_LORA_APRS_PSRAMJSONDOCUMENT_H +#define TTGO_T_BEAM_LORA_APRS_PSRAMJSONDOCUMENT_H + +#include + +struct SpiRamAllocator { + void* allocate(size_t size) { + return heap_caps_malloc(size, MALLOC_CAP_SPIRAM); + } + + void deallocate(void* pointer) { + heap_caps_free(pointer); + } + + void* reallocate(void* ptr, size_t new_size) { + return heap_caps_realloc(ptr, new_size, MALLOC_CAP_SPIRAM); + } +}; + +using PSRAMJsonDocument = BasicJsonDocument; + + +#endif //TTGO_T_BEAM_LORA_APRS_PSRAMJSONDOCUMENT_H diff --git a/platformio.ini b/platformio.ini index 6f7a08b..9c79649 100644 --- a/platformio.ini +++ b/platformio.ini @@ -20,42 +20,47 @@ extra_scripts = pre:tools/buildscript_versioning.py pre:tools/compress_assets.py lib_deps = - RadioHead + http://git.mis.ks.ua/US1GHQ/RadioHead.git TinyGPSPlus Adafruit SSD1306 Adafruit GFX Library Adafruit Unified Sensor + adafruit/Adafruit BMP280 Library @ 2.4.0 https://github.com/SQ9MDD/AXP202X_Library.git SparkFun u-blox Arduino Library bblanchon/ArduinoJson + DallasTemperature build_flags = -Wl,--gc-sections,--relax - -D 'KISS_PROTOCOL' - -D 'CALLSIGN="N0CALL-0"' - -D 'DIGI_PATH="ECHO"' - -D 'FIXED_BEACON_EN' - -D 'LATIDUDE_PRESET="0000.00N"' - -D 'LONGITUDE_PRESET="00000.00E"' - -D 'APRS_SYMBOL_TABLE="/"' - -D 'APRS_SYMBOL="["' - -D 'MY_COMMENT="Lora Tracker"' - -D 'SHOW_ALT' - -D 'SHOW_BATT' - -D 'SHOW_RX_PACKET' - -D 'SHOW_RX_TIME=10000' - -D 'TXFREQ=433.775' - -D 'SPEED_1200' ; comment out to set 300baud - -D 'TXdbmW=20' - -D 'ENABLE_OLED' - -D 'ENABLE_LED_SIGNALING' - -D 'NETWORK_TNC_PORT=8001' - -D 'MAX_TIME_TO_NEXT_TX=360000L' - -D 'FIX_BEACON_INTERVAL=1800000L' -; -D 'RSSI_SNR_REPORT' ; RSSI and snr report in the KISS PACKET -; -D 'TX_RX_LNA' ; Enable external pins for 1W modules, choose pins in *.ino file (default using D16 for RXEN and D17 for TXEN) + -D 'KISS_PROTOCOL' ; leave enabled + -D 'CALLSIGN="NOCALL-0"' ; can be set from www interfeace + -D 'DIGI_PATH="ECHO"' ; can be set from www interfeace + -D 'FIXED_BEACON_EN' ; can be set from www interfeace + -D 'LATIDUDE_PRESET="0000.00N"' ; can be set from www interfeace + -D 'LONGITUDE_PRESET="00000.00E"' ; can be set from www interfeace + -D 'APRS_SYMBOL_TABLE="/"' ; can be set from www interfeace + -D 'APRS_SYMBOL="["' ; can be set from www interfeace + -D 'MY_COMMENT="Lora Tracker"' ; can be set from www interfeace + -D 'SHOW_ALT' ; can be set from www interfeace + -D 'SHOW_BATT' ; can be set from www interfeace + -D 'SHOW_RX_PACKET' ; can be set from www interfeace + -D 'SHOW_RX_TIME=10000' ; can be set from www interfeace + -D 'TXFREQ=433.775' ; set operating frequency + -D 'SPEED_1200' ; comment out to set 300baud + -D 'TXdbmW=20' ; set power +; -D 'ENABLE_OLED' ; can be set from www interfeace + -D 'ENABLE_LED_SIGNALING' ; can be set from www interfeace + -D 'NETWORK_TNC_PORT=8001' ; default KISS TCP port + -D 'NETWORK_GPS_PORT=10110' ; GPS NMEA Port + -D 'MAX_TIME_TO_NEXT_TX=360000L' ; can be set from www interfeace + -D 'FIX_BEACON_INTERVAL=1800000L' ; can be set from www interfeace +; -D 'RSSI_SNR_REPORT' ; RSSI and snr report in the KISS PACKET +; -D 'TX_RX_LNA' ; Set external pins for 1W modules +; -D 'USE_BME280' ; Temp sensor for simple WX station +; -D 'HEIGTH_PRESET=34' [env:ttgo-t-beam-v1.0] -platform = espressif32 @ 3.3.1 +platform = espressif32 @ 3.3.2 board = ttgo-t-beam build_flags = ${env.build_flags} @@ -64,7 +69,7 @@ build_flags = ; -D ENABLE_BLUETOOTH [env:ttgo-t-beam-v0.7] -platform = espressif32 @ 3.3.1 +platform = espressif32 @ 3.3.2 board = ttgo-t-beam build_flags = ${env.build_flags} @@ -73,7 +78,7 @@ build_flags = -D T_BEAM_V0_7 [env:ttgo-lora32-v2.1] -platform = espressif32 @ 3.3.1 +platform = espressif32 @ 3.3.2 board = ttgo-lora32-v21 build_flags = ${env.build_flags} @@ -82,7 +87,7 @@ build_flags = -D LORA32_21 [env:ttgo-lora32-v2] -platform = espressif32 @ 3.3.1 +platform = espressif32 @ 3.3.2 board = ttgo-lora32-v2 build_flags = ${env.build_flags} @@ -91,7 +96,7 @@ build_flags = -D LORA32_2 [env:ttgo-lora32-v1] -platform = espressif32 @ 3.3.1 +platform = espressif32 @ 3.3.2 board = ttgo-lora32-v1 build_flags = ${env.build_flags} @@ -100,7 +105,7 @@ build_flags = -D LORA32_1 [env:Heltec-WiFi-v1] -platform = espressif32 @ 3.3.1 +platform = espressif32 @ 3.3.2 board = heltec_wifi_kit_32 build_flags = ${env.build_flags} @@ -109,7 +114,7 @@ build_flags = -D HELTEC_V1 [env:Heltec-WiFi-v2] -platform = espressif32 @ 3.3.1 +platform = espressif32 @ 3.3.2 board = heltec_wifi_kit_32_v2 board_build.f_cpu = 80000000L build_flags = @@ -119,7 +124,7 @@ build_flags = -D HELTEC_V2 [env:Esp32-Dev-v1] -platform = espressif32 @ 3.3.1 +platform = espressif32 @ 3.3.2 board = esp32dev ; change MCU frequency ;board_build.f_cpu = 80000000L @@ -139,7 +144,7 @@ build_flags = -D ENABLE_BLUETOOTH -D ENABLE_SYSLOG -D 'SYSLOG_IP="192.168.0.102"' - -D DEVELOPMENT_DEBUG + -D DEVELOPMENT_DEBUG lib_deps = ${env.lib_deps} arcao/Syslog diff --git a/src/TTGO_T-Beam_LoRa_APRS.ino b/src/TTGO_T-Beam_LoRa_APRS.ino index 503c2a4..9860819 100644 --- a/src/TTGO_T-Beam_LoRa_APRS.ino +++ b/src/TTGO_T-Beam_LoRa_APRS.ino @@ -32,6 +32,10 @@ #include "taskWebServer.h" #endif +#ifdef USE_BME280 + #include // BME280 Library +#endif + // oled address #define SSD1306_ADDRESS 0x3C @@ -149,9 +153,15 @@ boolean show_cmt = true; #else boolean enabled_oled = false; #endif +#ifdef USE_BME280 + boolean enable_bme280 = true; +#else + boolean enable_bme280 = false; +#endif // Variables and Constants String loraReceivedFrameString = ""; //data on buff is copied to this string +String loraSendFrameString = ""; //data on buff is copied to this string String Outputstring = ""; String outString=""; //The new Output String with GPS Conversion RAW String LongShown=""; @@ -203,7 +213,7 @@ ulong time_delay = 0; ulong shutdown_delay = 0; ulong shutdown_delay_time = 10000; ulong shutdown_countdown_timer = 0; -boolean shutdown_active =true; +boolean shutdown_active =false; boolean shutdown_countdown_timer_enable = false; boolean shutdown_usb_status_bef = false; #define ANGLE 60 // angle to send packet at smart beaconing @@ -211,6 +221,16 @@ boolean shutdown_usb_status_bef = false; float average_course[ANGLE_AVGS]; float avg_c_y, avg_c_x; +//Variables for BME Sensors +boolean hum_temp = false; +uint8_t hum_temp_ctr, hum_temp_ctr_max = 3; +boolean tempsensoravailable=true; +float hum=0; //Stores humidity value +float temp=99.99; //Stores temperature value +float tempf=99.99; //Stores temperature value +float pressure=0; //Stores pressure value in hPa +int pressure_offset=0; //Stores offset for pressure correction + uint8_t txPower = TXdbmW; #ifdef ENABLE_WIFI @@ -239,6 +259,10 @@ uint8_t loraReceivedLength = sizeof(lora_RXBUFF); #else #define OLED_RESET 16 #endif +//Temp sensor +#ifdef USE_BME280 + Adafruit_BMP280 bme; // if BME is used +#endif Adafruit_SSD1306 display(128, 64, &Wire, OLED_RESET); @@ -277,9 +301,10 @@ void prepareAPRSFrame(){ outString += Tcall; if (relay_path){ - outString += ">APLS01," + relay_path + ":!"; + outString += ">APLO01:!"; + }else{ - outString += ">APLS01:!"; + outString += ">APLO01," + relay_path + ":!"; } if(gps_state && gps.location.isValid()){ @@ -308,12 +333,48 @@ void prepareAPRSFrame(){ } outString += Talt; } - }else{ //fixed position not compresed + } + else + { + //fixed position not compresed outString += aprsLatPreset; outString += aprsSymbolTable; outString += aprsLonPreset; outString += aprsSymbol; - } +#ifdef USE_BME280 + if(enable_bme280) + { + // bme.takeForcedMeasurement(); + tempf = bme.readTemperature()*9/5+29; + // hum = bme.readHumidity(); + pressure = bme.readPressure()/100 + pressure_offset; + outString += ".../...g...t"; + if (tempf < 0) { // negative Werte erstellen + outString += "-"; + if(tempf>-10) {outString += "0"; } + tempf = abs(tempf); + } + else + { // positive Werte erstellen + if(tempf<100) {outString += "0"; } + if(tempf<10) {outString += "0"; } + } + helper = String(tempf-3,0); + helper.trim(); + outString += helper; + outString += "r...p...P...h"; + if(hum<10) {outString += "0"; } + helper = String(hum,0); + helper.trim(); + outString += helper; + outString += "b"; + if(pressure<1000) {outString += "0"; } + helper = String(pressure*10,0); + helper.trim(); + outString += helper; + } +#endif +} if(show_cmt){ outString += aprsComment; @@ -363,7 +424,7 @@ void sendpacket(){ * @param aprsFreq * @param message */ -void loraSend(byte txPower, float aprsFreq, const String &message) { +void loraSend(byte txPower, float aprsFreq, const String message) { #ifdef TX_RX_LNA digitalWrite(TXPIN, HIGH); digitalWrite(RXPIN, LOW); @@ -375,7 +436,12 @@ void loraSend(byte txPower, float aprsFreq, const String &message) { lastTX = millis(); int messageSize = min(message.length(), sizeof(lora_TXBUFF) - 1); - message.toCharArray((char*)lora_TXBUFF, messageSize + 1, 0); +// message.getBytes(lora_TXBUFF, messageSize + 1, 0); + loraSendFrameString = message; + for (int i = 0 ; i < messageSize ; i++) { + lora_TXBUFF[i] = (char)loraSendFrameString[i]; + } + if (aprsMode == 1200) { rf95.setModemConfig(BG_RF95::Bw125Cr47Sf512); @@ -384,6 +450,7 @@ void loraSend(byte txPower, float aprsFreq, const String &message) { { rf95.setModemConfig(BG_RF95::Bw125Cr45Sf4096); } + rf95.setFrequency(aprsFreq); rf95.setTxPower(txPower); rf95.sendAPRS(lora_TXBUFF, messageSize); @@ -540,7 +607,7 @@ void sendTelemetryFrame() { String telemetryEquations = String(":") + Tcall + ":EQNS.0,5.1,3000,0,10,0,0,10,0,0,28,3000,0,10,0"; String telemetryData = String("T#MIC") + String(b_volt) + ","+ String(b_in_c) + ","+ String(b_out_c) + ","+ String(ac_volt) + ","+ String(ac_c) + ",00000000"; String telemetryBase = ""; - telemetryBase += Tcall + ">APLS01" + ":"; + telemetryBase += Tcall + ">APLO01" + ":"; sendToTNC(telemetryBase + telemetryParamsNames); sendToTNC(telemetryBase + telemetryUnitNames); sendToTNC(telemetryBase + telemetryEquations); @@ -552,7 +619,9 @@ void sendTelemetryFrame() { // + SETUP --------------------------------------------------------------+// void setup(){ SPI.begin(SPI_sck,SPI_miso,SPI_mosi,SPI_ss); //DO2JMG Heltec Patch - + + bool bme_status; + #ifdef BUZZER ledcSetup(0,1E5,12); ledcAttachPin(BUZZER,0); @@ -868,11 +937,28 @@ void setup(){ writedisplaytext("","","","","",""); time_to_refresh = millis() + showRXTime; displayInvalidGPS(); + +#ifdef USE_BME280 + bme_status = bme.begin(0x76); + if (!bme_status) + { + Serial.println("Could not find a valid BME280 sensor, check wiring!"); + //writedisplaytext("LoRa-APRS","","Init:","BME280 ERROR!","","",3000); + tempsensoravailable = false; + } + pressure_offset = calc_pressure_offset(HEIGTH_PRESET); + // bme.takeForcedMeasurement(); + temp = bme.readTemperature() - 3; // bme Temperatur auslesen + // hum = bme.readHumidity(); + pressure = bme.readPressure()/100 + pressure_offset; +#endif + #ifdef TX_RX_LNA digitalWrite(TXPIN, LOW); digitalWrite(RXPIN, HIGH); #endif digitalWrite(TXLED, LOW); + hum_temp_ctr = 0; } // +---------------------------------------------------------------------+// @@ -970,6 +1056,34 @@ void loop() { } #endif + if (hum_temp) + { + ++hum_temp_ctr; + if (hum_temp_ctr>hum_temp_ctr_max) + { + hum_temp_ctr = 0; + hum_temp=false; + } + #ifdef USE_BME280 + //bme.takeForcedMeasurement(); + temp = bme.readTemperature() - 3; // bme Temperatur auslesen + #endif + } + else + { + ++hum_temp_ctr; + if (hum_temp_ctr>hum_temp_ctr_max) + { + hum_temp_ctr = 0; + hum_temp=true; + } + #ifdef USE_BME280 + //bme.takeForcedMeasurement(); + //hum = bme.readHumidity(); + pressure = bme.readPressure()/100 + pressure_offset; + #endif + } + if (rf95.waitAvailableTimeout(100)) { #ifdef T_BEAM_V1_0 #ifdef ENABLE_LED_SIGNALING @@ -1112,4 +1226,16 @@ void loop() { #endif #endif vTaskDelay(1); +} + +int calc_pressure_offset(int height) { + // + // A very simple method to calculate the offset for correcting the measured air pressure + // to the pressure at mean sea level (MSL). It is simplificated to "For each 8m change in height + // the pressure is changing by 1hPa." + // The exact method is described at + // https://de.wikipedia.org/wiki/Barometrische_H%C3%B6henformel#Internationale_H%C3%B6henformel + // + int offset = round(height / 8); + return(offset); } \ No newline at end of file diff --git a/src/taskGPS.cpp b/src/taskGPS.cpp index 895797f..0288f99 100644 --- a/src/taskGPS.cpp +++ b/src/taskGPS.cpp @@ -1,8 +1,16 @@ #include #include +#include + SFE_UBLOX_GPS myGPS; +#ifdef ENABLE_WIFI + #include "wifi_clients.h" + #define MAX_GPS_WIFI_CLIENTS 6 + WiFiClient * gps_clients[MAX_GPS_WIFI_CLIENTS]; +#endif + // Pins for GPS #ifdef T_BEAM_V1_0 static const int RXPin = 12, TXPin = 34; @@ -14,7 +22,7 @@ HardwareSerial gpsSerial(1); // TTGO has HW serial TinyGPSPlus gps; // The TinyGPS++ object bool gpsInitialized = false; -void taskGPS(void *parameter) { +[[noreturn]] void taskGPS(void *parameter) { if (!gpsInitialized){ gpsSerial.begin(GPSBaud, SERIAL_8N1, TXPin, RXPin); //Startup HW serial for GPS @@ -35,9 +43,31 @@ void taskGPS(void *parameter) { } } + String gpsDataBuffer = " "; for (;;) { + #ifdef ENABLE_WIFI + check_for_new_clients(&gpsServer, gps_clients, MAX_GPS_WIFI_CLIENTS); + #endif while (gpsSerial.available() > 0) { - gps.encode(gpsSerial.read()); + char gpsChar = (char)gpsSerial.read(); + gps.encode(gpsChar); + #ifdef ENABLE_WIFI + if (gpsChar == '$') { + gpsDataBuffer = String(gpsChar); + } else { + gpsDataBuffer += String(gpsChar); + + if (gpsChar == '\n') { + iterateWifiClients([](WiFiClient *client, int clientIdx, const String *data){ + if (client->connected()){ + client->print(*data); + client->flush(); + } + }, &gpsDataBuffer, gps_clients, MAX_GPS_WIFI_CLIENTS); + gpsDataBuffer = ""; + } + } + #endif } vTaskDelay(100 / portTICK_PERIOD_MS); } diff --git a/src/taskTNC.cpp b/src/taskTNC.cpp index 8c499c4..e744798 100644 --- a/src/taskTNC.cpp +++ b/src/taskTNC.cpp @@ -9,25 +9,6 @@ QueueHandle_t tncReceivedQueue = nullptr; #ifdef ENABLE_WIFI #define MAX_WIFI_CLIENTS 6 WiFiClient * clients[MAX_WIFI_CLIENTS]; - - typedef void (*f_connectedClientCallback_t) (WiFiClient *, int, const String *); - - void iterateWifiClients(f_connectedClientCallback_t callback, const String *data){ - for (int i=0; iconnected()) { - callback(client, i, data); - } else { - #ifdef ENABLE_WIFI_CLIENT_DEBUG - Serial.println(String("Disconnected client ") + client->remoteIP().toString() + ":" + client->remotePort()); - #endif - delete client; - clients[i] = nullptr; - } - } - } - } #endif #ifdef ENABLE_WIFI #define IN_TNC_BUFFERS (2+MAX_WIFI_CLIENTS) @@ -46,37 +27,43 @@ QueueHandle_t tncToSendQueue = nullptr; */ void handleKISSData(char character, int bufferIndex) { String *inTNCData = &inTNCDataBuffers[bufferIndex]; - if (inTNCData->length() == 0 && character != (char) FEND){ + if (inTNCData->length() == 0 && character != (char) FEND) + { // kiss frame begins with C0 return; } inTNCData->concat(character); - if (character == (char) FEND && inTNCData->length() > 3) { - const String &TNC2DataFrame = decode_kiss(*inTNCData); - - #ifdef LOCAL_KISS_ECHO - Serial.print(inTNCData); - #ifdef ENABLE_BLUETOOTH - if (SerialBT.hasClient()) { - SerialBT.print(inTNCData); - } - #endif - #ifdef ENABLE_WIFI - iterateWifiClients([](WiFiClient *client, const String *data){ - if (client->connected()){ - client->print(*data); - client->flush(); - } - }, &inTNCData); - #endif - #endif - auto *buffer = new String(); - buffer->concat(TNC2DataFrame); - if (xQueueSend(tncToSendQueue, &buffer, (1000 / portTICK_PERIOD_MS)) != pdPASS){ - delete buffer; + if (character == (char) FEND && inTNCData->length() > 3) + { + bool isDataFrame = false; + bool isPacketFrame = false; + const String &TNC2DataFrame = decode_kiss_pkt(*inTNCData, isDataFrame, isPacketFrame); + + if (isDataFrame && isPacketFrame) { + auto *buffer = new String(); + buffer->concat(TNC2DataFrame); + if (xQueueSend(tncToSendQueue, &buffer, (1000 / portTICK_PERIOD_MS)) != pdPASS) + { + delete buffer; + } + inTNCData->clear(); + } + else if (character == (char) FEND && inTNCData->length() > 3) + { + bool isDataFrame = false; + const String &TNC2DataFrame = decode_kiss(*inTNCData, isDataFrame); + + if (isDataFrame) { + auto *buffer = new String(); + buffer->concat(TNC2DataFrame); + if (xQueueSend(tncToSendQueue, &buffer, (1000 / portTICK_PERIOD_MS)) != pdPASS) + { + delete buffer; + } } inTNCData->clear(); } +} if (inTNCData->length() > 255){ // just in case of garbage input reset data inTNCData->clear(); @@ -103,49 +90,38 @@ void handleKISSData(char character, int bufferIndex) { } #endif #ifdef ENABLE_WIFI - WiFiClient new_client = tncServer.available(); - if (new_client.connected()){ - bool new_client_handled = false; - for (int i=0; i < MAX_WIFI_CLIENTS; i++) { - auto client = clients[i]; - if (client == nullptr) { - client = new WiFiClient(new_client); - clients[i] = client; - new_client_handled = true; - #ifdef ENABLE_WIFI_CLIENT_DEBUG - Serial.println(String("New client #") +String(i) + ": " + client->remoteIP().toString() + ":" + client->remotePort()); - #endif - break; - } - } - #ifdef ENABLE_WIFI_CLIENT_DEBUG - for (int i = 0; i < MAX_WIFI_CLIENTS; ++i) { - auto client = clients[i]; + check_for_new_clients(&tncServer, clients, MAX_WIFI_CLIENTS); - if (client != nullptr){ - Serial.println(String("Client #") +String(i) + ": " + client->remoteIP().toString() + ":" + client->remotePort()); - } - } - #endif - - - if (!new_client_handled){ - #ifdef ENABLE_WIFI_CLIENT_DEBUG - Serial.println(String("Refusing client ")); - #endif - new_client.stop(); - } - } iterateWifiClients([](WiFiClient * client, int clientIdx, const String * unused){ while (client->available() > 0) { char character = client->read(); - handleKISSData(character, 2+clientIdx); + handleKISSData(character, 1); } - }, nullptr); - + }, nullptr, clients, MAX_WIFI_CLIENTS); #endif + if (xQueueReceive(tncReceivedQueue, &loraReceivedFrameString, (1 / portTICK_PERIOD_MS)) == pdPASS) { + bool isPacketFrame = false; + const String &kissEncoded_pkt = encode_kiss_pkt(*loraReceivedFrameString, isPacketFrame); const String &kissEncoded = encode_kiss(*loraReceivedFrameString); + if (isPacketFrame) { + Serial.print(kissEncoded_pkt); + #ifdef ENABLE_BLUETOOTH + if (SerialBT.hasClient()){ + SerialBT.print(kissEncoded_pkt); + } + #endif + #ifdef ENABLE_WIFI + iterateWifiClients([](WiFiClient *client, int clientIdx, const String *data){ + if (client->connected()){ + client->print(*data); + client->flush(); + } + }, &kissEncoded_pkt, clients, MAX_WIFI_CLIENTS); + #endif + } + else + { Serial.print(kissEncoded); #ifdef ENABLE_BLUETOOTH if (SerialBT.hasClient()){ @@ -158,9 +134,9 @@ void handleKISSData(char character, int bufferIndex) { client->print(*data); client->flush(); } - }, &kissEncoded); + }, &kissEncoded, clients, MAX_WIFI_CLIENTS); #endif - + } delete loraReceivedFrameString; } vTaskDelay(50 / portTICK_PERIOD_MS); diff --git a/src/taskWebServer.cpp b/src/taskWebServer.cpp index a960226..4842adc 100644 --- a/src/taskWebServer.cpp +++ b/src/taskWebServer.cpp @@ -2,6 +2,7 @@ #include "taskWebServer.h" #include "preference_storage.h" #include "syslog_log.h" +#include "PSRAMJsonDocument.h" #include #include @@ -25,6 +26,7 @@ WebServer server(80); #ifdef KISS_PROTOCOL WiFiServer tncServer(NETWORK_TNC_PORT); #endif +WiFiServer gpsServer(NETWORK_GPS_PORT); #ifdef ENABLE_SYSLOG // A UDP instance to let us send and receive packets over UDP @@ -34,6 +36,11 @@ WebServer server(80); Syslog syslog(udpClient, SYSLOG_PROTO_IETF); #endif +#ifdef T_BEAM_V1_0 + #include + extern AXP20X_Class axp; +#endif + void sendCacheHeader() { server.sendHeader("Cache-Control", "max-age=3600"); } void sendGzipHeader() { server.sendHeader("Content-Encoding", "gzip"); } @@ -121,9 +128,20 @@ void handle_SaveWifiCfg() { void handle_Reboot() { server.sendHeader("Location", "/"); server.send(302,"text/html", ""); + server.close(); ESP.restart(); } +void handle_Shutdown() { + + #ifdef T_BEAM_V1_0 + server.send(200,"text/html", "Shutdown"); + axp.shutdown(); + #else + server.send(404,"text/html", "Not supported"); + #endif +} + void handle_Restore() { server.sendHeader("Location", "/"); server.send(302,"text/html", ""); @@ -163,7 +181,9 @@ void handle_Cfg() { jsonData += jsonLineFromPreferenceInt(PREF_DEV_AUTO_SHUT_PRESET); jsonData += jsonLineFromInt("FreeHeap", ESP.getFreeHeap()); jsonData += jsonLineFromInt("HeapSize", ESP.getHeapSize()); - jsonData += jsonLineFromInt("FreeSketchSpace", ESP.getFreeSketchSpace(), true); + jsonData += jsonLineFromInt("FreeSketchSpace", ESP.getFreeSketchSpace()); + jsonData += jsonLineFromInt("PSRAMSize", ESP.getPsramSize()); + jsonData += jsonLineFromInt("PSRAMFree", ESP.getFreePsram(), true); jsonData += "}"; server.send(200,"application/json", jsonData); @@ -247,7 +267,7 @@ void handle_saveDeviceCfg(){ } if (server.hasArg(PREF_APRS_FREQ)){ preferences.putDouble(PREF_APRS_FREQ, server.arg(PREF_APRS_FREQ).toDouble()); - } + } if (server.hasArg(PREF_APRS_MODE)){ preferences.putInt(PREF_APRS_MODE, server.arg(PREF_APRS_MODE).toInt()); } @@ -269,13 +289,14 @@ void handle_saveDeviceCfg(){ server.on("/scan_wifi", handle_ScanWifi); server.on("/save_wifi_cfg", handle_SaveWifiCfg); server.on("/reboot", handle_Reboot); + server.on("/shutdown", handle_Shutdown); server.on("/cfg", handle_Cfg); server.on("/received_list", handle_ReceivedList); server.on("/save_aprs_cfg", handle_SaveAPRSCfg); server.on("/save_device_cfg", handle_saveDeviceCfg); server.on("/restore", handle_Restore); server.on("/update", HTTP_POST, []() { - syslog_log(LOG_WARNING, String("Update finished. Status: ") + (Update.hasError() ? "Ok" : "Error")); + syslog_log(LOG_WARNING, String("Update finished. Status: ") + (!Update.hasError() ? "Ok" : "Error")); server.sendHeader("Connection", "close"); server.send(200, "text/plain", (Update.hasError()) ? "FAIL" : "OK"); delay(500); @@ -359,6 +380,7 @@ void handle_saveDeviceCfg(){ #ifdef KISS_PROTOCOL tncServer.begin(); #endif + gpsServer.begin(); if (MDNS.begin(webServerCfg->callsign.c_str())) { MDNS.setInstanceName(webServerCfg->callsign + " LoRa APRS TNC "); MDNS.addService("http", "tcp", 80); diff --git a/src/wifi_clients.cpp b/src/wifi_clients.cpp new file mode 100644 index 0000000..8993a11 --- /dev/null +++ b/src/wifi_clients.cpp @@ -0,0 +1,64 @@ +// +// Created by Admin on 11.06.2021. +// + +#ifdef ENABLE_WIFI + +#include + +#include "wifi_clients.h" + +void iterateWifiClients(f_connectedClientCallback_t callback, const String *data, WiFiClient * wifiClients[], int maxWifiClients){ + for (int i=0; i < maxWifiClients; i++) { + auto client = wifiClients[i]; + if (client != nullptr) { + if (client->connected()) { + callback(client, i, data); + } else { + #ifdef ENABLE_WIFI_CLIENT_DEBUG + Serial.println(String("Disconnected client ") + client->remoteIP().toString() + ":" + client->remotePort()); + #endif + delete client; + wifiClients[i] = nullptr; + } + } + } +} + +void check_for_new_clients(WiFiServer *wiFiServer, WiFiClient *wifiClients[], int maxWifiClients) { + WiFiClient new_client = wiFiServer->available(); + if (new_client.connected()){ + bool new_client_handled = false; + for (int i=0; i < maxWifiClients; i++) { + auto client = wifiClients[i]; + if (client == nullptr) { + client = new WiFiClient(new_client); + wifiClients[i] = client; + new_client_handled = true; + #ifdef ENABLE_WIFI_CLIENT_DEBUG + Serial.println(String("New client #") +String(i) + ": " + client->remoteIP().toString() + ":" + client->remotePort()); + #endif + break; + } + } + #ifdef ENABLE_WIFI_CLIENT_DEBUG + for (int i = 0; i < maxWifiClients; ++i) { + auto client = clients[i]; + + if (client != nullptr){ + Serial.println(String("Client #") +String(i) + ": " + client->remoteIP().toString() + ":" + client->remotePort()); + } + } + #endif + + + if (!new_client_handled){ + #ifdef ENABLE_WIFI_CLIENT_DEBUG + Serial.println(String("Refusing client ")); + #endif + new_client.stop(); + } + } +} + +#endif diff --git a/tools/buildscript_versioning.py b/tools/buildscript_versioning.py index 06e6d89..d4fdc86 100644 --- a/tools/buildscript_versioning.py +++ b/tools/buildscript_versioning.py @@ -1,6 +1,6 @@ FILENAME_BUILDNO = '.pio/versioning' FILENAME_VERSION_H = 'include/version.h' -version = 'v0.3.1.1.' +version = 'v0.4.0.' import datetime from subprocess import *