Merge pull request #21 from lora-aprs/develop

create first version: 20.46.0
pull/23/head
Peter Buchegger 2020-11-15 21:56:58 +01:00 zatwierdzone przez GitHub
commit d6610484b6
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
11 zmienionych plików z 671 dodań i 213 usunięć

Wyświetl plik

@ -1,10 +1,12 @@
name: PlatformIO CI
name: Build check and build
on:
push:
branches: [ master ]
paths-ignore:
- '*.md'
pull_request:
branches: [ master ]
paths-ignore:
- '*.md'
jobs:
PlatformIO-Check:

Wyświetl plik

@ -1,14 +1,13 @@
on:
push:
# Sequence of patterns matched against refs/tags
tags:
- 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10
- 'v*'
name: Upload Release Asset
name: Upload Release Assets
jobs:
build:
name: Upload Release Asset
name: Upload Release Assets
runs-on: ubuntu-latest
steps:
- run: sudo apt-get install python3-setuptools python3-wheel
@ -25,14 +24,25 @@ jobs:
release_name: Release ${{ github.ref }}
draft: false
prerelease: false
# upload heltec_wifi_lora_32_v1
- uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: .pio/build/TrackerD-OE1ACM/firmware.bin
asset_name: TrackerD-OE1ACM.bin
asset_path: .pio/build/heltec_wifi_lora_32_v1/firmware.bin
asset_name: heltec_wifi_lora_32_v1.bin
asset_content_type: application/bin
# upload heltec_wifi_lora_32_v2
- uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: .pio/build/heltec_wifi_lora_32_v2/firmware.bin
asset_name: heltec_wifi_lora_32_v2.bin
asset_content_type: application/bin
# upload ttgo-lora32-v1
- uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@ -41,6 +51,7 @@ jobs:
asset_path: .pio/build/ttgo-lora32-v1/firmware.bin
asset_name: ttgo-lora32-v1.bin
asset_content_type: application/bin
# upload ttgo-lora32-v2
- uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@ -49,6 +60,7 @@ jobs:
asset_path: .pio/build/ttgo-lora32-v2/firmware.bin
asset_name: ttgo-lora32-v2.bin
asset_content_type: application/bin
# upload ttgo-t-beam-v0_7
- uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@ -57,6 +69,7 @@ jobs:
asset_path: .pio/build/ttgo-t-beam-v0_7/firmware.bin
asset_name: ttgo-t-beam-v0_7.bin
asset_content_type: application/bin
# upload ttgo-t-beam-v1
- uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@ -65,3 +78,12 @@ jobs:
asset_path: .pio/build/ttgo-t-beam-v1/firmware.bin
asset_name: ttgo-t-beam-v1.bin
asset_content_type: application/bin
# upload TrackerD-OE1ACM
- uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: .pio/build/TrackerD-OE1ACM/firmware.bin
asset_name: TrackerD-OE1ACM.bin
asset_content_type: application/bin

127
README.md
Wyświetl plik

@ -1,89 +1,108 @@
# LoRa APRS iGate
![PlatformIO CI](https://github.com/lora-aprs/LoRa_APRS_iGate/workflows/PlatformIO%20CI/badge.svg)
![Build check and build](https://github.com/lora-aprs/LoRa_APRS_iGate/workflows/Build%20check%20and%20build/badge.svg)
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/0b7452d5b3b747b88c736e253dda51e6)](https://app.codacy.com/gh/lora-aprs/LoRa_APRS_iGate?utm_source=github.com&utm_medium=referral&utm_content=lora-aprs/LoRa_APRS_iGate&utm_campaign=Badge_Grade_Dashboard)
The LoRa APRS iGate will work with very cheep hardware which you can buy from amazon, ebay or aliexpress.
Try it out and be part of the APRS network.
**There is a german [quick start](https://www.lora-aprs.info/docs/LoRa_APRS_iGate/quick-start-guide/) page! Take a look ;)**
![TTGO LoRa32](pics/iGate.png)
## Blog posts and Youtube videos from other Hams
* [OE1ROT](https://www.aronaut.at/2020/11/lora-aprs-gateway-mit-esp32-boards/) (blog post - german) 14.11.2020
* [DL7AG](http://online.dl7ag.de/lora-aprs-dl7ag-10/) (blog post - german) 08.11.2020
* [Manuel Lausmann - iGate](https://www.youtube.com/watch?v=C7hfVe32pXs) (youtube - german) 06.11.2020
* [Manuel Lausmann - Tracker](https://www.youtube.com/watch?v=clIlTEFbWLk) (youtube - german) 02.11.2020
* [OE1ROT](https://www.aronaut.at/2019/12/lora-aprs-tracker-mit-ttgo-t-beam-433mhz/) (blog post - german) 09.12.2019
feel free to add yours or create a ticket if you want to be added.
## Supported boards
You can use one of the Lora32 boards without changings :
You can use one of the Lora32 boards without changings:
* Heltec WiFi LoRa 32 V1 (433MHz SX1278)
* Heltec WiFi LoRa 32 V2 (433MHz SX1278)
* TTGO LoRa32 V1 (433MHz SX1278)
* TTGO LoRa32 V2 (433MHz SX1278)
* TTGO LoRa32 V2.1 (433MHz SX1278)
* TTGO T-Beam V0.7 (433MHz SX1278)
* TTGO T-Beam V1 (433MHz SX1278)
* Tracker D from [OE1ACM and OE1CGC](https://www.lora-aprs.at/)
* and sure many more...
* Heltec WiFi LoRa 32 V1 (433MHz SX1278)
* Heltec WiFi LoRa 32 V2 (433MHz SX1278)
* TTGO LoRa32 V1 (433MHz SX1278)
* TTGO LoRa32 V2 (433MHz SX1278)
* TTGO LoRa32 V2.1 (433MHz SX1278)
* TTGO T-Beam V0.7 (433MHz SX1278)
* TTGO T-Beam V1 (433MHz SX1278)
* Tracker D from [OE1ACM and OE1CGC](https://www.lora-aprs.at/)
* and sure many more...
Here are some amazon-de links for some example boards:
* [LoRa32 V1](https://www.amazon.de/dp/B07VPHYYJD)
* [LoRa32 V1](https://www.amazon.de/dp/B07QRG89ZV)
* [LoRa32 V2](https://www.amazon.de/dp/B07VL97VNH)
* [LoRa32 V2.1](https://www.amazon.de/dp/B07RXSKPBX)
* [T-Beam V1.0](https://www.amazon.de/dp/B07RT9FKPL)
* [LoRa32 V1](https://www.amazon.de/dp/B07VPHYYJD)
* [LoRa32 V1](https://www.amazon.de/dp/B07QRG89ZV)
* [LoRa32 V2](https://www.amazon.de/dp/B07VL97VNH)
* [LoRa32 V2.1](https://www.amazon.de/dp/B07RXSKPBX)
* [T-Beam V1.0](https://www.amazon.de/dp/B07RT9FKPL)
This boards cost around 20 Euros, they are very cheap and perfect for an LoRa iGate.
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 ;)**
### How to compile
The best success is to use PlatformIO.
The best success is to use PlatformIO (and it is the only platform where I can support you).
* 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.
* 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.
Here is a [Video](https://www.youtube.com/watch?v=C7hfVe32pXs&feature=emb_logo) tutorial in german language how to install the software.
### Configuration
### Dependencies
* 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!
* 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'.
* [LoRa](https://github.com/sandeepmistry/arduino-LoRa) by Sandeep Mistry
* [APRS-IS-Lib](https://github.com/peterus/APRS-IS-Lib) by Peter Buchegger
* [APRS-Decoder-Lib](https://github.com/peterus/APRS-Decoder-Lib) by Peter Buchegger
* [LoRa-APRS-Lib](https://github.com/peterus/LoRa-APRS-Lib) by Peter Buchegger
* [ArduinoJson](https://github.com/bblanchon/ArduinoJson) by Benoit Blanchon
* [AXP202X_Library](https://github.com/lewisxhe/AXP202X_Library) by Lewis He
* [Adafruit SSD1306](https://github.com/adafruit/Adafruit_SSD1306) by Adafruit (with all dependecies)
* [NTPClient](https://github.com/arduino-libraries/NTPClient) by Fabrice Weinberg
## Branches in this repository and version system
But you don't need to download all this libs, PlatformIO will take care for you ;)
This firmware is developed in a rolling release system: everyday a new release could be created. But there are still rules where new pull requests has to go and and how the version system looks like.
## Configuration
### Branches
* 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!
* 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'.
There are 2 main branches:
* *master* and
* *develop*
## LoRa APRS iGates on aprs.fi
The *master* branch has all releases and is the most stable one. With the different tags you can jump to different versions or if you take the most current *master* branch you will just get the latest, stable version. There will be no side releases which are branched of from *master*. If there is a bugfix version it will be done directly on the *master* branch and a tag will be generated with a new version.
Feel free to add a link to your iGate here:
The *develop* branch is used for new feature development. It will be also used to stabilize the current development to create a new stable version (pull request to *master* branch). **Again:** all new development (pull requests) has to go directly into the *develop* branch!
* [OE5BPA-10](https://aprs.fi/info/a/OE5BPA-10)
### Version system
If the *develop* branch is stable enough for a new release it will be merged with a pull request to the *master* branch and a new version will be generated.
The versions are based on this settings:
* major: the current year (2 digits)
* minor: the current week of the year
* patch: if there is a important fix for an release, just increment the number, otherwise 0
*example*: a new release will be created on the 11/14/2020, this version numbers will be used:
* major: 20
* minor: 46
* patch: 0
so the version will be: 20.46.0
## Future plans
* [x] show time until next beaconing
* [ ] show login issues from IS server
* [ ] add better OLED library to support multiple different OLEDs
* [x] add support to turn OLED on, off and dimming
* [ ] add support for temperature chips (BMExxx)
* [ ] add FTP server support to upload configuration
* [ ] add web server for configuration and other things
* [ ] add statistics for received packages
* [ ] show received packages on a map
* [ ] etc.
* [x] show time until next beaconing
* [ ] show login issues from IS server
* [ ] add better OLED library to support multiple different OLEDs
* [x] add support to turn OLED on, off and dimming
* [ ] add support for temperature chips (BMExxx)
* [x] add FTP server support to upload configuration
* [ ] add web server for configuration and other things
* [ ] add statistics for received packages
* [ ] show received packages on a map
* [ ] etc.
## LoRa Tracker
@ -93,7 +112,7 @@ Look at my other project: a [LoRa Tracker](https://github.com/peterus/LoRa_APRS_
### Here are some peculiarities of the different boards
* TTGO T-Beam V1
* TTGO T-Beam V1
When adding a 0,96" OLED display direct to the board you have to be careful, there are two different pinout
versions on the market.
@ -101,4 +120,4 @@ For direct mount you need a display with this Pinout -> [VCC - GND - SCL - SDA](
A direct mount of the [other display](pics/display-wrong.jpg) is not possible without damage the display!
The 'wrong' display works too but you have to change VCC and GND by wire !
feel free to add hints!
feel free to add hints!

Wyświetl plik

@ -1,5 +1,4 @@
{
"version":3,
"callsign":"NOCALL-10",
"wifi":
{
@ -47,5 +46,12 @@
"always_on": true,
"timeout":10,
"overwrite_pin":0
},
"ftp":
{
"active":false,
"user": [
{ "name":"ftp", "password":"ftp" }
]
}
}

Wyświetl plik

@ -1,6 +1,3 @@
#[platformio]
#default_envs = ttgo-lora32-v1
[env]
platform = espressif32
framework = arduino
@ -15,19 +12,21 @@ lib_deps =
sandeepmistry/LoRa @ 0.7.2
peterus/APRS-Decoder-Lib @ 0.0.5
peterus/APRS-IS-Lib @ 0.0.7
peterus/ESP-FTP-Server-Lib @ 0.9.3
peterus/LoRa-APRS-Lib @ 0.0.5
check_tool = cppcheck
check_flags =
cppcheck: --suppress=*:*.pio\* --inline-suppr
monitor_flags = --raw
# activate for OTA Update, use the CALLSIGN from is-cfg.h as upload_port:
#upload_protocol = espota
#upload_port = <CALLSIGN>.local
[env:heltec_wifi_lora_32_V1]
[env:heltec_wifi_lora_32_v1]
board = ttgo-lora32-v1
build_flags = -Werror -Wall -DHELTEC_WIFI_LORA_32_V1
[env:heltec_wifi_lora_32_V2]
[env:heltec_wifi_lora_32_v2]
board = ttgo-lora32-v1
build_flags = -Werror -Wall -DHELTEC_WIFI_LORA_32_V2

Wyświetl plik

@ -6,6 +6,11 @@
#include <WiFiUdp.h>
#include <ArduinoOTA.h>
#include <APRS-IS.h>
#include <SPIFFS.h>
#include <ESP-FTP-Server-Lib.h>
#include <FTPFilesystem.h>
#include "logger.h"
#include "LoRa_APRS.h"
@ -28,6 +33,7 @@ volatile uint secondsSinceDisplay = 0;
WiFiMulti WiFiMulti;
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, 60*60);
FTPServer ftpServer;
Configuration Config;
APRS_IS * aprs_is = 0;
LoRa_APRS lora_aprs;
@ -43,6 +49,7 @@ void setup_lora();
void setup_ntp();
void setup_aprs_is();
void setup_timer();
void setup_ftp();
std::map<uint, std::shared_ptr<APRSMessage>> lastMessages;
@ -55,20 +62,22 @@ void setup()
Wire.begin(SDA, SCL);
if (!powerManagement.begin(Wire))
{
Serial.println("LoRa-APRS / Init / AXP192 Begin PASS");
} else {
Serial.println("LoRa-APRS / Init / AXP192 Begin FAIL");
logPrintlnI("AXP192 init done!");
}
else
{
logPrintlnE("AXP192 init failed!");
}
powerManagement.activateLoRa();
powerManagement.activateOLED();
powerManagement.deactivateGPS();
#endif
setup_display();
delay(500);
Serial.println("[INFO] LoRa APRS iGate & Digi by OE5BPA (Peter Buchegger)");
show_display("OE5BPA", "LoRa APRS iGate & Digi", "by Peter Buchegger", 3000);
logPrintlnA("LoRa APRS iGate & Digi by OE5BPA (Peter Buchegger)");
logPrintlnA("Version: 1.0.0-dev");
setup_display();
show_display("OE5BPA", "LoRa APRS iGate & Digi", "by Peter Buchegger", "1.0.0-dev", 3000);
load_config();
setup_lora();
@ -77,6 +86,7 @@ void setup()
setup_wifi();
setup_ota();
setup_ntp();
setup_ftp();
}
else
{
@ -94,7 +104,7 @@ void setup()
}
delay(500);
Serial.println("setup done...");
logPrintlnI("setup done...");
secondsSinceDisplay = 0;
}
@ -112,7 +122,6 @@ void loop()
{
turn_off_display();
display_is_on = false;
Serial.println("-");
}
static bool beacon_aprs_is = Config.aprs_is.active && Config.aprs_is.beacon;
@ -133,11 +142,27 @@ void loop()
beacon_digi = true;
}
if(Config.ftp.active)
{
ftpServer.handle();
static bool configWasOpen = false;
if(configWasOpen && ftpServer.countConnections() == 0)
{
logPrintlnW("Maybe the config has been changed via FTP, lets restart now to get the new config...");
Serial.println();
ESP.restart();
}
if(ftpServer.countConnections() > 0)
{
configWasOpen = true;
}
}
if(Config.wifi.active) ArduinoOTA.handle();
if(Config.wifi.active && WiFiMulti.run() != WL_CONNECTED)
{
setup_display(); secondsSinceDisplay = 0; display_is_on = true;
Serial.println("[ERROR] WiFi not connected!");
logPrintlnE("WiFi not connected!");
show_display("ERROR", "WiFi not connected!");
delay(1000);
return;
@ -145,26 +170,26 @@ void loop()
if(Config.aprs_is.active && !aprs_is->connected())
{
setup_display(); secondsSinceDisplay = 0; display_is_on = true;
Serial.print("[INFO] connecting to server: ");
Serial.print(Config.aprs_is.server);
Serial.print(" on port: ");
Serial.println(Config.aprs_is.port);
show_display("INFO", "Connecting to server");
logPrintI("connecting to APRS-IS server: ");
logPrintI(Config.aprs_is.server);
logPrintI(" on port: ");
logPrintlnI(String(Config.aprs_is.port));
show_display("INFO", "Connecting to APRS-IS server");
if(!aprs_is->connect(Config.aprs_is.server, Config.aprs_is.port))
{
Serial.println("[ERROR] Connection failed.");
Serial.println("[INFO] Waiting 5 seconds before retrying...");
logPrintlnE("Connection failed.");
logPrintlnI("Waiting 5 seconds before retrying...");
show_display("ERROR", "Server connection failed!", "waiting 5 sec");
delay(5000);
return;
}
Serial.println("[INFO] Connected to server!");
logPrintlnI("Connected to APRS-IS server!");
}
if(Config.aprs_is.active && aprs_is->available() > 0)
{
String str = aprs_is->getMessage();
Serial.print("[" + timeClient.getFormattedTime() + "] ");
Serial.println(str);
logPrintD("[" + timeClient.getFormattedTime() + "] ");
logPrintlnD(str);
}
if(lora_aprs.hasMessage())
{
@ -172,13 +197,13 @@ void loop()
setup_display(); secondsSinceDisplay = 0; display_is_on = true;
show_display(Config.callsign, timeClient.getFormattedTime() + " LoRa", "RSSI: " + String(lora_aprs.packetRssi()) + ", SNR: " + String(lora_aprs.packetSnr()), msg->toString());
Serial.print("[" + timeClient.getFormattedTime() + "] ");
Serial.print(" Received packet '");
Serial.print(msg->toString());
Serial.print("' with RSSI ");
Serial.print(lora_aprs.packetRssi());
Serial.print(" and SNR ");
Serial.println(lora_aprs.packetSnr());
logPrintD("[" + timeClient.getFormattedTime() + "] ");
logPrintD(" Received packet '");
logPrintD(msg->toString());
logPrintD("' with RSSI ");
logPrintD(String(lora_aprs.packetRssi()));
logPrintD(" and SNR ");
logPrintlnD(String(lora_aprs.packetSnr()));
if(Config.aprs_is.active)
{
@ -188,12 +213,12 @@ void loop()
{
if(msg->getSource().indexOf(Config.callsign) != -1)
{
Serial.print("Message already received as repeater: '");
Serial.print(msg->toString());
Serial.print("' with RSSI ");
Serial.print(lora_aprs.packetRssi());
Serial.print(" and SNR ");
Serial.println(lora_aprs.packetSnr());
logPrintD("Message already received as repeater: '");
logPrintD(msg->toString());
logPrintD("' with RSSI ");
logPrintD(String(lora_aprs.packetRssi()));
logPrintD(" and SNR ");
logPrintlnD(String(lora_aprs.packetSnr()));
return;
}
@ -213,24 +238,24 @@ void loop()
{
setup_display(); secondsSinceDisplay = 0; display_is_on = true;
show_display(Config.callsign, "RSSI: " + String(lora_aprs.packetRssi()) + ", SNR: " + String(lora_aprs.packetSnr()), msg->toString(), 0);
Serial.print("Received packet '");
Serial.print(msg->toString());
Serial.print("' with RSSI ");
Serial.print(lora_aprs.packetRssi());
Serial.print(" and SNR ");
Serial.println(lora_aprs.packetSnr());
logPrintD("Received packet '");
logPrintD(msg->toString());
logPrintD("' with RSSI ");
logPrintD(String(lora_aprs.packetRssi()));
logPrintD(" and SNR ");
logPrintlnD(String(lora_aprs.packetSnr()));
msg->setPath(String(Config.callsign) + "*");
lora_aprs.sendMessage(msg);
lastMessages.insert({secondsSinceStartup, msg});
}
else
{
Serial.print("Message already received (timeout): '");
Serial.print(msg->toString());
Serial.print("' with RSSI ");
Serial.print(lora_aprs.packetRssi());
Serial.print(" and SNR ");
Serial.println(lora_aprs.packetSnr());
logPrintD("Message already received (timeout): '");
logPrintD(msg->toString());
logPrintD("' with RSSI ");
logPrintD(String(lora_aprs.packetRssi()));
logPrintD(" and SNR ");
logPrintlnD(String(lora_aprs.packetSnr()));
}
return;
}
@ -254,19 +279,19 @@ void loop()
beacon_digi = false;
setup_display(); secondsSinceDisplay = 0; display_is_on = true;
show_display(Config.callsign, "Beacon to HF...");
Serial.print("[" + timeClient.getFormattedTime() + "] ");
Serial.print(BeaconMsg->encode());
logPrintD("[" + timeClient.getFormattedTime() + "] ");
logPrintlnD(BeaconMsg->encode());
lora_aprs.sendMessage(BeaconMsg);
Serial.println("finished TXing...");
logPrintlnD("finished TXing...");
show_display(Config.callsign, "Standby...");
}
if(beacon_aprs_is)
{
beacon_aprs_is = false;
setup_display(); secondsSinceDisplay = 0; display_is_on = true;
show_display(Config.callsign, "Beacon to APRS IS Server...");
Serial.print("[" + timeClient.getFormattedTime() + "] ");
Serial.print(BeaconMsg->encode());
show_display(Config.callsign, "Beacon to APRS-IS Server...");
logPrintD("[" + timeClient.getFormattedTime() + "] ");
logPrintlnD(BeaconMsg->encode());
aprs_is->sendMessage(BeaconMsg);
show_display(Config.callsign, "Standby...");
}
@ -278,7 +303,7 @@ void load_config()
Config = confmg.readConfiguration();
if(Config.callsign == "NOCALL-10")
{
Serial.println("[ERROR] You have to change your settings in 'data/is-cfg.json' and upload it via \"Upload File System image\"!");
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)
{}
@ -286,7 +311,7 @@ void load_config()
if(Config.aprs_is.active && !Config.wifi.active)
{
Serial.println("[ERROR] You have to activate Wifi for APRS IS to work, please check your settings!");
logPrintlnE("You have to activate Wifi for APRS IS to work, please check your settings!");
show_display("ERROR", "You have to activate Wifi for APRS IS to work, please check your settings!");
while (true)
{}
@ -296,6 +321,7 @@ void load_config()
{
Config.display.overwritePin = KEY_BUILTIN;
}
logPrintlnI("Configuration loaded!");
}
void setup_wifi()
@ -304,20 +330,20 @@ void setup_wifi()
WiFi.setHostname(Config.callsign.c_str());
for(Configuration::Wifi::AP ap : Config.wifi.APs)
{
logPrintD("Looking for AP: ");
logPrintlnD(ap.SSID);
WiFiMulti.addAP(ap.SSID.c_str(), ap.password.c_str());
}
Serial.print("[INFO] Waiting for WiFi");
logPrintlnI("Waiting for WiFi");
show_display("INFO", "Waiting for WiFi");
while(WiFiMulti.run() != WL_CONNECTED)
{
Serial.print(".");
show_display("INFO", "Waiting for WiFi", "....");
delay(500);
}
Serial.println("");
Serial.println("[INFO] WiFi connected");
Serial.print("[INFO] IP address: ");
Serial.println(WiFi.localIP());
logPrintlnI("WiFi connected");
logPrintD("IP address: ");
logPrintlnD(WiFi.localIP().toString());
show_display("INFO", "WiFi connected", "IP: ", WiFi.localIP().toString(), 2000);
}
@ -358,6 +384,7 @@ void setup_ota()
});
ArduinoOTA.setHostname(Config.callsign.c_str());
ArduinoOTA.begin();
logPrintlnI("OTA init done!");
}
void setup_lora()
@ -366,7 +393,7 @@ void setup_lora()
lora_aprs.setTxFrequency(Config.lora.frequencyTx);
if (!lora_aprs.begin(lora_aprs.getRxFrequency()))
{
Serial.println("[ERROR] Starting LoRa failed!");
logPrintlnE("Starting LoRa failed!");
show_display("ERROR", "Starting LoRa failed!");
while (1);
}
@ -374,7 +401,7 @@ void setup_lora()
lora_aprs.setSpreadingFactor(Config.lora.spreadingFactor);
lora_aprs.setSignalBandwidth(Config.lora.signalBandwidth);
lora_aprs.setCodingRate4(Config.lora.codingRate4);
Serial.println("[INFO] LoRa init done!");
logPrintlnI("LoRa init done!");
show_display("INFO", "LoRa init done!", 2000);
BeaconMsg = std::shared_ptr<APRSMessage>(new APRSMessage());
@ -388,12 +415,12 @@ void setup_lora()
void setup_ntp()
{
timeClient.begin();
if(!timeClient.forceUpdate())
while(!timeClient.forceUpdate())
{
Serial.println("[WARN] NTP Client force update issue!");
show_display("WARN", "NTP Client force update issue!", 2000);
logPrintlnW("NTP Client force update issue! Waiting 1 sek...");
show_display("WARN", "NTP Client force update issue! Waiting 1 sek...", 1000);
}
Serial.println("[INFO] NTP Client init done!");
logPrintlnI("NTP Client init done!");
show_display("INFO", "NTP Client init done!", 2000);
}
@ -420,6 +447,23 @@ void setup_timer()
timerAlarmEnable(timer);
}
void setup_ftp()
{
if(!Config.ftp.active)
{
return;
}
for(Configuration::Ftp::User user : Config.ftp.users)
{
logPrintD("Adding user to FTP Server: ");
logPrintlnD(user.name);
ftpServer.addUser(user.name, user.password);
}
ftpServer.addFilesystem("SPIFFS", &SPIFFS);
ftpServer.begin();
logPrintlnI("FTP Server init done!");
}
String create_lat_aprs(double lat)
{
char str[20];

Wyświetl plik

@ -1,26 +1,20 @@
#include "SPIFFS.h"
#include <SPIFFS.h>
#include "configuration.h"
#include "logger.h"
ConfigurationManagement::ConfigurationManagement(String FilePath)
: mFilePath(FilePath)
{
if(!SPIFFS.begin(true))
{
Serial.println("[ERROR] Mounting SPIFFS was not possible. Trying to format SPIFFS...");
logPrintlnE("Mounting SPIFFS was not possible. Trying to format SPIFFS...");
SPIFFS.format();
if(!SPIFFS.begin())
{
Serial.println("[ERROR] Formating SPIFFS was not okay!");
return;
logPrintlnE("Formating SPIFFS was not okay!");
}
}
if(!SPIFFS.exists(mFilePath))
{
Configuration conf;
writeConfiguration(conf);
}
}
Configuration ConfigurationManagement::readConfiguration()
@ -28,54 +22,79 @@ Configuration ConfigurationManagement::readConfiguration()
File file = SPIFFS.open(mFilePath);
if(!file)
{
Serial.println("Failed to open file for reading...");
logPrintlnE("Failed to open file for reading...");
return Configuration();
}
DynamicJsonDocument data(1024);
deserializeJson(data, file);
DynamicJsonDocument data(2048);
DeserializationError error = deserializeJson(data, file);
if(error)
{
logPrintlnW("Failed to read file, using default configuration.");
}
//serializeJson(data, Serial);
//Serial.println();
file.close();
Configuration conf;
conf.callsign = data["callsign"].as<String>();
conf.wifi.active = data["wifi"]["active"];
JsonArray aps = data["wifi"]["AP"].as<JsonArray>();
if(data.containsKey("callsign"))
conf.callsign = data["callsign"].as<String>();
conf.wifi.active = data["wifi"]["active"] | false;
JsonArray aps = data["wifi"]["AP"].as<JsonArray>();
for(JsonVariant v : aps)
{
Configuration::Wifi::AP ap;
ap.SSID = v["SSID"].as<String>();
ap.password = v["password"].as<String>();
ap.SSID = v["SSID"].as<String>();
ap.password = v["password"].as<String>();
conf.wifi.APs.push_back(ap);
}
conf.beacon.message = data["beacon"]["message"].as<String>();
conf.beacon.positionLatitude = data["beacon"]["position"]["latitude"];
conf.beacon.positionLongitude = data["beacon"]["position"]["longitude"];
conf.aprs_is.active = data["aprs_is"]["active"];
conf.aprs_is.password = data["aprs_is"]["password"].as<String>();
conf.aprs_is.server = data["aprs_is"]["server"].as<String>();
conf.aprs_is.port = data["aprs_is"]["port"];
conf.aprs_is.beacon = data["aprs_is"]["beacon"];
conf.aprs_is.beaconTimeout = data["aprs_is"]["beacon_timeout"];
conf.digi.active = data["digi"]["active"];
conf.digi.forwardTimeout = data["digi"]["forward_timeout"];
conf.digi.beacon = data["digi"]["beacon"];
conf.digi.beaconTimeout = data["digi"]["beacon_timeout"];
if(data.containsKey("beacon") && data["beacon"].containsKey("message"))
conf.beacon.message = data["beacon"]["message"].as<String>();
conf.beacon.positionLatitude = data["beacon"]["position"]["latitude"] | 0.0;
conf.beacon.positionLongitude = data["beacon"]["position"]["longitude"] | 0.0;
conf.aprs_is.active = data["aprs_is"]["active"] | false;
if(data.containsKey("aprs_is") && data["aprs_is"].containsKey("password"))
conf.aprs_is.password = data["aprs_is"]["password"].as<String>();
if(data.containsKey("aprs_is") && data["aprs_is"].containsKey("server"))
conf.aprs_is.server = data["aprs_is"]["server"].as<String>();
conf.aprs_is.port = data["aprs_is"]["port"] | 14580;
conf.aprs_is.beacon = data["aprs_is"]["beacon"] | true;
conf.aprs_is.beaconTimeout = data["aprs_is"]["beacon_timeout"] | 15;
conf.digi.active = data["digi"]["active"] | false;
conf.digi.forwardTimeout = data["digi"]["forward_timeout"] | 5;
conf.digi.beacon = data["digi"]["beacon"] | true;
conf.digi.beaconTimeout = data["digi"]["beacon_timeout"] | 30;
if(data["version"] >= 2)
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;
conf.display.alwaysOn = data["display"]["always_on"] | true;
conf.display.timeout = data["display"]["timeout"] | 10;
conf.display.overwritePin = data["display"]["overwrite_pin"] | 0;
conf.ftp.active = data["ftp"]["active"] | false;
JsonArray users = data["ftp"]["user"].as<JsonArray>();
for(JsonVariant u : users)
{
conf.lora.frequencyRx = data["lora"]["frequency_rx"];
conf.lora.frequencyTx = data["lora"]["frequency_tx"];
conf.lora.power = data["lora"]["power"];
conf.display.alwaysOn = data["display"]["always_on"];
conf.display.timeout = data["display"]["timeout"];
conf.display.overwritePin = data["display"]["overwrite_pin"];
Configuration::Ftp::User us;
us.name = u["name"].as<String>();
us.password = u["password"].as<String>();
conf.ftp.users.push_back(us);
}
if(data["version"] >= 3)
if(conf.ftp.users.empty())
{
conf.lora.spreadingFactor = data["lora"]["spreading_factor"];
conf.lora.signalBandwidth = data["lora"]["signal_bandwidth"];
conf.lora.codingRate4 = data["lora"]["coding_rate4"];
Configuration::Ftp::User us;
us.name = "ftp";
us.password = "ftp";
conf.ftp.users.push_back(us);
}
// update config in memory to get the new fields:
writeConfiguration(conf);
return conf;
}
@ -84,47 +103,53 @@ void ConfigurationManagement::writeConfiguration(Configuration conf)
File file = SPIFFS.open(mFilePath, "w");
if(!file)
{
Serial.println("Failed to open file for writing...");
logPrintlnE("Failed to open file for writing...");
return;
}
DynamicJsonDocument data(1024);
DynamicJsonDocument data(2048);
data["version"] = conf.version;
data["callsign"] = conf.callsign;
data["wifi"]["active"] = conf.wifi.active;
JsonArray aps = data["wifi"]["AP"].to<JsonArray>();
data["callsign"] = conf.callsign;
data["wifi"]["active"] = conf.wifi.active;
JsonArray aps = data["wifi"].createNestedArray("AP");
for(Configuration::Wifi::AP ap : conf.wifi.APs)
{
JsonVariant v;
v["SSID"] = ap.SSID;
v["password"] = ap.password;
aps.add(v);
JsonObject v = aps.createNestedObject();
v["SSID"] = ap.SSID;
v["password"] = ap.password;
}
data["beacon"]["message"] = conf.beacon.message;
data["beacon"]["position"]["latitude"] = conf.beacon.positionLatitude;
data["beacon"]["position"]["longitude"] = conf.beacon.positionLongitude;
data["aprs_is"]["active"] = conf.aprs_is.active;
data["aprs_is"]["password"] = conf.aprs_is.password;
data["aprs_is"]["server"] = conf.aprs_is.server;
data["aprs_is"]["port"] = conf.aprs_is.port;
data["aprs_is"]["beacon"] = conf.aprs_is.beacon;
data["aprs_is"]["beacon_timeout"] = conf.aprs_is.beaconTimeout;
data["digi"]["active"] = conf.digi.active;
data["digi"]["forward_timeout"] = conf.digi.forwardTimeout;
data["digi"]["beacon"] = conf.digi.beacon;
data["digi"]["beacon_timeout"] = conf.digi.beaconTimeout;
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;
data["display"]["always_on"] = conf.display.alwaysOn;
data["display"]["timeout"] = conf.display.timeout;
data["display"]["overwrite_pin"] = conf.display.overwritePin;
data["ftp"]["active"] = conf.ftp.active;
JsonArray users = data["ftp"].createNestedArray("user");
for(Configuration::Ftp::User u : conf.ftp.users)
{
JsonObject v = users.createNestedObject();
v["name"] = u.name;
v["password"] = u.password;
}
data["beacon"]["message"] = conf.beacon.message;
data["beacon"]["position"]["latitude"] = conf.beacon.positionLatitude;
data["beacon"]["position"]["longitude"] = conf.beacon.positionLongitude;
data["aprs_is"]["active"] = conf.aprs_is.active;
data["aprs_is"]["password"] = conf.aprs_is.password;
data["aprs_is"]["server"] = conf.aprs_is.server;
data["aprs_is"]["port"] = conf.aprs_is.port;
data["aprs_is"]["beacon"] = conf.aprs_is.beacon;
data["aprs_is"]["beacon_timeout"] = conf.aprs_is.beaconTimeout;
data["digi"]["active"] = conf.digi.active;
data["digi"]["forward_timeout"] = conf.digi.forwardTimeout;
data["digi"]["beacon"] = conf.digi.beacon;
data["digi"]["beacon_timeout"] = conf.digi.beaconTimeout;
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;
data["display"]["always_on"] = conf.display.alwaysOn;
data["display"]["timeout"] = conf.display.timeout;
data["display"]["overwrite_pin"] = conf.display.overwritePin;
serializeJson(data, file);
serializeJson(data, Serial);
Serial.println();
//serializeJson(data, Serial);
//Serial.println();
file.close();
}

Wyświetl plik

@ -82,9 +82,24 @@ public:
int overwritePin;
};
Configuration() : version(2), callsign("NOCALL-10") {};
class Ftp
{
public:
class User
{
public:
String name;
String password;
};
Ftp() : active(false) {}
bool active;
std::list<User> users;
};
Configuration() : callsign("NOCALL-10") {};
int version;
String callsign;
Wifi wifi;
Beacon beacon;
@ -92,6 +107,7 @@ public:
Digi digi;
LoRa lora;
Display display;
Ftp ftp;
};
class ConfigurationManagement

Wyświetl plik

@ -5,6 +5,7 @@
#include "display.h"
#include "pins.h"
#include "logger.h"
Adafruit_SSD1306 display(128, 64, &Wire, OLED_RST);
@ -18,9 +19,10 @@ void setup_display()
Wire.begin(OLED_SDA, OLED_SCL);
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3c, false, false))
{
Serial.println("SSD1306 allocation failed");
logPrintlnE("SSD1306 allocation failed");
while (1);
}
logPrintlnI("Display init done!");
}
void turn_off_display()

248
src/logger.cpp 100644
Wyświetl plik

@ -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 100644
Wyświetl plik

@ -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