Merge pull request #16 from lora-aprs/json_config

Json config
This commit is contained in:
Peter Buchegger 2021-03-07 23:13:12 +01:00 committed by GitHub
commit f15568c812
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 11030 additions and 184 deletions

View file

@ -1,4 +1,5 @@
name: PlatformIO CI
name: check and build
on: [push, pull_request] on: [push, pull_request]
@ -6,20 +7,50 @@ jobs:
PlatformIO-Check: PlatformIO-Check:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- run: sudo apt-get install python3-setuptools python3-wheel
- run: pip3 install platformio
- run: echo "$HOME/.local/bin" >> $GITHUB_PATH
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- run: platformio check --fail-on-defect low --fail-on-defect medium --fail-on-defect high --skip-packages --flags "--suppress=*:*.pio\* --inline-suppr" -v - name: Cache pip
uses: actions/cache@v2
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
restore-keys: ${{ runner.os }}-pip-
- name: Cache PlatformIO
uses: actions/cache@v2
with:
path: ~/.platformio
key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }}
- name: Set up Python
uses: actions/setup-python@v2
- name: Install PlatformIO
run: |
python -m pip install --upgrade pip
pip install --upgrade platformio
- name: Run PlatformIO Check
run: platformio check --fail-on-defect low --fail-on-defect medium --fail-on-defect high
PlatformIO-Build: PlatformIO-Build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- run: sudo apt-get install python3-setuptools python3-wheel
- run: pip3 install platformio
- run: echo "$HOME/.local/bin" >> $GITHUB_PATH
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- run: platformio run - name: Cache pip
uses: actions/cache@v2
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
restore-keys: ${{ runner.os }}-pip-
- name: Cache PlatformIO
uses: actions/cache@v2
with:
path: ~/.platformio
key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }}
- name: Set up Python
uses: actions/setup-python@v2
- name: Install PlatformIO
run: |
python -m pip install --upgrade pip
pip install --upgrade platformio
- name: Run PlatformIO CI
run: platformio run
- uses: actions/upload-artifact@v2 - uses: actions/upload-artifact@v2
with: with:
name: firmware name: firmware

View file

@ -15,28 +15,26 @@ You can use one of the Lora32 boards:
This boards cost around 30 Euros, they are very cheap but perfect for an LoRa iGate. This boards cost around 30 Euros, they are very cheap but perfect for an LoRa iGate.
Keep in minde: you need a 433MHz version! Keep in minde: you need a 433MHz version!
## Compiling ## Compiling and configuration
**There is a german [quick start](https://www.lora-aprs.info/docs/LoRa_APRS_iGate/quick-start-guide/) page! Take a look ;)**
**There is a french [quick start](http://www.f5kmy.fr/spip.php?article509) page! Take a look ;)**
### How to compile ### How to compile
The best success is to use PlatformIO. Go to https://platformio.org/ and download the IDE. Just open the folder and you can compile the Firmware. The best success is to use PlatformIO (and it is the only platform where I can support you).
Here is a [Video](https://www.youtube.com/watch?v=clIlTEFbWLk&feature=emb_logo) tutorial in german language how to install the software.
### Dependencies * Go to [PlatformIO](https://platformio.org/) download and install the IDE.
* If installed open the IDE, go to the left side and klick on 'extensions' then search for 'PatformIO' and install.
* When installed click 'the ant head' on the left and choose import the project on the right.
* Just open the folder and you can compile the Firmware.
* [LoRa](https://github.com/sandeepmistry/arduino-LoRa) by Sandeep Mistry ### Configuration
* [APRS-Decoder-Lib](https://github.com/peterus/APRS-Decoder-Lib) by Peter Buchegger
* [Adafruit SSD1306](https://github.com/adafruit/Adafruit_SSD1306) by Adafruit (with all dependecies)
* [TinyGPSPlus](https://github.com/mikalhart/TinyGPSPlus) by Mikal Hart
* [AXP202X_Library](https://github.com/lewisxhe/AXP202X_Library) by Lewis He
## Configuration * You can find all nessesary settings to change for your configuration in **data/is-cfg.json**.
* To upload it to your board you have to do this via **Upload File System image** in PlatformIO!
Change your configuration in settings.h * To find the 'Upload File System image' click the PlatformIO symbol (the little alien) on the left side, choos your configuration, click on 'Platform' and search for 'Upload File System image'.
## Future plans
The complete configuration should move to [IotWebConf](https://github.com/prampec/IotWebConf).
## LoRa iGate ## LoRa iGate

31
data/tracker.json Normal file
View file

@ -0,0 +1,31 @@
{
"callsign":"NOCALL-10",
"debug": false,
"beacon":
{
"message":"LoRa Tracker, Info: github.com/lora-aprs/LoRa_APRS_Tracker",
"timeout": 1,
"symbol": "[",
"overlay": "/"
},
"smart_beacon":
{
"active":false,
"turn_min":25,
"slow_rate":300,
"slow_speed":10,
"fast_rate":60,
"fast_speed":100,
"min_tx_dist":100,
"min_bcn":5
},
"lora":
{
"frequency_rx":433775000,
"frequency_tx":433775000,
"power":20,
"spreading_factor":12,
"signal_bandwidth":125000,
"coding_rate4":5
}
}

View file

@ -4,9 +4,11 @@ platform = espressif32 @ 3.0.0
framework = arduino framework = arduino
lib_ldf_mode = deep+ lib_ldf_mode = deep+
monitor_speed = 115200 monitor_speed = 115200
monitor_filters = esp32_exception_decoder
lib_deps = lib_deps =
adafruit/Adafruit GFX Library @ 1.7.5 adafruit/Adafruit GFX Library @ 1.7.5
adafruit/Adafruit SSD1306 @ 2.4.0 adafruit/Adafruit SSD1306 @ 2.4.0
bblanchon/ArduinoJson @ 6.17.0
lewisxhe/AXP202X_Library @ 1.1.2 lewisxhe/AXP202X_Library @ 1.1.2
sandeepmistry/LoRa @ 0.7.2 sandeepmistry/LoRa @ 0.7.2
peterus/APRS-Decoder-Lib @ 0.0.5 peterus/APRS-Decoder-Lib @ 0.0.5
@ -14,7 +16,8 @@ lib_deps =
paulstoffregen/Time @ 1.6 paulstoffregen/Time @ 1.6
check_tool = cppcheck check_tool = cppcheck
check_flags = check_flags =
cppcheck: --suppress=*:*.pio\* --inline-suppr cppcheck: --suppress=*:*.pio\* --inline-suppr -DCPPCHECK
check_skip_packages = yes
[env:ttgo-t-beam-v1] [env:ttgo-t-beam-v1]
board = ttgo-t-beam board = ttgo-t-beam

View file

@ -4,49 +4,44 @@
#include <TinyGPS++.h> #include <TinyGPS++.h>
#include <TimeLib.h> #include <TimeLib.h>
#include <WiFi.h> #include <WiFi.h>
#include "settings.h"
#include "display.h" #include "display.h"
#include "pins.h" #include "pins.h"
#include "power_management.h" #include "power_management.h"
#include "configuration.h"
Configuration Config;
#include "power_management.h" #include "power_management.h"
PowerManagement powerManagement; PowerManagement powerManagement;
#include "logger.h"
HardwareSerial ss(1); HardwareSerial ss(1);
TinyGPSPlus gps; TinyGPSPlus gps;
void load_config();
void setup_lora(); void setup_lora();
void setup_gps(); void setup_gps();
String create_lat_aprs(RawDegrees lat); String create_lat_aprs(RawDegrees lat);
String create_long_aprs(RawDegrees lng); String create_long_aprs(RawDegrees lng);
String createDateString(time_t t); String createDateString(time_t t);
String createTimeString(time_t t); String createTimeString(time_t t);
String getSmartBeaconState();
static bool send_update = true;
// Initial lat/lng pos, change to your base station coordnates
float lastTxLat = 0;
float lastTxLng = 0;
float lastTxdistance;
unsigned long txInterval = 60000L; // Initial 60 secs internal
int previousHeading, currentHeading, headingDelta, headingTresh = 0;
int lastTxTime = millis();
// cppcheck-suppress unusedFunction // cppcheck-suppress unusedFunction
void setup() void setup()
{ {
Serial.begin(115200); Serial.begin(115200);
#ifdef TTGO_T_Beam_V1_0 #ifdef TTGO_T_Beam_V1_0
Wire.begin(SDA, SCL); Wire.begin(SDA, SCL);
if (!powerManagement.begin(Wire)) if (!powerManagement.begin(Wire))
{ {
Serial.println("[INFO] AXP192 init done!"); logPrintlnI("AXP192 init done!");
} }
else else
{ {
Serial.println("[ERROR] AXP192 init failed!"); logPrintlnE("AXP192 init failed!");
} }
powerManagement.activateLoRa(); powerManagement.activateLoRa();
powerManagement.activateOLED(); powerManagement.activateOLED();
@ -55,15 +50,12 @@ void setup()
#endif #endif
delay(500); delay(500);
Serial.println("[INFO] LoRa APRS Tracker by OE5BPA (Peter Buchegger)"); logPrintlnI("LoRa APRS Tracker by OE5BPA (Peter Buchegger)");
setup_display(); setup_display();
show_display("OE5BPA", "LoRa APRS Tracker", "by Peter Buchegger", 2000); show_display("OE5BPA", "LoRa APRS Tracker", "by Peter Buchegger", 2000);
load_config();
#ifdef SB_ACTIVE
Serial.println("[INFO] Smart Beaconing Active");
show_display("DJ1AN", "Smart Beaconing","Active", 1000);
#endif
setup_gps(); setup_gps();
setup_lora(); setup_lora();
@ -71,103 +63,112 @@ void setup()
WiFi.mode(WIFI_OFF); WiFi.mode(WIFI_OFF);
btStop(); btStop();
logPrintlnI("Smart Beacon is " + getSmartBeaconState());
show_display("INFO", "Smart Beacon is " + getSmartBeaconState(), 1000);
logPrintlnI("setup done...");
delay(500); delay(500);
Serial.println("[INFO] setup done...");
} }
// cppcheck-suppress unusedFunction // cppcheck-suppress unusedFunction
void loop() void loop()
{ {
#if !defined(DEBUGMODE) if(Config.debug)
while (ss.available() > 0)
{ {
char c = ss.read(); while(Serial.available() > 0)
//Serial.print(c); {
gps.encode(c); char c = Serial.read();
//Serial.print(c);
gps.encode(c);
}
} }
#else else
while (Serial.available() > 0)
{ {
char c = Serial.read(); while(ss.available() > 0)
//Serial.print(c); {
gps.encode(c); char c = ss.read();
//Serial.print(c);
gps.encode(c);
}
} }
#endif
bool gps_time_update = gps.time.isUpdated(); bool gps_time_update = gps.time.isUpdated();
bool gps_loc_update = gps.location.isUpdated(); bool gps_loc_update = gps.location.isUpdated();
static time_t nextBeaconTimeStamp = -1; static time_t nextBeaconTimeStamp = -1;
static bool send_update = true;
if(gps.time.isValid()) if(gps.time.isValid())
{ {
setTime(gps.time.hour(),gps.time.minute(),gps.time.second(),gps.date.day(),gps.date.month(),gps.date.year()); setTime(gps.time.hour(), gps.time.minute(), gps.time.second(), gps.date.day(), gps.date.month(), gps.date.year());
if (nextBeaconTimeStamp <= now() || nextBeaconTimeStamp == -1) if(nextBeaconTimeStamp <= now())
{ {
send_update = true; send_update = true;
} }
} }
#ifdef SB_ACTIVE static double lastTxLat = 0.0;
send_update = false; static double lastTxLng = 0.0;
if ( gps_loc_update ) static double lastTxdistance = 0.0;
static unsigned long txInterval = 60000L; // Initial 60 secs internal
static double currentHeading = 0;
static double previousHeading = 0;
static int lastTxTime = millis();
if(Config.smart_beacon.active)
{ {
lastTxdistance = TinyGPSPlus::distanceBetween( if(gps_loc_update)
gps.location.lat(),
gps.location.lng(),
lastTxLat,
lastTxLng
);
// Get headings and heading delta
currentHeading = (int) gps.course.deg();
if ( currentHeading >= 180 )
{ {
currentHeading = currentHeading-180; lastTxdistance = TinyGPSPlus::distanceBetween(gps.location.lat(), gps.location.lng(), lastTxLat, lastTxLng);
}
headingDelta = (int) ( previousHeading - currentHeading ) % 360; // Get headings and heading delta
int turn_slope = 100; currentHeading = gps.course.deg();
headingTresh = (float) SB_TURN_MIN + turn_slope / gps.speed.kmph(); double headingDelta = abs(previousHeading - currentHeading);
int lastTx = millis() - lastTxTime; int lastTx = millis() - lastTxTime;
if ( lastTx > SB_MIN_BCN * 1000) if(lastTx > Config.smart_beacon.min_bcn * 1000)
{
//send_update = true;
// Check for heading more than 25 degrees
if ( (headingDelta < -SB_TURN_MIN || headingDelta > SB_TURN_MIN) && lastTxdistance > SB_MIN_TX_DIST )
{ {
send_update = true; // Check for heading more than 25 degrees
if(headingDelta > Config.smart_beacon.turn_min && lastTxdistance > Config.smart_beacon.min_tx_dist)
{
send_update = true;
}
} }
}
if ( lastTx >= txInterval ) if(lastTx >= txInterval)
{
// Trigger Tx Tracker when Tx interval is reach
// Will not Tx if stationary bcos speed < 5 and lastTxDistance < 20
if ( lastTxdistance > 20 )
{ {
send_update = true; // Trigger Tx Tracker when Tx interval is reach
// Will not Tx if stationary bcos speed < 5 and lastTxDistance < 20
if (lastTxdistance > 20)
{
send_update = true;
}
} }
} }
} }
#endif
if(send_update && gps.location.isValid() && gps_loc_update) if(send_update && gps_loc_update)
{ {
powerManagement.deactivateMeasurement();
nextBeaconTimeStamp = now() + (BEACON_TIMEOUT * SECS_PER_MIN);
send_update = false; send_update = false;
nextBeaconTimeStamp = now() + (Config.beacon.timeout * SECS_PER_MIN);
APRSMessage msg; APRSMessage msg;
msg.setSource(CALL); msg.setSource(Config.callsign);
msg.setDestination("APLT0"); msg.setDestination("APLT0");
String lat = create_lat_aprs(gps.location.rawLat()); String lat = create_lat_aprs(gps.location.rawLat());
String lng = create_long_aprs(gps.location.rawLng()); String lng = create_long_aprs(gps.location.rawLng());
msg.getAPRSBody()->setData(String("=") + lat + SYMBOL_OVERLAY + lng + SYMBOL_CODE + BEACON_MESSAGE);
#ifdef TTGO_T_Beam_V1_0
String batteryVoltage(powerManagement.getBatteryVoltage(), 2);
String batteryChargeCurrent(powerManagement.getBatteryChargeDischargeCurrent(), 0);
msg.getAPRSBody()->setData(String("=") + lat + Config.beacon.overlay + lng + Config.beacon.symbol + Config.beacon.message + " - Bat.: " + batteryVoltage + "V - Cur.: " + batteryChargeCurrent + "mA");
#else
msg.getAPRSBody()->setData(String("=") + lat + Config.beacon.overlay + lng + Config.beacon.symbol + Config.beacon.message);
#endif
String data = msg.encode(); String data = msg.encode();
Serial.println(data); logPrintlnD(data);
show_display("<< TX >>", data); show_display("<< TX >>", data);
LoRa.beginPacket(); LoRa.beginPacket();
// Header: // Header:
@ -177,16 +178,15 @@ void loop()
// APRS Data: // APRS Data:
LoRa.write((const uint8_t *)data.c_str(), data.length()); LoRa.write((const uint8_t *)data.c_str(), data.length());
LoRa.endPacket(); LoRa.endPacket();
powerManagement.activateMeasurement();
#ifdef SB_ACTIVE
lastTxLat = gps.location.lat();
lastTxLng = gps.location.lng();
previousHeading = currentHeading;
lastTxdistance = 0;
lastTxTime = millis();
#endif
if (Config.smart_beacon.active)
{
lastTxLat = gps.location.lat();
lastTxLng = gps.location.lng();
previousHeading = currentHeading;
lastTxdistance = 0.0;
lastTxTime = millis();
}
} }
if(gps_time_update) if(gps_time_update)
@ -196,70 +196,81 @@ void loop()
String batteryChargeCurrent(powerManagement.getBatteryChargeDischargeCurrent(), 0); String batteryChargeCurrent(powerManagement.getBatteryChargeDischargeCurrent(), 0);
#endif #endif
show_display(CALL, show_display(Config.callsign,
createDateString(now()) + " " + createTimeString(now()), createDateString(now()) + " " + createTimeString(now()),
String("Sats: ") + gps.satellites.value() + " HDOP: " + gps.hdop.hdop(), String("Sats: ") + gps.satellites.value() + " HDOP: " + gps.hdop.hdop(),
String("Nxt Bcn: ") + createTimeString(nextBeaconTimeStamp) String("Nxt Bcn: ") + createTimeString(nextBeaconTimeStamp)
#ifdef TTGO_T_Beam_V1_0 #ifdef TTGO_T_Beam_V1_0
, String("Bat: ") + batteryVoltage + "V " + batteryChargeCurrent + "mA" , String("Bat: ") + batteryVoltage + "V, " + batteryChargeCurrent + "mA"
#endif #endif
, String("Smart Beacon: " + getSmartBeaconState())
); );
#ifdef SB_ACTIVE if(Config.smart_beacon.active)
// Change the Tx internal based on the current speed {
if ( gps.speed.kmph() < 5 ) // Change the Tx internal based on the current speed
{ if(gps.speed.kmph() < 5)
txInterval = 300000; // Change Tx internal to 5 mins {
} txInterval = 300000; // Change Tx internal to 5 mins
else if ( gps.speed.kmph() < SB_SLOW_SPEED ) }
{ else if(gps.speed.kmph() < Config.smart_beacon.slow_speed)
txInterval = SB_SLOW_RATE * 1000; // Change Tx interval {
} txInterval = Config.smart_beacon.slow_rate * 1000;
else if ( gps.speed.kmph() > SB_FAST_SPEED) }
{ else if(gps.speed.kmph() > Config.smart_beacon.fast_speed)
txInterval = SB_FAST_RATE * 1000; // Change Tx interval {
} txInterval = Config.smart_beacon.fast_rate * 1000;
else }
{ else
// Interval inbetween low and high speed {
txInterval = (SB_FAST_SPEED / gps.speed.kmph()) * SB_FAST_RATE * 1000; // Interval inbetween low and high speed
} txInterval = (Config.smart_beacon.fast_speed / gps.speed.kmph()) * Config.smart_beacon.fast_rate * 1000;
#endif }
}
} }
#if !defined(DEBUGMODE) if ((Config.debug == false) && (millis() > 5000 && gps.charsProcessed() < 10))
if(millis() > 5000 && gps.charsProcessed() < 10)
{ {
Serial.println("No GPS detected!"); logPrintlnE("No GPS frames detected! Try to reset the GPS Chip with this firmware: https://github.com/lora-aprs/TTGO-T-Beam_GPS-reset");
} }
#endif }
void load_config()
{
ConfigurationManagement confmg("/tracker.json");
Config = confmg.readConfiguration();
if(Config.callsign == "NOCALL-10")
{
logPrintlnE("You have to change your settings in 'data/is-cfg.json' and upload it via \"Upload File System image\"!");
show_display("ERROR", "You have to change your settings in 'data/is-cfg.json' and upload it via \"Upload File System image\"!");
while(true)
{}
}
} }
void setup_lora() void setup_lora()
{ {
Serial.println("[INFO] Set SPI pins!"); logPrintlnI("Set SPI pins!");
SPI.begin(LORA_SCK, LORA_MISO, LORA_MOSI, LORA_CS); SPI.begin(LORA_SCK, LORA_MISO, LORA_MOSI, LORA_CS);
logPrintlnI("Set LoRa pins!");
LoRa.setPins(LORA_CS, LORA_RST, LORA_IRQ); LoRa.setPins(LORA_CS, LORA_RST, LORA_IRQ);
Serial.println("[INFO] Set LoRa pins!");
long freq = FREQ_SET; long freq = Config.lora.frequencyTx;
Serial.print("[INFO] frequency: "); logPrintI("frequency: ");
Serial.println(freq); logPrintlnI(String(freq));
if (!LoRa.begin(freq)) { if (!LoRa.begin(freq)) {
Serial.println("[ERROR] Starting LoRa failed!"); logPrintlnE("Starting LoRa failed!");
show_display("ERROR", "Starting LoRa failed!"); show_display("ERROR", "Starting LoRa failed!");
while (1); while(true)
{}
} }
LoRa.setSpreadingFactor(SF_SET); LoRa.setSpreadingFactor(Config.lora.spreadingFactor);
LoRa.setSignalBandwidth(BW_SET); LoRa.setSignalBandwidth(Config.lora.signalBandwidth);
LoRa.setCodingRate4(CR_SET); LoRa.setCodingRate4(Config.lora.codingRate4);
LoRa.enableCrc(); LoRa.enableCrc();
LoRa.setTxPower(20); LoRa.setTxPower(Config.lora.power);
Serial.println("[INFO] LoRa init done!"); logPrintlnI("LoRa init done!");
show_display("INFO", "LoRa init done!", 2000); show_display("INFO", "LoRa init done!", 2000);
} }
@ -311,3 +322,12 @@ String createTimeString(time_t t)
sprintf(line, "%02d:%02d:%02d", hour(t), minute(t), second(t)); sprintf(line, "%02d:%02d:%02d", hour(t), minute(t), second(t));
return String(line); return String(line);
} }
String getSmartBeaconState()
{
if (Config.smart_beacon.active)
{
return "On";
}
return "Off";
}

104
src/configuration.cpp Normal file
View file

@ -0,0 +1,104 @@
#include <SPIFFS.h>
#include "configuration.h"
#include "logger.h"
ConfigurationManagement::ConfigurationManagement(String FilePath)
: mFilePath(FilePath)
{
if(!SPIFFS.begin(true))
{
logPrintlnE("Mounting SPIFFS was not possible. Trying to format SPIFFS...");
SPIFFS.format();
if(!SPIFFS.begin())
{
logPrintlnE("Formating SPIFFS was not okay!");
}
}
}
Configuration ConfigurationManagement::readConfiguration()
{
File file = SPIFFS.open(mFilePath);
if(!file)
{
logPrintlnE("Failed to open file for reading...");
return Configuration();
}
DynamicJsonDocument data(2048);
DeserializationError error = deserializeJson(data, file);
if(error)
{
logPrintlnE("Failed to read file, using default configuration.");
}
file.close();
Configuration conf;
if(data.containsKey("callsign"))
conf.callsign = data["callsign"].as<String>();
conf.debug = data["debug"] | false;
if(data.containsKey("beacon") && data["beacon"].containsKey("message"))
conf.beacon.message = data["beacon"]["message"].as<String>();
conf.beacon.timeout = data["beacon"]["timeout"] | 1;
if(data.containsKey("beacon") && data["beacon"].containsKey("symbol"))
conf.beacon.symbol = data["beacon"]["symbol"].as<String>();
if(data.containsKey("beacon") && data["beacon"].containsKey("overlay"))
conf.beacon.overlay = data["beacon"]["overlay"].as<String>() ;
conf.smart_beacon.active = data["smart_beacon"]["active"] | false;
conf.smart_beacon.turn_min = data["smart_beacon"]["turn_min"] | 25;
conf.smart_beacon.slow_rate = data["smart_beacon"]["slow_rate"] | 300;
conf.smart_beacon.slow_speed = data["smart_beacon"]["slow_speed"] | 10;
conf.smart_beacon.fast_rate = data["smart_beacon"]["fast_rate"] | 60;
conf.smart_beacon.fast_speed = data["smart_beacon"]["fast_speed"] | 100;
conf.smart_beacon.min_tx_dist = data["smart_beacon"]["min_tx_dist"] | 100;
conf.smart_beacon.min_bcn = data["smart_beacon"]["min_bcn"] | 5;
conf.lora.frequencyRx = data["lora"]["frequency_rx"] | 433775000;
conf.lora.frequencyTx = data["lora"]["frequency_tx"] | 433775000;
conf.lora.power = data["lora"]["power"] | 20;
conf.lora.spreadingFactor = data["lora"]["spreading_factor"] | 12;
conf.lora.signalBandwidth = data["lora"]["signal_bandwidth"] | 125000;
conf.lora.codingRate4 = data["lora"]["coding_rate4"] | 5;
return conf;
}
// cppcheck-suppress unusedFunction
void ConfigurationManagement::writeConfiguration(Configuration conf)
{
File file = SPIFFS.open(mFilePath, "w");
if(!file)
{
logPrintlnE("Failed to open file for writing...");
return;
}
DynamicJsonDocument data(2048);
data["callsign"] = conf.callsign;
data["debug"] = conf.debug;
data["beacon"]["message"] = conf.beacon.message;
data["beacon"]["timeout"] = conf.beacon.timeout;
data["beacon"]["symbol"] = conf.beacon.symbol;
data["beacon"]["overlay"] = conf.beacon.overlay;
data["smart_beacon"]["active"] = conf.smart_beacon.active;
data["smart_beacon"]["turn_min"] = conf.smart_beacon.turn_min;
data["smart_beacon"]["slow_rate"] = conf.smart_beacon.slow_rate;
data["smart_beacon"]["slow_speed"] = conf.smart_beacon.slow_speed;
data["smart_beacon"]["fast_rate"] = conf.smart_beacon.fast_rate;
data["smart_beacon"]["fast_speed"] = conf.smart_beacon.fast_speed;
data["smart_beacon"]["min_tx_dist"] = conf.smart_beacon.min_tx_dist;
data["smart_beacon"]["min_bcn"] = conf.smart_beacon.min_bcn ;
data["lora"]["frequency_rx"] = conf.lora.frequencyRx;
data["lora"]["frequency_tx"] = conf.lora.frequencyTx;
data["lora"]["power"] = conf.lora.power;
data["lora"]["spreading_factor"] = conf.lora.spreadingFactor;
data["lora"]["signal_bandwidth"] = conf.lora.signalBandwidth;
data["lora"]["coding_rate4"] = conf.lora.codingRate4;
serializeJson(data, file);
file.close();
}

74
src/configuration.h Normal file
View file

@ -0,0 +1,74 @@
#ifndef CONFIGURATION_H_
#define CONFIGURATION_H_
#include <list>
#include <Arduino.h>
#ifndef CPPCHECK
#include <ArduinoJson.h>
#endif
class Configuration
{
public:
class Beacon
{
public:
Beacon() : message("LoRa Tracker, Info: github.com/lora-aprs/LoRa_APRS_Tracker"), timeout(1), symbol("["), overlay("/") {}
String message;
int timeout;
String symbol;
String overlay;
};
class Smart_Beacon
{
public:
Smart_Beacon() : active(false), turn_min(25), slow_rate(300), slow_speed(10), fast_rate(60), fast_speed(100), min_tx_dist(100), min_bcn(5) {}
bool active;
int turn_min;
int slow_rate;
int slow_speed;
int fast_rate;
int fast_speed;
int min_tx_dist;
int min_bcn;
};
class LoRa
{
public:
LoRa() : frequencyRx(433775000), frequencyTx(433775000), power(20), spreadingFactor(12), signalBandwidth(125000), codingRate4(5) {}
long frequencyRx;
long frequencyTx;
int power;
int spreadingFactor;
long signalBandwidth;
int codingRate4;
};
Configuration() : callsign("NOCALL-10"), debug(false) {};
String callsign;
bool debug;
Beacon beacon;
Smart_Beacon smart_beacon;
LoRa lora;
};
class ConfigurationManagement
{
public:
explicit ConfigurationManagement(String FilePath);
Configuration readConfiguration();
void writeConfiguration(Configuration conf);
private:
const String mFilePath;
};
#endif

View file

@ -4,8 +4,8 @@
#include <Adafruit_SSD1306.h> #include <Adafruit_SSD1306.h>
#include "display.h" #include "display.h"
#include "settings.h"
#include "pins.h" #include "pins.h"
#include "logger.h"
Adafruit_SSD1306 display(128, 64, &Wire, OLED_RST); Adafruit_SSD1306 display(128, 64, &Wire, OLED_RST);
@ -19,8 +19,9 @@ void setup_display()
Wire.begin(OLED_SDA, OLED_SCL); Wire.begin(OLED_SDA, OLED_SCL);
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3c, false, false)) if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3c, false, false))
{ {
Serial.println("SSD1306 allocation failed"); logPrintlnE("SSD1306 allocation failed");
while (1); while(true)
{}
} }
display.clearDisplay(); display.clearDisplay();

248
src/logger.cpp Normal file
View file

@ -0,0 +1,248 @@
#include "logger.h"
#undef LOG_RESET_COLOR
#undef LOG_COLOR_E
#undef LOG_COLOR_W
#undef LOG_COLOR_I
#undef LOG_COLOR_D
#undef LOG_COLOR_V
#define LOG_COLOR_BLACK "30"
#define LOG_COLOR_RED "31"
#define LOG_COLOR_GREEN "32"
#define LOG_COLOR_BROWN "33"
#define LOG_COLOR_BLUE "34"
#define LOG_COLOR_PURPLE "35"
#define LOG_COLOR_CYAN "36"
#define LOG_COLOR(COLOR) "\033[0;" COLOR "m"
#define LOG_BOLD(COLOR) "\033[1;" COLOR "m"
#define LOG_RESET_COLOR "\033[0m"
#define LOG_COLOR_E LOG_COLOR(LOG_COLOR_RED)
#define LOG_COLOR_W LOG_COLOR(LOG_COLOR_BROWN)
#define LOG_COLOR_I LOG_COLOR(LOG_COLOR_GREEN)
#define LOG_COLOR_D LOG_COLOR(LOG_COLOR_BLUE)
#define LOG_COLOR_V LOG_COLOR(LOG_COLOR_CYAN)
Logger::Logger()
: _serial(Serial), _level(DEBUG_LEVEL_DEBUG), _printIsNewline(true)
{
}
// cppcheck-suppress unusedFunction
void Logger::setSerial(const HardwareSerial & serial)
{
_serial = serial;
}
// cppcheck-suppress unusedFunction
void Logger::setDebugLevel(debug_level_t level)
{
_level = level;
}
// cppcheck-suppress unusedFunction
void Logger::printA(const String & text, const char * file, uint32_t line)
{
printStartColor(DEBUG_LEVEL_NONE);
printHeader(DEBUG_LEVEL_NONE, file, line, false);
_serial.print(text);
printEndColor(DEBUG_LEVEL_NONE);
}
// cppcheck-suppress unusedFunction
void Logger::printE(const String & text, const char * file, uint32_t line)
{
printStartColor(DEBUG_LEVEL_ERROR);
printHeader(DEBUG_LEVEL_ERROR, file, line, false);
_serial.print(text);
printEndColor(DEBUG_LEVEL_ERROR);
}
// cppcheck-suppress unusedFunction
void Logger::printlnA(const String & text, const char * file, uint32_t line)
{
printStartColor(DEBUG_LEVEL_NONE);
printHeader(DEBUG_LEVEL_NONE, file, line, true);
_serial.println(text);
printEndColor(DEBUG_LEVEL_NONE);
}
// cppcheck-suppress unusedFunction
void Logger::printlnE(const String & text, const char * file, uint32_t line)
{
printStartColor(DEBUG_LEVEL_ERROR);
printHeader(DEBUG_LEVEL_ERROR, file, line, true);
_serial.println(text);
printEndColor(DEBUG_LEVEL_ERROR);
}
// cppcheck-suppress unusedFunction
void Logger::printV(const String & text, const char * file, uint32_t line)
{
if (_level >= DEBUG_LEVEL_VERBOSE)
{
printStartColor(DEBUG_LEVEL_VERBOSE);
printHeader(DEBUG_LEVEL_VERBOSE, file, line, false);
_serial.print(text);
printEndColor(DEBUG_LEVEL_VERBOSE);
}
}
// cppcheck-suppress unusedFunction
void Logger::printD(const String & text, const char * file, uint32_t line)
{
if (_level >= DEBUG_LEVEL_DEBUG)
{
printStartColor(DEBUG_LEVEL_DEBUG);
printHeader(DEBUG_LEVEL_DEBUG, file, line, false);
_serial.print(text);
printEndColor(DEBUG_LEVEL_DEBUG);
}
}
// cppcheck-suppress unusedFunction
void Logger::printI(const String & text, const char * file, uint32_t line)
{
if (_level >= DEBUG_LEVEL_INFO)
{
printStartColor(DEBUG_LEVEL_INFO);
printHeader(DEBUG_LEVEL_INFO, file, line, false);
_serial.print(text);
printEndColor(DEBUG_LEVEL_INFO);
}
}
// cppcheck-suppress unusedFunction
void Logger::printW(const String & text, const char * file, uint32_t line)
{
if (_level >= DEBUG_LEVEL_WARN)
{
printStartColor(DEBUG_LEVEL_WARN);
printHeader(DEBUG_LEVEL_WARN, file, line, false);
_serial.print(text);
printEndColor(DEBUG_LEVEL_WARN);
}
}
// cppcheck-suppress unusedFunction
void Logger::printlnV(const String & text, const char * file, uint32_t line)
{
if (_level >= DEBUG_LEVEL_VERBOSE)
{
printStartColor(DEBUG_LEVEL_VERBOSE);
printHeader(DEBUG_LEVEL_VERBOSE, file, line, true);
_serial.println(text);
printEndColor(DEBUG_LEVEL_VERBOSE);
}
}
// cppcheck-suppress unusedFunction
void Logger::printlnD(const String & text, const char * file, uint32_t line)
{
if (_level >= DEBUG_LEVEL_DEBUG)
{
printStartColor(DEBUG_LEVEL_DEBUG);
printHeader(DEBUG_LEVEL_DEBUG, file, line, true);
_serial.println(text);
printEndColor(DEBUG_LEVEL_DEBUG);
}
}
// cppcheck-suppress unusedFunction
void Logger::printlnI(const String & text, const char * file, uint32_t line)
{
if (_level >= DEBUG_LEVEL_INFO)
{
printStartColor(DEBUG_LEVEL_INFO);
printHeader(DEBUG_LEVEL_INFO, file, line, true);
_serial.println(text);
printEndColor(DEBUG_LEVEL_INFO);
}
}
// cppcheck-suppress unusedFunction
void Logger::printlnW(const String & text, const char * file, uint32_t line)
{
if (_level >= DEBUG_LEVEL_WARN)
{
printStartColor(DEBUG_LEVEL_WARN);
printHeader(DEBUG_LEVEL_WARN, file, line, true);
_serial.println(text);
printEndColor(DEBUG_LEVEL_WARN);
}
}
void Logger::printStartColor(debug_level_t level)
{
switch (level)
{
case DEBUG_LEVEL_ERROR:
_serial.print(LOG_COLOR_E);
break;
case DEBUG_LEVEL_WARN:
_serial.print(LOG_COLOR_W);
break;
case DEBUG_LEVEL_INFO:
_serial.print(LOG_COLOR_I);
break;
case DEBUG_LEVEL_DEBUG:
_serial.print(LOG_COLOR_D);
break;
case DEBUG_LEVEL_VERBOSE:
_serial.print(LOG_COLOR_V);
break;
default:
break;
}
}
void Logger::printHeader(debug_level_t level, const char * file, uint32_t line, bool isln)
{
if (_printIsNewline)
{
Serial.printf("%c %25s %4d : ", levelToChar(level), file, line);
if(!isln)
{
_printIsNewline = false;
}
}
else
{
_printIsNewline = isln;
}
}
void Logger::printEndColor(debug_level_t level)
{
switch (level)
{
case DEBUG_LEVEL_ERROR:
case DEBUG_LEVEL_WARN:
case DEBUG_LEVEL_INFO:
case DEBUG_LEVEL_DEBUG:
case DEBUG_LEVEL_VERBOSE:
_serial.print(LOG_RESET_COLOR);
break;
default:
break;
}
}
char Logger::levelToChar(debug_level_t level)
{
switch (level)
{
case DEBUG_LEVEL_ERROR:
return 'E';
case DEBUG_LEVEL_WARN:
return 'W';
case DEBUG_LEVEL_INFO:
return 'I';
case DEBUG_LEVEL_DEBUG:
return 'D';
case DEBUG_LEVEL_VERBOSE:
return 'V';
default:
return ' ';
}
}

75
src/logger.h Normal file
View file

@ -0,0 +1,75 @@
#ifndef _LOGGER_H_
#define _LOGGER_H_
#include <Arduino.h>
class Logger
{
public:
enum debug_level_t {
DEBUG_LEVEL_NONE, // No debug output
DEBUG_LEVEL_ERROR, // Critical errors
DEBUG_LEVEL_WARN, // Error conditions but not critical
DEBUG_LEVEL_INFO, // Information messages
DEBUG_LEVEL_DEBUG, // Extra information - default level (if not changed)
DEBUG_LEVEL_VERBOSE, // More information than the usual
DEBUG_LEVELS_SIZE
};
static Logger & instance()
{
static Logger _instance;
return _instance;
}
~Logger() {}
void setSerial(const HardwareSerial & serial = Serial);
void setDebugLevel(debug_level_t level);
// print always:
void printA(const String & text, const char * file, uint32_t line); // always
void printE(const String & text, const char * file, uint32_t line); // error
void printlnA(const String & text, const char * file, uint32_t line); // always with new line
void printlnE(const String & text, const char * file, uint32_t line); // error with new line
// depending on verbose level:
void printV(const String & text, const char * file, uint32_t line); // verbose
void printD(const String & text, const char * file, uint32_t line); // debug
void printI(const String & text, const char * file, uint32_t line); // information
void printW(const String & text, const char * file, uint32_t line); // warning
void printlnV(const String & text, const char * file, uint32_t line); // verbose with new line
void printlnD(const String & text, const char * file, uint32_t line); // debug with new line
void printlnI(const String & text, const char * file, uint32_t line); // information with new line
void printlnW(const String & text, const char * file, uint32_t line); // warning with new line
private:
HardwareSerial & _serial;
debug_level_t _level;
bool _printIsNewline;
void printStartColor(debug_level_t level);
void printHeader(debug_level_t level, const char * file, uint32_t line, bool isln);
void printEndColor(debug_level_t level);
char levelToChar(debug_level_t level);
Logger();
Logger(const Logger &);
Logger & operator = (const Logger &);
};
#define logPrintA(text) Logger::instance().printA(text, __FILE__, __LINE__)
#define logPrintE(text) Logger::instance().printE(text, __FILE__, __LINE__)
#define logPrintlnA(text) Logger::instance().printlnA(text, __FILE__, __LINE__)
#define logPrintlnE(text) Logger::instance().printlnE(text, __FILE__, __LINE__)
#define logPrintV(text) Logger::instance().printV(text, __FILE__, __LINE__)
#define logPrintD(text) Logger::instance().printD(text, __FILE__, __LINE__)
#define logPrintI(text) Logger::instance().printI(text, __FILE__, __LINE__)
#define logPrintW(text) Logger::instance().printW(text, __FILE__, __LINE__)
#define logPrintlnV(text) Logger::instance().printlnV(text, __FILE__, __LINE__)
#define logPrintlnD(text) Logger::instance().printlnD(text, __FILE__, __LINE__)
#define logPrintlnI(text) Logger::instance().printlnI(text, __FILE__, __LINE__)
#define logPrintlnW(text) Logger::instance().printlnW(text, __FILE__, __LINE__)
#endif

View file

@ -84,4 +84,3 @@ double PowerManagement::getBatteryChargeDischargeCurrent()
} }
return -1.0 * axp.getBattDischargeCurrent(); return -1.0 * axp.getBattDischargeCurrent();
} }

View file

@ -1,28 +0,0 @@
#ifndef SETTINGS_H_
#define SETTINGS_H_
#define CALL "OE5BPA-7"
#define BEACON_MESSAGE "LoRa APRS SB Tracker test"
#define BEACON_TIMEOUT 1 // Beacon interval in Minutes. Will be overwritten by SB_ACTIVE
#define SYMBOL_CODE ">"
#define SYMBOL_OVERLAY "/"
// Freq and Mode Setup - 2021-02-17 YC1HVZ
#define FREQ_SET 433775E3 //set freq in KHz XxxXxxE3 6 digit. E3 is KHz
#define SF_SET 12 //Spreading Factor
#define CR_SET 5 //Coding Rate
#define BW_SET 125E3 //Bandwith -> E3 is khz
// SMART BEACONING PARAMETERS - 2020-11-22 DJ1AN
#define SB_ACTIVE // uncomment to enable Smart Beaconing
#define SB_TURN_MIN 25 // enter turn angle for smart direction depending beaconing (default=20)
#define SB_SLOW_RATE 300 // slow speed TX rate in s
#define SB_SLOW_SPEED 10 // slow speed in km/h
#define SB_FAST_RATE 60 // high speed TX rate in s
#define SB_FAST_SPEED 100 // fast speed in km/h
#define SB_MIN_TX_DIST 100 // minimum Distance between tx in m
#define SB_MIN_BCN 5 // minimum SB rate in s
//#define DEBUGMODE // uncomment to activate Debug Mode. Gets GPS Data over Serial.
#endif

10269
test/cumberland_01.nmea Normal file

File diff suppressed because it is too large Load diff

21
test/send_nmea.py Executable file
View file

@ -0,0 +1,21 @@
#!/bin/python
import time
import sys
import serial
f = open(sys.argv[1], "r")
ser = serial.Serial(sys.argv[2], 115200, timeout=0)
sleep_count = 0
for x in f:
s = ser.read(100)
if s:
print(s.decode(), end='')
sleep_count = sleep_count + 1
ser.write(x.encode())
if sleep_count > 2:
time.sleep(1)
sleep_count = 0
ser.close()
f.close()