This commit is contained in:
Ricardo Guzman (Richonguzman) 2023-05-29 12:25:22 -04:00 committed by GitHub
parent af12cb8105
commit 60dfc6b54e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 1665 additions and 0 deletions

49
README.md Normal file
View File

@ -0,0 +1,49 @@
# Richonguzman / CD2RXU LoRa APRS Tracker/Station
# (Firmware for Tx and Rx !!!)
NOTE: To take advantage of Tx/Rx capabilities you should have an Tx/R x LoRa iGate (near you) like:
https://github.com/richonguzman/LoRa_APRS_iGate
____________________________________________________
Recent improvements:
- NOW WE HAVE A MENU (just pushing the central button IO38) !!!
- Saving, Reading and Deleting Messages.
- Asking Weather Report
- Processor from 240Mhz to 80MHz to save almost 20% power consumption (from ~ 100mA to almost ~80mA) (Thanks Mane76).
- 4th line of the OLED SCREEN shows New Messages Received.
- 5th line of the OLED SCREEN shows Recent Heard Trackers/Station/iGates Tx.
____________________________________________________
# MENU EXPLANATION
on the Tracker Screen/Menu 0:
- 1 short press/push = Forced GPS Beacon Tx
- 1 long press/push = Change between three Callsigns saved on "/data/tracker.json".
- 2 short press/pushes = Menu 1 (where you cand read Messages)
on the Menu 1:
- 1 short press/push = Read Received Messages saved on internal Memory.
- 1 long press/push = Delete all Messages from internal Memory.
- 2 short press/pushes = Menu 2 (where you cand ask for Weather Report and more).
on the Menu 2:
- 1 short press/push = Ask for Weather Report (WX report will arrive in seconds).
- 1 long press/push = Listen to other Trackers (working on it!!!).
- 2 short press/pushes = Menu 0 (back to the Tracker Screen).
____________________________________________________
Versions:
- 2023.04.16 Sending and Receiving LoRa Packets.
- 2023.05.12 Saving Messages to Internal Memory.
- 2023.05.14 Adding Menu.
- 2023.05.21 Adding Last-Heard LoRa Stations/Trackers
- 2023.05.29 Adding Altitude + Speed or Course + Speed in the encoded GPS info.
____________________________________________________
This code was inspided by OE5BPA LoRa Tracker and uses byte-saving part of the APRS 434 firmware published by Serge Y. Stroobandt, ON4AA.
- https://github.com/aprs434/lora.tracker
- https://github.com/lora-aprs/LoRa_APRS_Tracker
____________________________________________________
# Hope You Enjoy this, 73 !! CD2RXU , Valparaiso, Chile

78
data/tracker_config.json Normal file
View File

@ -0,0 +1,78 @@
{
"beacons": [
{
"callsign": "CD2RXU-7",
"destination": "APLR01",
"path": "WIDE1-1",
"symbol": "[",
"overlay": "/",
"comment": "github.com/richonguzman/LoRa_APRS_Tracker",
"smart_beacon": {
"active": true,
"slowRate": 120,
"slowSpeed": 3,
"fastRate": 60,
"fastSpeed": 15,
"minTxDist": 50,
"minDeltaBeacon": 20,
"turnMinDeg": 12,
"turnSlope": 60
}
},
{
"callsign": "CD2RXU-8",
"destination": "APLR01",
"path": "WIDE1-1",
"symbol": "b",
"overlay": "/",
"comment": "github.com/richonguzman/LoRa_APRS_Tracker",
"smart_beacon": {
"active": true,
"slowRate": 120,
"slowSpeed": 5,
"fastRate": 60,
"fastSpeed": 40,
"minTxDist": 100,
"minDeltaBeacon": 12,
"turnMinDeg": 12,
"turnSlope": 60
}
},
{
"callsign": "CD2RXU-9",
"destination": "APLR01",
"path": "WIDE1-1",
"symbol": ">",
"overlay": "/",
"comment": "github.com/richonguzman/LoRa_APRS_Tracker",
"smart_beacon": {
"active": true,
"slowRate": 120,
"slowSpeed": 10,
"fastRate": 60,
"fastSpeed": 70,
"minTxDist": 100,
"minDeltaBeacon": 12,
"turnMinDeg": 10,
"turnSlope": 80
}
}
],
"lora": {
"frequency": 433775000,
"spreadingFactor": 12,
"signalBandwidth": 125000,
"codingRate4": 5,
"power": 20
},
"other": {
"nonSmartBeaconRate": 15,
"listeningTrackerTime": 30,
"maxDistanceToTracker": 30,
"defaultStatusAfterBoot" : true,
"defaultStatus": "https://github.com/richonguzman/LoRa_APRS_Tracker",
"ecoMode": false,
"standingUpdateTime": 5,
"sendAltitude": true
}
}

32
platformio.ini Normal file
View File

@ -0,0 +1,32 @@
[platformio]
default_envs = ttgo-t-beam-v1
[env]
platform = espressif32 @ 6.2.0
framework = arduino
lib_ldf_mode = deep+
monitor_speed = 115200
monitor_filters = esp32_exception_decoder
lib_deps =
adafruit/Adafruit GFX Library @ 1.11.5
adafruit/Adafruit SSD1306 @ 2.5.7
bblanchon/ArduinoJson @ 6.21.2
lewisxhe/AXP202X_Library @ 1.1.3
sandeepmistry/LoRa @ 0.8.0
peterus/APRS-Decoder-Lib @ 0.0.6
mikalhart/TinyGPSPlus @ 1.0.3
paulstoffregen/Time @ 1.6
shaggydog/OneButton @ 1.5.0
peterus/esp-logger @ 1.0.0
check_tool = cppcheck
check_flags =
cppcheck: --suppress=*:*.pio\* --inline-suppr -DCPPCHECK
check_skip_packages = yes
[env:ttgo-t-beam-v1]
board = ttgo-t-beam
build_flags = -Werror -Wall -DTTGO_T_Beam_V1_0
[env:ttgo-t-beam-v0_7]
board = ttgo-t-beam
build_flags = -Werror -Wall -DTTGO_T_Beam_V0_7

1068
src/LoRa_APRS_Tracker.cpp Normal file

File diff suppressed because it is too large Load Diff

118
src/configuration.h Normal file
View File

@ -0,0 +1,118 @@
#ifndef CONFIGURATION_H_
#define CONFIGURATION_H_
#include <Arduino.h>
#include <SPIFFS.h>
#include <FS.h>
#include <ArduinoJson.h>
#include <vector>
class Beacon {
public:
String callsign;
String destination;
String path;
String symbol;
String overlay;
String comment;
bool smartBeaconState;
int slowRate;
int slowSpeed;
int fastRate;
int fastSpeed;
int minTxDist;
int minDeltaBeacon;
int turnMinDeg;
int turnSlope;
};
class LoraModule {
public:
long frequency;
int spreadingFactor;
long signalBandwidth;
int codingRate4;
int power;
};
class Configuration {
public:
std::vector<Beacon> beacons;
LoraModule loramodule;
int nonSmartBeaconRate;
int listeningTrackerTime;
int maxDistanceToTracker;
bool defaultStatusAfterBoot;
String defaultStatus;
bool ecoMode;
bool standingUpdate;
int standingUpdateTime;
bool sendAltitude;
Configuration(String &filePath) {
_filePath = filePath;
if (!SPIFFS.begin(false)) {
Serial.println("SPIFFS Mount Failed");
return;
}
readFile(SPIFFS, _filePath.c_str());
}
private:
String _filePath;
void readFile(fs::FS &fs, const char *fileName) {
StaticJsonDocument<2048> data;
File configFile = fs.open(fileName, "r");
DeserializationError error = deserializeJson(data, configFile);
if (error) {
Serial.println("Failed to read file, using default configuration");
}
JsonArray BeaconsArray = data["beacons"];
for (int i = 0; i < BeaconsArray.size(); i++) {
Beacon bcn;
bcn.callsign = BeaconsArray[i]["callsign"].as<String>();
bcn.destination = BeaconsArray[i]["destination"].as<String>();
bcn.path = BeaconsArray[i]["path"].as<String>();
bcn.symbol = BeaconsArray[i]["symbol"].as<String>();
bcn.overlay = BeaconsArray[i]["overlay"].as<String>();
bcn.comment = BeaconsArray[i]["comment"].as<String>();
bcn.smartBeaconState = BeaconsArray[i]["smart_beacon"]["active"].as<bool>();
bcn.slowRate = BeaconsArray[i]["smart_beacon"]["slowRate"].as<int>();
bcn.slowSpeed = BeaconsArray[i]["smart_beacon"]["slowSpeed"].as<int>();
bcn.fastRate = BeaconsArray[i]["smart_beacon"]["fastRate"].as<int>();
bcn.fastSpeed = BeaconsArray[i]["smart_beacon"]["fastSpeed"].as<int>();
bcn.minTxDist = BeaconsArray[i]["smart_beacon"]["minTxDist"].as<int>();
bcn.minDeltaBeacon = BeaconsArray[i]["smart_beacon"]["minDeltaBeacon"].as<int>();
bcn.turnMinDeg = BeaconsArray[i]["smart_beacon"]["turnMinDeg"].as<int>();
bcn.turnSlope = BeaconsArray[i]["smart_beacon"]["turnSlope"].as<int>();
beacons.push_back(bcn);
}
loramodule.frequency = data["lora"]["frequency"].as<long>();
loramodule.spreadingFactor = data["lora"]["spreadingFactor"].as<int>();
loramodule.signalBandwidth = data["lora"]["signalBandwidth"].as<long>();
loramodule.codingRate4 = data["lora"]["codingRate4"].as<int>();
loramodule.power = data["lora"]["power"].as<int>();
nonSmartBeaconRate = data["other"]["nonSmartBeaconRate"].as<int>();
listeningTrackerTime = data["other"]["listeningTrackerTime"].as<int>();
maxDistanceToTracker = data["other"]["maxDistanceToTracker"].as<int>();
defaultStatusAfterBoot = data["other"]["defaultStatusAfterBoot"].as<bool>();
defaultStatus = data["other"]["defaultStatus"].as<String>();
ecoMode = data["other"]["ecoMode "].as<bool>();
standingUpdateTime = data["other"]["standingUpdateTime"].as<int>();
sendAltitude = data["other"]["sendAltitude"].as<bool>();
configFile.close();
}
};
#endif

158
src/display.cpp Normal file
View File

@ -0,0 +1,158 @@
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Wire.h>
#include <logger.h>
#include "display.h"
#include "pins.h"
extern logging::Logger logger;
Adafruit_SSD1306 display(128, 64, &Wire, OLED_RST);
// cppcheck-suppress unusedFunction
void setup_display() {
pinMode(OLED_RST, OUTPUT);
digitalWrite(OLED_RST, LOW);
delay(20);
digitalWrite(OLED_RST, HIGH);
Wire.begin(OLED_SDA, OLED_SCL);
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3c, false, false)) {
logger.log(logging::LoggerLevel::LOGGER_LEVEL_ERROR, "SSD1306", "allocation failed!");
while (true) {
}
}
display.clearDisplay();
display.setTextColor(WHITE);
display.setTextSize(1);
display.setCursor(0, 0);
display.print("LORA SENDER ");
display.ssd1306_command(SSD1306_SETCONTRAST);
display.ssd1306_command(1);
display.display();
}
// cppcheck-suppress unusedFunction
void display_toggle(bool toggle) {
if (toggle) {
display.ssd1306_command(SSD1306_DISPLAYON);
} else {
display.ssd1306_command(SSD1306_DISPLAYOFF);
}
}
// cppcheck-suppress unusedFunction
void show_display(String header, int wait) {
display.clearDisplay();
display.setTextColor(WHITE);
display.setTextSize(2);
display.setCursor(0, 0);
display.println(header);
display.ssd1306_command(SSD1306_SETCONTRAST);
display.ssd1306_command(1);
display.display();
delay(wait);
}
// cppcheck-suppress unusedFunction
void show_display(String header, String line1, int wait) {
display.clearDisplay();
display.setTextColor(WHITE);
display.setTextSize(2);
display.setCursor(0, 0);
display.println(header);
display.setTextSize(1);
display.setCursor(0, 16);
display.println(line1);
display.ssd1306_command(SSD1306_SETCONTRAST);
display.ssd1306_command(1);
display.display();
delay(wait);
}
// cppcheck-suppress unusedFunction
void show_display(String header, String line1, String line2, int wait) {
display.clearDisplay();
display.setTextColor(WHITE);
display.setTextSize(2);
display.setCursor(0, 0);
display.println(header);
display.setTextSize(1);
display.setCursor(0, 16);
display.println(line1);
display.setCursor(0, 26);
display.println(line2);
display.ssd1306_command(SSD1306_SETCONTRAST);
display.ssd1306_command(1);
display.display();
delay(wait);
}
// cppcheck-suppress unusedFunction
void show_display(String header, String line1, String line2, String line3, int wait) {
display.clearDisplay();
display.setTextColor(WHITE);
display.setTextSize(2);
display.setCursor(0, 0);
display.println(header);
display.setTextSize(1);
display.setCursor(0, 16);
display.println(line1);
display.setCursor(0, 26);
display.println(line2);
display.setCursor(0, 36);
display.println(line3);
display.ssd1306_command(SSD1306_SETCONTRAST);
display.ssd1306_command(1);
display.display();
delay(wait);
}
// cppcheck-suppress unusedFunction
void show_display(String header, String line1, String line2, String line3, String line4, int wait) {
display.clearDisplay();
display.setTextColor(WHITE);
display.setTextSize(2);
display.setCursor(0, 0);
display.println(header);
display.setTextSize(1);
display.setCursor(0, 16);
display.println(line1);
display.setCursor(0, 26);
display.println(line2);
display.setCursor(0, 36);
display.println(line3);
display.setCursor(0, 46);
display.println(line4);
display.ssd1306_command(SSD1306_SETCONTRAST);
display.ssd1306_command(1);
display.display();
delay(wait);
}
// cppcheck-suppress unusedFunction
void show_display(String header, String line1, String line2, String line3, String line4, String line5, int wait) {
display.clearDisplay();
display.setTextColor(WHITE);
display.setTextSize(2);
display.setCursor(0, 0);
display.println(header);
display.setTextSize(1);
display.setCursor(0, 16);
display.println(line1);
display.setCursor(0, 26);
display.println(line2);
display.setCursor(0, 36);
display.println(line3);
display.setCursor(0, 46);
display.println(line4);
display.setCursor(0, 56);
display.println(line5);
display.ssd1306_command(SSD1306_SETCONTRAST);
display.ssd1306_command(1);
display.display();
delay(wait);
}

15
src/display.h Normal file
View File

@ -0,0 +1,15 @@
#ifndef DISPLAY_H_
#define DISPLAY_H_
void setup_display();
void display_toggle(bool toggle);
void show_display(String header, int wait = 0);
void show_display(String header, String line1, int wait = 0);
void show_display(String header, String line1, String line2, int wait = 0);
void show_display(String header, String line1, String line2, String line3, int wait = 0);
void show_display(String header, String line1, String line2, String line3, String line4, int wait = 0);
void show_display(String header, String line1, String line2, String line3, String line4, String line5, int wait = 0);
#endif

24
src/pins.h Normal file
View File

@ -0,0 +1,24 @@
#ifndef PINS_H_
#define PINS_H_
#undef OLED_SDA
#undef OLED_SCL
#undef OLED_RST
#define OLED_SDA 21
#define OLED_SCL 22
#define OLED_RST 16
#define BUTTON_PIN 38 // The middle button GPIO on the T-Beam
#ifdef TTGO_T_Beam_V0_7
#define GPS_RX 15
#define GPS_TX 12
#endif
#ifdef TTGO_T_Beam_V1_0
#define GPS_RX 12
#define GPS_TX 34
#endif
#endif

86
src/power_management.cpp Normal file
View File

@ -0,0 +1,86 @@
#include "power_management.h"
// cppcheck-suppress uninitMemberVar
PowerManagement::PowerManagement() {
}
// cppcheck-suppress unusedFunction
bool PowerManagement::begin(TwoWire &port) {
bool result = axp.begin(port, AXP192_SLAVE_ADDRESS);
if (!result) {
axp.setDCDC1Voltage(3300);
}
return result;
}
// cppcheck-suppress unusedFunction
void PowerManagement::activateLoRa() {
axp.setPowerOutPut(AXP192_LDO2, AXP202_ON);
}
// cppcheck-suppress unusedFunction
void PowerManagement::deactivateLoRa() {
axp.setPowerOutPut(AXP192_LDO2, AXP202_OFF);
}
// cppcheck-suppress unusedFunction
void PowerManagement::activateGPS() {
axp.setPowerOutPut(AXP192_LDO3, AXP202_ON);
}
// cppcheck-suppress unusedFunction
void PowerManagement::deactivateGPS() {
axp.setPowerOutPut(AXP192_LDO3, AXP202_OFF);
}
// cppcheck-suppress unusedFunction
void PowerManagement::activateOLED() {
axp.setPowerOutPut(AXP192_DCDC1, AXP202_ON);
}
// cppcheck-suppress unusedFunction
void PowerManagement::decativateOLED() {
axp.setPowerOutPut(AXP192_DCDC1, AXP202_OFF);
}
// cppcheck-suppress unusedFunction
void PowerManagement::disableChgLed() {
axp.setChgLEDMode(AXP20X_LED_OFF);
}
// cppcheck-suppress unusedFunction
void PowerManagement::enableChgLed() {
axp.setChgLEDMode(AXP20X_LED_LOW_LEVEL);
}
// cppcheck-suppress unusedFunction
void PowerManagement::activateMeasurement() {
axp.adc1Enable(AXP202_BATT_CUR_ADC1 | AXP202_BATT_VOL_ADC1, true);
}
// cppcheck-suppress unusedFunction
void PowerManagement::deactivateMeasurement() {
axp.adc1Enable(AXP202_BATT_CUR_ADC1 | AXP202_BATT_VOL_ADC1, false);
}
// cppcheck-suppress unusedFunction
double PowerManagement::getBatteryVoltage() {
return axp.getBattVoltage() / 1000.0;
}
// cppcheck-suppress unusedFunction
double PowerManagement::getBatteryChargeDischargeCurrent() {
if (axp.isChargeing()) {
return axp.getBattChargeCurrent();
}
return -1.0 * axp.getBattDischargeCurrent();
}
bool PowerManagement::isBatteryConnect() {
return axp.isBatteryConnect();
}
bool PowerManagement::isChargeing() {
return axp.isChargeing();
}

37
src/power_management.h Normal file
View File

@ -0,0 +1,37 @@
#ifndef POWER_MANAGEMENT_H_
#define POWER_MANAGEMENT_H_
#include <Arduino.h>
#include <axp20x.h>
class PowerManagement {
public:
PowerManagement();
bool begin(TwoWire &port);
void activateLoRa();
void deactivateLoRa();
void activateGPS();
void deactivateGPS();
void activateOLED();
void decativateOLED();
void enableChgLed();
void disableChgLed();
void activateMeasurement();
void deactivateMeasurement();
double getBatteryVoltage();
double getBatteryChargeDischargeCurrent();
bool isBatteryConnect();
bool isChargeing();
private:
AXP20X_Class axp;
};
#endif