FULL KISS PACKEt MODE

KISS_TNC
US1GHQ 2021-09-06 20:15:00 +03:00
parent c29cf2c1f5
commit 1f4c38f71e
18 changed files with 630 additions and 278 deletions

View File

@ -1,4 +1,4 @@
# LoRa tracker with KISS TNC2 capability # LoRa tracker with KISS TNC capability
Tracker can be used on its own. Tracker can be used on its own.
You can also connect it via blueetooth with APRSdroid. You can also connect it via blueetooth with APRSdroid.

View File

@ -227,12 +227,17 @@
</div> </div>
<article> <article>
<div class="grid-container quarters"> <div class="grid-container quarters">
<form action="/reboot" method="post"> <form action="/shutdown" method="post" onsubmit="return confirmAction('shutdown');">
<div>
<input class="button-primary" type="submit" value="Shutdown">
</div>
</form>
<form action="/reboot" method="post" onsubmit="return confirmAction('reboot');">
<div> <div>
<input class="button-primary" type="submit" value="Reboot"> <input class="button-primary" type="submit" value="Reboot">
</div> </div>
</form> </form>
<form action="/restore" method="post"> <form action="/restore" method="post" onsubmit="return confirmAction('do factory reset');">
<div> <div>
<input class="button-primary" type="submit" value="Factory reset"> <input class="button-primary" type="submit" value="Factory reset">
</div> </div>

View File

@ -113,4 +113,8 @@ function updateFileUpload(event) {
xhr.open('POST', '/update'); xhr.open('POST', '/update');
xhr.send(data); xhr.send(data);
}
function confirmAction(actionName) {
return window.confirm('Are you shure want to ' + actionName);
} }

View File

@ -2,4 +2,5 @@
#include <TinyGPS++.h> #include <TinyGPS++.h>
extern TinyGPSPlus gps; extern TinyGPSPlus gps;
void taskGPS(void *parameter);
[[noreturn]] void taskGPS(void *parameter);

View File

@ -7,6 +7,7 @@
#endif #endif
#if defined(ENABLE_WIFI) #if defined(ENABLE_WIFI)
#include "taskWebServer.h" #include "taskWebServer.h"
#include "wifi_clients.h"
#endif #endif
extern QueueHandle_t tncToSendQueue; extern QueueHandle_t tncToSendQueue;
extern QueueHandle_t tncReceivedQueue; extern QueueHandle_t tncReceivedQueue;

View File

@ -12,6 +12,7 @@ extern BG_RF95 rf95;
#ifdef KISS_PROTOCOL #ifdef KISS_PROTOCOL
extern WiFiServer tncServer; extern WiFiServer tncServer;
#endif #endif
extern WiFiServer gpsServer;
typedef struct { typedef struct {
String callsign; String callsign;
} tWebServerCfg; } tWebServerCfg;

View File

@ -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 <WiFiClient.h>
#include <WiFiServer.h>
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

View File

@ -116,7 +116,7 @@ bool BG_RF95::init()
// An innocuous ISM frequency, same as RF22's // An innocuous ISM frequency, same as RF22's
setFrequency(433.800); setFrequency(433.800);
// Lowish power // Lowish power
setTxPower(20); setTxPower(23);
return true; return true;
} }
@ -392,7 +392,7 @@ void BG_RF95::setTxPower(int8_t power, bool useRFO)
// for the transmitter output // for the transmitter output
if (useRFO) if (useRFO)
{ {
if (power > 14) power = 14; if (power > 19) power = 19;
if (power < -1) power = -1; if (power < -1) power = -1;
spiWrite(BG_RF95_REG_09_PA_CONFIG, BG_RF95_MAX_POWER | (power + 1)); spiWrite(BG_RF95_REG_09_PA_CONFIG, BG_RF95_MAX_POWER | (power + 1));
} else { } 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_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); spiWrite(BG_RF95_REG_4D_PA_DAC, BG_RF95_PA_DAC_ENABLE);
//power -= 3; //power -= 3;
power = 20; // and set PA_DAC_ENABLE power = 23; // and set PA_DAC_ENABLE
} else { } else {
spiWrite(BG_RF95_REG_4D_PA_DAC, BG_RF95_PA_DAC_DISABLE); spiWrite(BG_RF95_REG_4D_PA_DAC, BG_RF95_PA_DAC_DISABLE);

View File

@ -7,7 +7,7 @@ String decode_address_ax25(const String &ax25Address, bool &isLast, bool isRelay
bool validateKISSFrame(const String &kissFormattedFrame); bool validateKISSFrame(const String &kissFormattedFrame);
String decapsulateKISS(const String& frame); String decapsulateKISS(const String &frame);
/* /*
* https://ham.zmailer.org/oh2mqk/aprx/PROTOCOLS * https://ham.zmailer.org/oh2mqk/aprx/PROTOCOLS
@ -33,100 +33,188 @@ String decapsulateKISS(const String& frame);
*/ */
String encode_kiss(const String& tnc2FormattedFrame) { String encode_kiss(const String &tnc2FormattedFrame)
String ax25Frame = ""; {
String ax25Frame = "";
if (validateTNC2Frame(tnc2FormattedFrame)){ char control_id = 0x03;
String address = ""; if (validateTNC2Frame(tnc2FormattedFrame)) {
bool dst_addres_written = false; String address = "";
for (int p=0;p<=tnc2FormattedFrame.indexOf(':');p++){ bool dst_addres_written = false;
char currentChar = tnc2FormattedFrame.charAt(p); for (int p = 0; p <= tnc2FormattedFrame.indexOf(':'); p++)
if (currentChar == ':' || currentChar == '>' || currentChar == ','){ {
if (!dst_addres_written && (currentChar == ',' || currentChar == ':')){ char currentChar = tnc2FormattedFrame.charAt(p);
// ax25 frame DST SRC if (currentChar == ':' || currentChar == '>' || currentChar == ',')
// tnc2 frame SRC DST {
ax25Frame = encode_address_ax25(address) + ax25Frame; if (!dst_addres_written && (currentChar == ',' || currentChar == ':'))
dst_addres_written = true; {
} else { // ax25 frame DST SRC
ax25Frame += encode_address_ax25(address); // tnc2 frame SRC DST
} ax25Frame = encode_address_ax25(address) + ax25Frame;
address = ""; dst_addres_written = true;
} else {
address += currentChar;
}
} }
auto lastAddressChar = (uint8_t)ax25Frame.charAt(ax25Frame.length() - 1); else
ax25Frame.setCharAt(ax25Frame.length() - 1, (char)(lastAddressChar | IS_LAST_ADDRESS_POSITION_MASK)); {
ax25Frame += (char)APRS_CONTROL_FIELD; ax25Frame += encode_address_ax25(address);
ax25Frame += (char)APRS_INFORMATION_FIELD; }
ax25Frame += tnc2FormattedFrame.substring(tnc2FormattedFrame.indexOf(':')+1); address = "";
}
else
{
address += currentChar;
}
} }
//Parse APRS CONTROL ID
if (tnc2FormattedFrame.lastIndexOf("SABM") > 1) { control_id = 0x2f; }
else if (tnc2FormattedFrame.lastIndexOf("DISC") > 1) { control_id = 0x43; }
else if (tnc2FormattedFrame.lastIndexOf("DM") > 1) { control_id = 0x0f; }
else if (tnc2FormattedFrame.lastIndexOf("UA") > 1) { control_id = 0x63; }
else if (tnc2FormattedFrame.lastIndexOf("FRMR") > 1) { control_id = 0x87; }
else if (tnc2FormattedFrame.lastIndexOf("XID") > 1) { control_id = 0xaf; }
else if (tnc2FormattedFrame.lastIndexOf("TEST") > 1) { control_id = 0xe3; }
else if (tnc2FormattedFrame.lastIndexOf("RR") > 1) { control_id = 0x01; }
else if (tnc2FormattedFrame.lastIndexOf("II") > 1) { control_id = 0xf0; }
else { control_id = 0x03; }
//END
auto lastAddressChar = (uint8_t) ax25Frame.charAt(ax25Frame.length() - 1);
ax25Frame.setCharAt(ax25Frame.length() - 1, (char) (lastAddressChar | IS_LAST_ADDRESS_POSITION_MASK));
ax25Frame += (char) control_id;
ax25Frame += (char) APRS_INFORMATION_FIELD;
ax25Frame += tnc2FormattedFrame.substring(tnc2FormattedFrame.indexOf(':') + 1);
}
String kissFrame = encapsulateKISS(ax25Frame, CMD_DATA); String kissFrame = encapsulateKISS(ax25Frame, CMD_DATA);
return kissFrame; return kissFrame;
} }
String encapsulateKISS(const String &ax25Frame, uint8_t TNCCmd) { String encapsulateKISS(const String &ax25Frame, uint8_t TNCCmd)
String kissFrame = ""; {
kissFrame += (char)FEND; // start of frame String kissFrame = "";
kissFrame += (char)(0x0f & TNCCmd); // TNC0, cmd kissFrame += (char) FEND; // start of frame
for (int i = 0; i < ax25Frame.length(); ++i) { kissFrame += (char) (0x0f & TNCCmd); // TNC0, cmd
char currentChar = ax25Frame.charAt(i); for (int i = 0; i < ax25Frame.length(); ++i)
if (currentChar == (char)FEND){ {
kissFrame += (char)FESC; char currentChar = ax25Frame.charAt(i);
kissFrame += (char)TFEND; if (currentChar == (char) FEND)
} else if (currentChar == (char)FESC){ {
kissFrame += (char)FESC; kissFrame += (char) FESC;
kissFrame += (char)TFESC; kissFrame += (char) TFEND;
} else {
kissFrame += currentChar;
}
} }
kissFrame += (char)FEND; // end of frame else if (currentChar == (char) FESC)
return kissFrame; {
kissFrame += (char) FESC;
kissFrame += (char) TFESC;
}
else
{
kissFrame += currentChar;
}
}
kissFrame += (char) FEND; // end of frame
return kissFrame;
} }
String decapsulateKISS(const String& frame) { String decapsulateKISS(const String &frame)
String ax25Frame = ""; {
for (int i = 2; i < frame.length() - 1; ++i) { String ax25Frame = "";
char currentChar = frame.charAt(i); for (int i = 2; i < frame.length() - 1; ++i)
if (currentChar == (char)FESC){ {
char nextChar = frame.charAt(i+1); char currentChar = frame.charAt(i);
if (nextChar == (char)TFEND){ if (currentChar == (char) FESC)
ax25Frame += (char)FEND; {
} else if (nextChar == (char)TFESC){ char nextChar = frame.charAt(i + 1);
ax25Frame += (char)FESC; if (nextChar == (char) TFEND)
} {
i++; ax25Frame += (char) FEND;
} else { }
ax25Frame += currentChar; else if (nextChar == (char) TFESC)
} {
ax25Frame += (char) FESC;
}
i++;
}
else
{
ax25Frame += currentChar;
} }
}
return ax25Frame; return ax25Frame;
} }
String decode_kiss(const String& kissFormattedFrame) { /**
String TNC2Frame = ""; *
* @param inputKISSTNCFrame
if (validateKISSFrame(kissFormattedFrame)){ * @param dataFrame
String ax25Frame = decapsulateKISS(kissFormattedFrame); * @return Decapsulated TNC2KISS APRS data frame, or raw command data frame
bool isLast = false; */
String dst_addr = decode_address_ax25(ax25Frame.substring(0, 7), isLast, false); String decode_kiss(const String &inputKISSTNCFrame, bool &dataFrame) {
String src_addr = decode_address_ax25(ax25Frame.substring(7, 14), isLast, false); String TNC2Frame = "";
TNC2Frame = src_addr + ">" + dst_addr; String control_f = "";
int digi_info_index = 14; //UI-FRAMES
while (!isLast && digi_info_index + 7 < ax25Frame.length()){ bool ui_sabm = false;
String digi_addr = decode_address_ax25(ax25Frame.substring(digi_info_index, digi_info_index + 7), isLast, true); bool ui_disc = false;
TNC2Frame += ',' + digi_addr; bool ui_dm = false;
digi_info_index += 7; bool ui_ua = false;
} bool ui_frmr = false;
TNC2Frame += ':'; bool ui_xid = false;
TNC2Frame += ax25Frame.substring(digi_info_index + 2); bool ui_test = false;
//S-FRAMES
bool us_rr = false;
//I-Frame
bool ui_i = false;
//END FRAMES
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;
}
//parse for conrol frame
for (int i = 16; i < inputKISSTNCFrame.length() - 1; ++i)
{
ui_sabm = inputKISSTNCFrame.charAt(i) == U_SABM;
ui_disc = inputKISSTNCFrame.charAt(i) == U_DISC;
ui_dm = inputKISSTNCFrame.charAt(i) == U_DM;
ui_ua = inputKISSTNCFrame.charAt(i) == U_UA;
ui_frmr = inputKISSTNCFrame.charAt(i) == U_FRMR;
ui_xid = inputKISSTNCFrame.charAt(i) == U_XID;
ui_test = inputKISSTNCFrame.charAt(i) == U_TEST;
//S-Frame TEST
us_rr = inputKISSTNCFrame.charAt(i) == S_RR;
//I-Frame TEST
ui_i = inputKISSTNCFrame.charAt(i) == I_I;
}
if (ui_sabm) { control_f = "<SABM>"; }
else if (ui_disc) { control_f = "<DISC>"; }
else if (ui_dm) { control_f = "<DM>"; }
else if (ui_ua) { control_f = "<UA>"; }
else if (ui_frmr) { control_f = "<FRMR>"; }
else if (ui_xid) { control_f = "<XID>"; }
else if (ui_test) { control_f = "<TEST>"; }
else if (us_rr) { control_f = "<RR>"; }
else if (ui_i) { control_f = "<II>"; }
else { control_f = ' '; }
//control frames
TNC2Frame += ':';
TNC2Frame += control_f;
//END
TNC2Frame += ax25Frame.substring(digi_info_index + 2);
}
else
{
// command frame, currently ignored
TNC2Frame += inputKISSTNCFrame;
} }
}
return TNC2Frame; return TNC2Frame;
} }
/** /**
@ -135,32 +223,32 @@ String decode_kiss(const String& kissFormattedFrame) {
* @return * @return
*/ */
String encode_address_ax25(String tnc2Address) { String encode_address_ax25(String tnc2Address) {
bool hasBeenDigipited = tnc2Address.indexOf('*') != -1; bool hasBeenDigipited = tnc2Address.indexOf('*') != -1;
if (tnc2Address.indexOf('-') == -1){ if (tnc2Address.indexOf('-') == -1) {
if (hasBeenDigipited){ if (hasBeenDigipited) {
// ex. TCPIP* in tnc2Address // ex. TCPIP* in tnc2Address
// so we skip last char // so we skip last char
tnc2Address = tnc2Address.substring(0, tnc2Address.length()-1); tnc2Address = tnc2Address.substring(0, tnc2Address.length() - 1);
}
tnc2Address += "-0";
} }
tnc2Address += "-0";
}
int separatorIndex = tnc2Address.indexOf('-'); int separatorIndex = tnc2Address.indexOf('-');
int ssid = tnc2Address.substring(separatorIndex + 1).toInt(); int ssid = tnc2Address.substring(separatorIndex + 1).toInt();
// TODO: SSID should not be > 16 // TODO: SSID should not be > 16
String kissAddress = ""; String kissAddress = "";
for (int i = 0; i < 6; ++i) { for (int i = 0; i < 6; ++i) {
char addressChar; char addressChar;
if (tnc2Address.length() > i && i < separatorIndex){ if (tnc2Address.length() > i && i < separatorIndex) {
addressChar = tnc2Address.charAt(i); addressChar = tnc2Address.charAt(i);
} else { } else {
addressChar = ' '; addressChar = ' ';
}
kissAddress += (char)(addressChar << 1);
} }
kissAddress += (char)((ssid << 1) | 0b01100000 | (hasBeenDigipited ? HAS_BEEN_DIGIPITED_MASK : 0)); kissAddress += (char) (addressChar << 1);
return kissAddress; }
kissAddress += (char) ((ssid << 1) | 0b01100000 | (hasBeenDigipited ? HAS_BEEN_DIGIPITED_MASK : 0));
return kissAddress;
} }
/** /**
@ -169,30 +257,36 @@ String encode_address_ax25(String tnc2Address) {
* @return * @return
*/ */
String decode_address_ax25(const String &ax25Address, bool &isLast, bool isRelay) { String decode_address_ax25(const String &ax25Address, bool &isLast, bool isRelay) {
String TNCAddress = ""; String TNCAddress = "";
for (int i = 0; i < 6; ++i) { for (int i = 0; i < 6; ++i) {
uint8_t currentCharacter = ax25Address.charAt(i); uint8_t currentCharacter = ax25Address.charAt(i);
currentCharacter >>= 1; currentCharacter >>= 1;
if (currentCharacter != ' '){ if (currentCharacter != ' ') {
TNCAddress += (char)currentCharacter; TNCAddress += (char) currentCharacter;
}
} }
auto ssid_char = (uint8_t) ax25Address.charAt(6); }
bool hasBeenDigipited = ssid_char & HAS_BEEN_DIGIPITED_MASK; auto ssid_char = (uint8_t) ax25Address.charAt(6);
isLast = ssid_char & IS_LAST_ADDRESS_POSITION_MASK; bool hasBeenDigipited = ssid_char & HAS_BEEN_DIGIPITED_MASK;
ssid_char >>= 1; isLast = ssid_char & IS_LAST_ADDRESS_POSITION_MASK;
ssid_char >>= 1;
int ssid = 0b1111 & ssid_char; int ssid = 0b1111 & ssid_char;
if (ssid){ if (ssid) {
TNCAddress += '-'; TNCAddress += '-';
TNCAddress += ssid; TNCAddress += ssid;
} }
if (isRelay && hasBeenDigipited){ if (isRelay && hasBeenDigipited) {
TNCAddress += '*'; TNCAddress += '*';
} }
return 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;
}

View File

@ -1,13 +1,31 @@
#include <Arduino.h> #include <Arduino.h>
#include "KISS.h" #include "KISS.h"
#define APRS_CONTROL_FIELD 0x03 #define APRS_CONTROL_FIELD 0xf0
#define APRS_INFORMATION_FIELD 0xf0 #define APRS_INFORMATION_FIELD 0xf0
#define HAS_BEEN_DIGIPITED_MASK 0b10000000 #define HAS_BEEN_DIGIPITED_MASK 0b10000000
#define IS_LAST_ADDRESS_POSITION_MASK 0b1 #define IS_LAST_ADDRESS_POSITION_MASK 0b1
//Data control frames, ported from Direwolf
//U-Frames
#define U_SABME 0x6f
#define U_SABM 0x3f //ok
#define U_DISC 0x53 //ok
#define U_DM 0x1f //need test
#define U_UA 0x73 //ok
#define U_FRMR 0x97 //need test
#define U_UI 0x13 //need test
#define U_XID 0xbf //need test
#define U_TEST 0xf3 //need test
//S-Frames
#define S_RR 0x11 //need test
#define S_RNR 0x15 //need test
#define S_REJ 0x19 //need test
#define S_SREJ 0x1d //need test
//I-Frame
#define I_I 0xff //need test
//END
String encode_kiss(const String& tnc2FormattedFrame); 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); String encapsulateKISS(const String &ax25Frame, uint8_t TNCCmd);

View File

@ -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 <ArduinoJson.h>
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<SpiRamAllocator>;
#endif //TTGO_T_BEAM_LORA_APRS_PSRAMJSONDOCUMENT_H

View File

@ -25,37 +25,42 @@ lib_deps =
Adafruit SSD1306 Adafruit SSD1306
Adafruit GFX Library Adafruit GFX Library
Adafruit Unified Sensor Adafruit Unified Sensor
adafruit/Adafruit BMP280 Library @ 2.4.0
https://github.com/SQ9MDD/AXP202X_Library.git https://github.com/SQ9MDD/AXP202X_Library.git
SparkFun u-blox Arduino Library SparkFun u-blox Arduino Library
bblanchon/ArduinoJson bblanchon/ArduinoJson
DallasTemperature
build_flags = build_flags =
-Wl,--gc-sections,--relax -Wl,--gc-sections,--relax
-D 'KISS_PROTOCOL' -D 'KISS_PROTOCOL' ; leave enabled
-D 'CALLSIGN="N0CALL-0"' -D 'CALLSIGN="NOCALL-0"' ; can be set from www interfeace
-D 'DIGI_PATH="ECHO"' -D 'DIGI_PATH="ECHO"' ; can be set from www interfeace
-D 'FIXED_BEACON_EN' -D 'FIXED_BEACON_EN' ; can be set from www interfeace
-D 'LATIDUDE_PRESET="0000.00N"' -D 'LATIDUDE_PRESET="0000.00N"' ; can be set from www interfeace
-D 'LONGITUDE_PRESET="00000.00E"' -D 'LONGITUDE_PRESET="00000.00E"' ; can be set from www interfeace
-D 'APRS_SYMBOL_TABLE="/"' -D 'APRS_SYMBOL_TABLE="/"' ; can be set from www interfeace
-D 'APRS_SYMBOL="["' -D 'APRS_SYMBOL="["' ; can be set from www interfeace
-D 'MY_COMMENT="Lora Tracker"' -D 'MY_COMMENT="Lora Tracker"' ; can be set from www interfeace
-D 'SHOW_ALT' -D 'SHOW_ALT' ; can be set from www interfeace
-D 'SHOW_BATT' -D 'SHOW_BATT' ; can be set from www interfeace
-D 'SHOW_RX_PACKET' -D 'SHOW_RX_PACKET' ; can be set from www interfeace
-D 'SHOW_RX_TIME=10000' -D 'SHOW_RX_TIME=10000' ; can be set from www interfeace
-D 'TXFREQ="433.775"' -D 'TXFREQ=433.775' ; set operating frequency
; -D 'SPEED_1200' ; comment out to set 300baud -D 'SPEED_1200' ; comment out to set 300baud
-D 'TXdbmW=20' -D 'TXdbmW=20' ; set power
-D 'ENABLE_OLED' ; -D 'ENABLE_OLED' ; can be set from www interfeace
-D 'ENABLE_LED_SIGNALING' -D 'ENABLE_LED_SIGNALING' ; can be set from www interfeace
-D 'NETWORK_TNC_PORT=8001' -D 'NETWORK_TNC_PORT=8001' ; default KISS TCP port
-D 'MAX_TIME_TO_NEXT_TX=360000L' -D 'NETWORK_GPS_PORT=10110' ; GPS NMEA Port
-D 'FIX_BEACON_INTERVAL=1800000L' -D 'MAX_TIME_TO_NEXT_TX=360000L' ; can be set from www interfeace
; -D 'RSSI_SNR_REPORT' ; RSSI and snr report in the KISS PACKET -D 'FIX_BEACON_INTERVAL=1800000L' ; can be set from www interfeace
; -D 'TX_RX_LNA' ; Set external pins for 1W modules ; -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] [env:ttgo-t-beam-v1.0]
platform = espressif32 @ 3.3.0 platform = espressif32 @ 3.3.1
board = ttgo-t-beam board = ttgo-t-beam
build_flags = build_flags =
${env.build_flags} ${env.build_flags}
@ -64,7 +69,7 @@ build_flags =
; -D ENABLE_BLUETOOTH ; -D ENABLE_BLUETOOTH
[env:ttgo-t-beam-v0.7] [env:ttgo-t-beam-v0.7]
platform = espressif32 @ 3.3.0 platform = espressif32 @ 3.3.1
board = ttgo-t-beam board = ttgo-t-beam
build_flags = build_flags =
${env.build_flags} ${env.build_flags}
@ -73,7 +78,7 @@ build_flags =
-D T_BEAM_V0_7 -D T_BEAM_V0_7
[env:ttgo-lora32-v2.1] [env:ttgo-lora32-v2.1]
platform = espressif32 @ 3.3.0 platform = espressif32 @ 3.3.1
board = ttgo-lora32-v21 board = ttgo-lora32-v21
build_flags = build_flags =
${env.build_flags} ${env.build_flags}
@ -82,7 +87,7 @@ build_flags =
-D LORA32_21 -D LORA32_21
[env:ttgo-lora32-v2] [env:ttgo-lora32-v2]
platform = espressif32 @ 3.3.0 platform = espressif32 @ 3.3.1
board = ttgo-lora32-v2 board = ttgo-lora32-v2
build_flags = build_flags =
${env.build_flags} ${env.build_flags}
@ -91,7 +96,7 @@ build_flags =
-D LORA32_2 -D LORA32_2
[env:ttgo-lora32-v1] [env:ttgo-lora32-v1]
platform = espressif32 @ 3.3.0 platform = espressif32 @ 3.3.1
board = ttgo-lora32-v1 board = ttgo-lora32-v1
build_flags = build_flags =
${env.build_flags} ${env.build_flags}
@ -100,7 +105,7 @@ build_flags =
-D LORA32_1 -D LORA32_1
[env:Heltec-WiFi-v1] [env:Heltec-WiFi-v1]
platform = espressif32 @ 3.3.0 platform = espressif32 @ 3.3.1
board = heltec_wifi_kit_32 board = heltec_wifi_kit_32
build_flags = build_flags =
${env.build_flags} ${env.build_flags}
@ -109,7 +114,7 @@ build_flags =
-D HELTEC_V1 -D HELTEC_V1
[env:Heltec-WiFi-v2] [env:Heltec-WiFi-v2]
platform = espressif32 @ 3.3.0 platform = espressif32 @ 3.3.1
board = heltec_wifi_kit_32_v2 board = heltec_wifi_kit_32_v2
board_build.f_cpu = 80000000L board_build.f_cpu = 80000000L
build_flags = build_flags =
@ -119,14 +124,14 @@ build_flags =
-D HELTEC_V2 -D HELTEC_V2
[env:Esp32-Dev-v1] [env:Esp32-Dev-v1]
platform = espressif32 @ 3.3.0 platform = espressif32 @ 3.3.1
board = esp32dev board = esp32dev
; change MCU frequency ; change MCU frequency
;board_build.f_cpu = 80000000L ;board_build.f_cpu = 80000000L
build_flags = build_flags =
${env.build_flags} ${env.build_flags}
; -D ENABLE_WIFI -D ENABLE_WIFI
-D ENABLE_BLUETOOTH ; -D ENABLE_BLUETOOTH
-D ESP32_DEV_V1 -D ESP32_DEV_V1
[env:ttgo-t-beam-v1.0-development] [env:ttgo-t-beam-v1.0-development]
@ -139,7 +144,7 @@ build_flags =
-D ENABLE_BLUETOOTH -D ENABLE_BLUETOOTH
-D ENABLE_SYSLOG -D ENABLE_SYSLOG
-D 'SYSLOG_IP="192.168.0.102"' -D 'SYSLOG_IP="192.168.0.102"'
-D DEVELOPMENT_DEBUG -D DEVELOPMENT_DEBUG
lib_deps = lib_deps =
${env.lib_deps} ${env.lib_deps}
arcao/Syslog arcao/Syslog

View File

@ -32,6 +32,10 @@
#include "taskWebServer.h" #include "taskWebServer.h"
#endif #endif
#ifdef USE_BME280
#include <Adafruit_BMP280.h> // BME280 Library
#endif
// oled address // oled address
#define SSD1306_ADDRESS 0x3C #define SSD1306_ADDRESS 0x3C
@ -114,9 +118,14 @@ String aprsComment = MY_COMMENT;
String aprsLatPreset = LATIDUDE_PRESET; String aprsLatPreset = LATIDUDE_PRESET;
String aprsLonPreset = LONGITUDE_PRESET; String aprsLonPreset = LONGITUDE_PRESET;
//temporary hack for setfreq //Set params
String aprsFreq = TXFREQ; double aprsFreq = TXFREQ;
String aprsMode = "1200";
#ifdef SPEED_1200
ulong aprsMode = 1200;
#else
ulong aprsMode = 300;
#endif
boolean gps_state = true; boolean gps_state = true;
boolean key_up = true; boolean key_up = true;
@ -144,6 +153,11 @@ boolean show_cmt = true;
#else #else
boolean enabled_oled = false; boolean enabled_oled = false;
#endif #endif
#ifdef USE_BME280
boolean enable_bme280 = true;
#else
boolean enable_bme280 = false;
#endif
// Variables and Constants // Variables and Constants
String loraReceivedFrameString = ""; //data on buff is copied to this string String loraReceivedFrameString = ""; //data on buff is copied to this string
@ -198,7 +212,7 @@ ulong time_delay = 0;
ulong shutdown_delay = 0; ulong shutdown_delay = 0;
ulong shutdown_delay_time = 10000; ulong shutdown_delay_time = 10000;
ulong shutdown_countdown_timer = 0; ulong shutdown_countdown_timer = 0;
boolean shutdown_active =true; boolean shutdown_active =false;
boolean shutdown_countdown_timer_enable = false; boolean shutdown_countdown_timer_enable = false;
boolean shutdown_usb_status_bef = false; boolean shutdown_usb_status_bef = false;
#define ANGLE 60 // angle to send packet at smart beaconing #define ANGLE 60 // angle to send packet at smart beaconing
@ -206,6 +220,16 @@ boolean shutdown_usb_status_bef = false;
float average_course[ANGLE_AVGS]; float average_course[ANGLE_AVGS];
float avg_c_y, avg_c_x; 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; uint8_t txPower = TXdbmW;
#ifdef ENABLE_WIFI #ifdef ENABLE_WIFI
@ -234,6 +258,10 @@ uint8_t loraReceivedLength = sizeof(lora_RXBUFF);
#else #else
#define OLED_RESET 16 #define OLED_RESET 16
#endif #endif
//Temp sensor
#ifdef USE_BME280
Adafruit_BMP280 bme; // if BME is used
#endif
Adafruit_SSD1306 display(128, 64, &Wire, OLED_RESET); Adafruit_SSD1306 display(128, 64, &Wire, OLED_RESET);
@ -303,12 +331,48 @@ void prepareAPRSFrame(){
} }
outString += Talt; outString += Talt;
} }
}else{ //fixed position not compresed }
else
{
//fixed position not compresed
outString += aprsLatPreset; outString += aprsLatPreset;
outString += aprsSymbolTable; outString += aprsSymbolTable;
outString += aprsLonPreset; outString += aprsLonPreset;
outString += aprsSymbol; 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){ if(show_cmt){
outString += aprsComment; outString += aprsComment;
@ -358,7 +422,7 @@ void sendpacket(){
* @param aprsFreq * @param aprsFreq
* @param message * @param message
*/ */
void loraSend(byte txPower, String aprsFreq, const String &message) { void loraSend(byte txPower, float aprsFreq, const String &message) {
#ifdef TX_RX_LNA #ifdef TX_RX_LNA
digitalWrite(TXPIN, HIGH); digitalWrite(TXPIN, HIGH);
digitalWrite(RXPIN, LOW); digitalWrite(RXPIN, LOW);
@ -371,13 +435,16 @@ void loraSend(byte txPower, String aprsFreq, const String &message) {
int messageSize = min(message.length(), sizeof(lora_TXBUFF) - 1); int messageSize = min(message.length(), sizeof(lora_TXBUFF) - 1);
message.toCharArray((char*)lora_TXBUFF, messageSize + 1, 0); message.toCharArray((char*)lora_TXBUFF, messageSize + 1, 0);
#ifdef SPEED_1200 if (aprsMode == 1200)
{
rf95.setModemConfig(BG_RF95::Bw125Cr47Sf512); rf95.setModemConfig(BG_RF95::Bw125Cr47Sf512);
#else }
else
{
rf95.setModemConfig(BG_RF95::Bw125Cr45Sf4096); rf95.setModemConfig(BG_RF95::Bw125Cr45Sf4096);
#endif }
float aprsFreq2 = aprsFreq.toFloat();
rf95.setFrequency(aprsFreq2); rf95.setFrequency(aprsFreq);
rf95.setTxPower(txPower); rf95.setTxPower(txPower);
rf95.sendAPRS(lora_TXBUFF, messageSize); rf95.sendAPRS(lora_TXBUFF, messageSize);
rf95.waitPacketSent(); rf95.waitPacketSent();
@ -395,8 +462,6 @@ void batt_read(){
#ifdef T_BEAM_V1_0 #ifdef T_BEAM_V1_0
BattVolts = axp.getBattVoltage()/1000; BattVolts = axp.getBattVoltage()/1000;
InpVolts = axp.getVbusVoltage()/1000; InpVolts = axp.getVbusVoltage()/1000;
#elif HELTEC_V2
BattVolts = analogRead(13)*7.221/1024;
#else #else
BattVolts = analogRead(35)*7.221/4096; BattVolts = analogRead(35)*7.221/4096;
#endif #endif
@ -547,7 +612,9 @@ void sendTelemetryFrame() {
// + SETUP --------------------------------------------------------------+// // + SETUP --------------------------------------------------------------+//
void setup(){ void setup(){
SPI.begin(SPI_sck,SPI_miso,SPI_mosi,SPI_ss); //DO2JMG Heltec Patch SPI.begin(SPI_sck,SPI_miso,SPI_mosi,SPI_ss); //DO2JMG Heltec Patch
bool bme_status;
#ifdef BUZZER #ifdef BUZZER
ledcSetup(0,1E5,12); ledcSetup(0,1E5,12);
ledcAttachPin(BUZZER,0); ledcAttachPin(BUZZER,0);
@ -666,15 +733,15 @@ void setup(){
//Set parameters (freq,mode,txpower) //Set parameters (freq,mode,txpower)
if (!preferences.getBool(PREF_APRS_FREQ_INIT)){ if (!preferences.getBool(PREF_APRS_FREQ_INIT)){
preferences.putBool(PREF_APRS_FREQ_INIT, true); preferences.putBool(PREF_APRS_FREQ_INIT, true);
preferences.putString(PREF_APRS_FREQ, aprsFreq); preferences.putDouble(PREF_APRS_FREQ, aprsFreq);
} }
aprsFreq = preferences.getString(PREF_APRS_FREQ); aprsFreq = preferences.getDouble(PREF_APRS_FREQ);
if (!preferences.getBool(PREF_APRS_MODE_INIT)){ if (!preferences.getBool(PREF_APRS_MODE_INIT)){
preferences.putBool(PREF_APRS_MODE_INIT, true); preferences.putBool(PREF_APRS_MODE_INIT, true);
preferences.putString(PREF_APRS_MODE, aprsMode); preferences.putInt(PREF_APRS_MODE, aprsMode);
} }
aprsMode = preferences.getString(PREF_APRS_MODE); aprsMode = preferences.getInt(PREF_APRS_MODE);
if (!preferences.getBool(PREF_APRS_TXPOWER_INIT)){ if (!preferences.getBool(PREF_APRS_TXPOWER_INIT)){
preferences.putBool(PREF_APRS_TXPOWER_INIT, true); preferences.putBool(PREF_APRS_TXPOWER_INIT, true);
@ -825,13 +892,16 @@ void setup(){
#endif #endif
batt_read(); batt_read();
writedisplaytext("LoRa-APRS","","Init:","ADC OK!","BAT: "+String(BattVolts,1),""); writedisplaytext("LoRa-APRS","","Init:","ADC OK!","BAT: "+String(BattVolts,1),"");
#ifdef SPEED_1200 if (aprsMode == 1200)
{
rf95.setModemConfig(BG_RF95::Bw125Cr47Sf512); rf95.setModemConfig(BG_RF95::Bw125Cr47Sf512);
#else }
else
{
rf95.setModemConfig(BG_RF95::Bw125Cr45Sf4096); rf95.setModemConfig(BG_RF95::Bw125Cr45Sf4096);
#endif }
float aprsFreq2 = aprsFreq.toFloat();
rf95.setFrequency(aprsFreq2); rf95.setFrequency(aprsFreq);
rf95.setTxPower(txPower); rf95.setTxPower(txPower);
delay(250); delay(250);
#ifdef KISS_PROTOCOL #ifdef KISS_PROTOCOL
@ -860,11 +930,28 @@ void setup(){
writedisplaytext("","","","","",""); writedisplaytext("","","","","","");
time_to_refresh = millis() + showRXTime; time_to_refresh = millis() + showRXTime;
displayInvalidGPS(); 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 #ifdef TX_RX_LNA
digitalWrite(TXPIN, LOW); digitalWrite(TXPIN, LOW);
digitalWrite(RXPIN, HIGH); digitalWrite(RXPIN, HIGH);
#endif #endif
digitalWrite(TXLED, LOW); digitalWrite(TXLED, LOW);
hum_temp_ctr = 0;
} }
// +---------------------------------------------------------------------+// // +---------------------------------------------------------------------+//
@ -962,6 +1049,34 @@ void loop() {
} }
#endif #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)) { if (rf95.waitAvailableTimeout(100)) {
#ifdef T_BEAM_V1_0 #ifdef T_BEAM_V1_0
#ifdef ENABLE_LED_SIGNALING #ifdef ENABLE_LED_SIGNALING
@ -1104,4 +1219,16 @@ void loop() {
#endif #endif
#endif #endif
vTaskDelay(1); 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);
} }

View File

@ -1,8 +1,16 @@
#include <taskGPS.h> #include <taskGPS.h>
#include <SparkFun_Ublox_Arduino_Library.h> #include <SparkFun_Ublox_Arduino_Library.h>
#include <taskWebServer.h>
SFE_UBLOX_GPS myGPS; 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 // Pins for GPS
#ifdef T_BEAM_V1_0 #ifdef T_BEAM_V1_0
static const int RXPin = 12, TXPin = 34; static const int RXPin = 12, TXPin = 34;
@ -14,7 +22,7 @@ HardwareSerial gpsSerial(1); // TTGO has HW serial
TinyGPSPlus gps; // The TinyGPS++ object TinyGPSPlus gps; // The TinyGPS++ object
bool gpsInitialized = false; bool gpsInitialized = false;
void taskGPS(void *parameter) { [[noreturn]] void taskGPS(void *parameter) {
if (!gpsInitialized){ if (!gpsInitialized){
gpsSerial.begin(GPSBaud, SERIAL_8N1, TXPin, RXPin); //Startup HW serial for GPS gpsSerial.begin(GPSBaud, SERIAL_8N1, TXPin, RXPin); //Startup HW serial for GPS
@ -35,9 +43,31 @@ void taskGPS(void *parameter) {
} }
} }
String gpsDataBuffer = " ";
for (;;) { for (;;) {
#ifdef ENABLE_WIFI
check_for_new_clients(&gpsServer, gps_clients, MAX_GPS_WIFI_CLIENTS);
#endif
while (gpsSerial.available() > 0) { 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); vTaskDelay(100 / portTICK_PERIOD_MS);
} }

View File

@ -9,25 +9,6 @@ QueueHandle_t tncReceivedQueue = nullptr;
#ifdef ENABLE_WIFI #ifdef ENABLE_WIFI
#define MAX_WIFI_CLIENTS 6 #define MAX_WIFI_CLIENTS 6
WiFiClient * clients[MAX_WIFI_CLIENTS]; 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; i<MAX_WIFI_CLIENTS; i++) {
auto client = clients[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;
clients[i] = nullptr;
}
}
}
}
#endif #endif
#ifdef ENABLE_WIFI #ifdef ENABLE_WIFI
#define IN_TNC_BUFFERS (2+MAX_WIFI_CLIENTS) #define IN_TNC_BUFFERS (2+MAX_WIFI_CLIENTS)
@ -52,28 +33,15 @@ void handleKISSData(char character, int bufferIndex) {
} }
inTNCData->concat(character); inTNCData->concat(character);
if (character == (char) FEND && inTNCData->length() > 3) { if (character == (char) FEND && inTNCData->length() > 3) {
const String &TNC2DataFrame = decode_kiss(*inTNCData); bool isDataFrame = false;
const String &TNC2DataFrame = decode_kiss(*inTNCData, isDataFrame);
#ifdef LOCAL_KISS_ECHO if (isDataFrame) {
Serial.print(inTNCData); auto *buffer = new String();
#ifdef ENABLE_BLUETOOTH buffer->concat(TNC2DataFrame);
if (SerialBT.hasClient()) { if (xQueueSend(tncToSendQueue, &buffer, (1000 / portTICK_PERIOD_MS)) != pdPASS) {
SerialBT.print(inTNCData); delete buffer;
} }
#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;
} }
inTNCData->clear(); inTNCData->clear();
} }
@ -103,45 +71,14 @@ void handleKISSData(char character, int bufferIndex) {
} }
#endif #endif
#ifdef ENABLE_WIFI #ifdef ENABLE_WIFI
WiFiClient new_client = tncServer.available(); check_for_new_clients(&tncServer, clients, MAX_WIFI_CLIENTS);
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];
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){ iterateWifiClients([](WiFiClient * client, int clientIdx, const String * unused){
while (client->available() > 0) { while (client->available() > 0) {
char character = client->read(); char character = client->read();
handleKISSData(character, 2+clientIdx); handleKISSData(character, 2+clientIdx);
} }
}, nullptr); }, nullptr, clients, MAX_WIFI_CLIENTS);
#endif #endif
if (xQueueReceive(tncReceivedQueue, &loraReceivedFrameString, (1 / portTICK_PERIOD_MS)) == pdPASS) { if (xQueueReceive(tncReceivedQueue, &loraReceivedFrameString, (1 / portTICK_PERIOD_MS)) == pdPASS) {
@ -158,7 +95,7 @@ void handleKISSData(char character, int bufferIndex) {
client->print(*data); client->print(*data);
client->flush(); client->flush();
} }
}, &kissEncoded); }, &kissEncoded, clients, MAX_WIFI_CLIENTS);
#endif #endif
delete loraReceivedFrameString; delete loraReceivedFrameString;

View File

@ -2,6 +2,7 @@
#include "taskWebServer.h" #include "taskWebServer.h"
#include "preference_storage.h" #include "preference_storage.h"
#include "syslog_log.h" #include "syslog_log.h"
#include "PSRAMJsonDocument.h"
#include <time.h> #include <time.h>
#include <ArduinoJson.h> #include <ArduinoJson.h>
@ -25,6 +26,7 @@ WebServer server(80);
#ifdef KISS_PROTOCOL #ifdef KISS_PROTOCOL
WiFiServer tncServer(NETWORK_TNC_PORT); WiFiServer tncServer(NETWORK_TNC_PORT);
#endif #endif
WiFiServer gpsServer(NETWORK_GPS_PORT);
#ifdef ENABLE_SYSLOG #ifdef ENABLE_SYSLOG
// A UDP instance to let us send and receive packets over UDP // 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); Syslog syslog(udpClient, SYSLOG_PROTO_IETF);
#endif #endif
#ifdef T_BEAM_V1_0
#include <axp20x.h>
extern AXP20X_Class axp;
#endif
void sendCacheHeader() { server.sendHeader("Cache-Control", "max-age=3600"); } void sendCacheHeader() { server.sendHeader("Cache-Control", "max-age=3600"); }
void sendGzipHeader() { server.sendHeader("Content-Encoding", "gzip"); } void sendGzipHeader() { server.sendHeader("Content-Encoding", "gzip"); }
@ -56,6 +63,9 @@ String jsonLineFromPreferenceBool(const char *preferenceName, bool last=false){
String jsonLineFromPreferenceInt(const char *preferenceName, bool last=false){ String jsonLineFromPreferenceInt(const char *preferenceName, bool last=false){
return String("\"") + preferenceName + "\":" + (preferences.getInt(preferenceName)) + (last ? + R"()" : + R"(,)"); return String("\"") + preferenceName + "\":" + (preferences.getInt(preferenceName)) + (last ? + R"()" : + R"(,)");
} }
String jsonLineFromPreferenceDouble(const char *preferenceName, bool last=false){
return String("\"") + preferenceName + "\":" + String(preferences.getDouble(preferenceName),3) + (last ? + R"()" : + R"(,)");
}
String jsonLineFromPreferenceFloat(const char *preferenceName, bool last=false){ String jsonLineFromPreferenceFloat(const char *preferenceName, bool last=false){
return String("\"") + preferenceName + "\":\"" + (preferences.getFloat(preferenceName)) + (last ? + R"(")" : + R"(",)"); return String("\"") + preferenceName + "\":\"" + (preferences.getFloat(preferenceName)) + (last ? + R"(")" : + R"(",)");
} }
@ -118,9 +128,20 @@ void handle_SaveWifiCfg() {
void handle_Reboot() { void handle_Reboot() {
server.sendHeader("Location", "/"); server.sendHeader("Location", "/");
server.send(302,"text/html", ""); server.send(302,"text/html", "");
server.close();
ESP.restart(); 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() { void handle_Restore() {
server.sendHeader("Location", "/"); server.sendHeader("Location", "/");
server.send(302,"text/html", ""); server.send(302,"text/html", "");
@ -149,8 +170,8 @@ void handle_Cfg() {
jsonData += jsonLineFromPreferenceBool(PREF_APRS_FIXED_BEACON_PRESET); jsonData += jsonLineFromPreferenceBool(PREF_APRS_FIXED_BEACON_PRESET);
jsonData += jsonLineFromPreferenceBool(PREF_APRS_SHOW_ALTITUDE); jsonData += jsonLineFromPreferenceBool(PREF_APRS_SHOW_ALTITUDE);
jsonData += jsonLineFromPreferenceBool(PREF_APRS_GPS_EN); jsonData += jsonLineFromPreferenceBool(PREF_APRS_GPS_EN);
jsonData += jsonLineFromPreferenceString(PREF_APRS_FREQ); jsonData += jsonLineFromPreferenceDouble(PREF_APRS_FREQ);
jsonData += jsonLineFromPreferenceString(PREF_APRS_MODE); jsonData += jsonLineFromPreferenceInt(PREF_APRS_MODE);
jsonData += jsonLineFromPreferenceInt(PREF_APRS_TXPOWER); jsonData += jsonLineFromPreferenceInt(PREF_APRS_TXPOWER);
jsonData += jsonLineFromPreferenceBool(PREF_DEV_OL_EN); jsonData += jsonLineFromPreferenceBool(PREF_DEV_OL_EN);
jsonData += jsonLineFromPreferenceBool(PREF_APRS_SHOW_CMT); jsonData += jsonLineFromPreferenceBool(PREF_APRS_SHOW_CMT);
@ -160,7 +181,9 @@ void handle_Cfg() {
jsonData += jsonLineFromPreferenceInt(PREF_DEV_AUTO_SHUT_PRESET); jsonData += jsonLineFromPreferenceInt(PREF_DEV_AUTO_SHUT_PRESET);
jsonData += jsonLineFromInt("FreeHeap", ESP.getFreeHeap()); jsonData += jsonLineFromInt("FreeHeap", ESP.getFreeHeap());
jsonData += jsonLineFromInt("HeapSize", ESP.getHeapSize()); 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 += "}"; jsonData += "}";
server.send(200,"application/json", jsonData); server.send(200,"application/json", jsonData);
@ -243,10 +266,10 @@ void handle_saveDeviceCfg(){
preferences.putInt(PREF_DEV_AUTO_SHUT_PRESET, server.arg(PREF_DEV_AUTO_SHUT_PRESET).toInt()); preferences.putInt(PREF_DEV_AUTO_SHUT_PRESET, server.arg(PREF_DEV_AUTO_SHUT_PRESET).toInt());
} }
if (server.hasArg(PREF_APRS_FREQ)){ if (server.hasArg(PREF_APRS_FREQ)){
preferences.putString(PREF_APRS_FREQ, server.arg(PREF_APRS_FREQ)); preferences.putDouble(PREF_APRS_FREQ, server.arg(PREF_APRS_FREQ).toDouble());
} }
if (server.hasArg(PREF_APRS_MODE)){ if (server.hasArg(PREF_APRS_MODE)){
preferences.putString(PREF_APRS_MODE, server.arg(PREF_APRS_MODE)); preferences.putInt(PREF_APRS_MODE, server.arg(PREF_APRS_MODE).toInt());
} }
if (server.hasArg(PREF_APRS_TXPOWER)){ if (server.hasArg(PREF_APRS_TXPOWER)){
preferences.putInt(PREF_APRS_TXPOWER, server.arg(PREF_APRS_TXPOWER).toInt()); preferences.putInt(PREF_APRS_TXPOWER, server.arg(PREF_APRS_TXPOWER).toInt());
@ -266,13 +289,14 @@ void handle_saveDeviceCfg(){
server.on("/scan_wifi", handle_ScanWifi); server.on("/scan_wifi", handle_ScanWifi);
server.on("/save_wifi_cfg", handle_SaveWifiCfg); server.on("/save_wifi_cfg", handle_SaveWifiCfg);
server.on("/reboot", handle_Reboot); server.on("/reboot", handle_Reboot);
server.on("/shutdown", handle_Shutdown);
server.on("/cfg", handle_Cfg); server.on("/cfg", handle_Cfg);
server.on("/received_list", handle_ReceivedList); server.on("/received_list", handle_ReceivedList);
server.on("/save_aprs_cfg", handle_SaveAPRSCfg); server.on("/save_aprs_cfg", handle_SaveAPRSCfg);
server.on("/save_device_cfg", handle_saveDeviceCfg); server.on("/save_device_cfg", handle_saveDeviceCfg);
server.on("/restore", handle_Restore); server.on("/restore", handle_Restore);
server.on("/update", HTTP_POST, []() { 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.sendHeader("Connection", "close");
server.send(200, "text/plain", (Update.hasError()) ? "FAIL" : "OK"); server.send(200, "text/plain", (Update.hasError()) ? "FAIL" : "OK");
delay(500); delay(500);
@ -356,6 +380,7 @@ void handle_saveDeviceCfg(){
#ifdef KISS_PROTOCOL #ifdef KISS_PROTOCOL
tncServer.begin(); tncServer.begin();
#endif #endif
gpsServer.begin();
if (MDNS.begin(webServerCfg->callsign.c_str())) { if (MDNS.begin(webServerCfg->callsign.c_str())) {
MDNS.setInstanceName(webServerCfg->callsign + " LoRa APRS TNC "); MDNS.setInstanceName(webServerCfg->callsign + " LoRa APRS TNC ");
MDNS.addService("http", "tcp", 80); MDNS.addService("http", "tcp", 80);

View File

@ -0,0 +1,64 @@
//
// Created by Admin on 11.06.2021.
//
#ifdef ENABLE_WIFI
#include <wifi_clients.h>
#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

View File

@ -1,6 +1,6 @@
FILENAME_BUILDNO = '.pio/versioning' FILENAME_BUILDNO = '.pio/versioning'
FILENAME_VERSION_H = 'include/version.h' FILENAME_VERSION_H = 'include/version.h'
version = 'v0.3.' version = 'v0.3.2.'
import datetime import datetime
from subprocess import * from subprocess import *
@ -11,7 +11,7 @@ try:
build_no = int(f.readline()) + 1 build_no = int(f.readline()) + 1
except: except:
print('Starting build number from 1..') print('Starting build number from 1..')
build_no = 143 build_no = 1
with open(FILENAME_BUILDNO, 'w+') as f: with open(FILENAME_BUILDNO, 'w+') as f:
f.write(str(build_no)) f.write(str(build_no))
print('Build number: {}'.format(build_no)) print('Build number: {}'.format(build_no))