diff --git a/.gitignore b/.gitignore index 89cc49c..592f203 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ .vscode/c_cpp_properties.json .vscode/launch.json .vscode/ipch +.idea \ No newline at end of file diff --git a/data/tracker_config.json b/data/tracker_config.json index 8fc506a..e7f7379 100644 --- a/data/tracker_config.json +++ b/data/tracker_config.json @@ -70,7 +70,9 @@ "maxDistanceToTracker": 30, "standingUpdateTime": 15, "sendAltitude": true, - "sendBatteryInfo": false + "sendBatteryInfo": false, + "bluetooth": true, + "disableGps": false }, "ptt_trigger": { "active": false, diff --git a/platformio.ini b/platformio.ini index 5b39b32..54567c1 100644 --- a/platformio.ini +++ b/platformio.ini @@ -16,6 +16,7 @@ platform = espressif32 @ 6.3.1 framework = arduino lib_ldf_mode = deep+ monitor_speed = 115200 +upload_speed = 921600 monitor_filters = esp32_exception_decoder lib_deps = adafruit/Adafruit BusIO@^1.14.1 @@ -29,11 +30,11 @@ lib_deps = shaggydog/OneButton @ 1.5.0 peterus/esp-logger @ 1.0.0 lewisxhe/XPowersLib@^0.1.7 - h2zero/NimBLE-Arduino@^1.4.1 check_tool = cppcheck check_flags = cppcheck: --suppress=*:*.pio\* --inline-suppr -DCPPCHECK check_skip_packages = yes +board_build.partitions = no_ota.csv [env:ttgo-t-beam-v1] board = ttgo-t-beam @@ -46,3 +47,7 @@ build_flags = -Werror -Wall -DTTGO_T_Beam_V0_7 [env:ttgo-t-beam-v1_2] board = ttgo-t-beam build_flags = -Werror -Wall -DTTGO_T_Beam_V1_2 + +[env:ttgo-t-lora-v2_1] +board = ttgo-lora32-v21 +build_flags = -Werror -Wall -DTTGO_T_LORA_V2_1 \ No newline at end of file diff --git a/src/LoRa_APRS_Tracker.cpp b/src/LoRa_APRS_Tracker.cpp index 13f65a1..48fde17 100644 --- a/src/LoRa_APRS_Tracker.cpp +++ b/src/LoRa_APRS_Tracker.cpp @@ -1,8 +1,5 @@ -#ifdef ESP32 -#include -#endif +#include #include -#include #include #include #include @@ -21,16 +18,17 @@ #include "display.h" #include "SPIFFS.h" #include "utils.h" +#include "bluetooth_utils.h" Configuration Config; PowerManagement powerManagement; HardwareSerial neo6m_gps(1); TinyGPSPlus gps; -NimBLECharacteristic* pCharacteristic; +BluetoothSerial SerialBT; OneButton userButton = OneButton(BUTTON_PIN, true, true); -String versionDate = "2023.08.01"; +String versionDate = "2023.08.02"; int myBeaconsIndex = 0; int myBeaconsSize = Config.beacons.size(); @@ -64,12 +62,11 @@ bool symbolAvailable = true; logging::Logger logger; - void setup() { Serial.begin(115200); powerManagement.setup(); - + setup_display(); show_display(" LoRa APRS", "", " Richonguzman", " -- CD2RXU --", "", " " + versionDate, 4000); logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "Main", "RichonGuzman -> CD2RXU --> LoRa APRS Tracker/Station"); @@ -82,13 +79,13 @@ void setup() { MSG_Utils::loadNumMessages(); GPS_Utils::setup(); + LoRa_Utils::setup(); WiFi.mode(WIFI_OFF); - btStop(); - logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "Main", "WiFi and BT controller stopped"); - esp_bt_controller_disable(); - logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "Main", "BT controller disabled"); + logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "Main", "WiFi controller stopped"); + + BLUETOOTH_Utils::setup(); userButton.attachClick(BUTTON_Utils::singlePress); userButton.attachLongPressStart(BUTTON_Utils::longPress); @@ -97,7 +94,7 @@ void setup() { powerManagement.lowerCpuFrequency(); logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "Main", "Smart Beacon is: %s", utils::getSmartBeaconState()); logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "Main", "Setup Done!"); - menuDisplay = 0; + menuDisplay = BUTTON_PIN == -1 ? 20 : 0; } void loop() { @@ -118,7 +115,7 @@ void loop() { MSG_Utils::checkReceivedMessage(LoRa_Utils::receivePacket()); STATION_Utils::checkListenedTrackersByTimeAndDelete(); - int currentSpeed = (int)gps.speed.kmph(); + int currentSpeed = (int) gps.speed.kmph(); lastTx = millis() - lastTxTime; if (!sendUpdate && gps_loc_update && currentBeacon->smartBeaconState) { @@ -136,8 +133,12 @@ void loop() { } if (gps_time_update) { - MENU_Utils::showOnScreen(); STATION_Utils::checkSmartBeaconInterval(currentSpeed); } - GPS_Utils::checkStartUpFrames(); + + if (millis() - displayTime >= 1000 || gps_time_update) { + GPS_Utils::checkStartUpFrames(); + MENU_Utils::showOnScreen(); + displayTime = millis(); + } } \ No newline at end of file diff --git a/src/bluetooth_utils.cpp b/src/bluetooth_utils.cpp new file mode 100644 index 0000000..f56339f --- /dev/null +++ b/src/bluetooth_utils.cpp @@ -0,0 +1,96 @@ +#include +#include "bluetooth_utils.h" +#include "logger.h" +#include "display.h" +#include "lora_utils.h" +#include "configuration.h" +#include "TinyGPSPlus.h" + +extern Configuration Config; +extern BluetoothSerial SerialBT; +extern logging::Logger logger; +extern TinyGPSPlus gps; + +namespace BLUETOOTH_Utils { + String serialReceived; + bool shouldSendToLoRa = false; + + void setup() { + if (!Config.bluetooth) { + btStop(); + esp_bt_controller_disable(); + logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "Main", "BT controller disabled"); + + return; + } + + serialReceived.reserve(255); + + SerialBT.register_callback(BLUETOOTH_Utils::bluetoothCallback); + SerialBT.onData(BLUETOOTH_Utils::getData); // callback instead of while to avoid RX buffer limit when NMEA data received + + if (!SerialBT.begin(String("LoRa Tracker " + String((unsigned long) ESP.getEfuseMac())))) { + logger.log(logging::LoggerLevel::LOGGER_LEVEL_ERROR, "Bluetooth", "Starting Bluetooth failed!"); + show_display("ERROR", "Starting Bluetooth failed!"); + while(true) { + delay(1000); + } + } + + logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "Bluetooth", "Bluetooth init done!"); + } + + void bluetoothCallback(esp_spp_cb_event_t event, esp_spp_cb_param_t *param) { + if (event == ESP_SPP_SRV_OPEN_EVT) { + logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "Bluetooth", "Bluetooth client connected !"); + } else if (event == ESP_SPP_CLOSE_EVT) { + logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "Bluetooth", "Bluetooth client disconnected !"); + } + } + + void getData(const uint8_t *buffer, size_t size) { + if (size == 0) { + return; + } + + shouldSendToLoRa = false; + serialReceived.clear(); + + bool isNmea = buffer[0] == '$'; + + for (int i = 0; i < size; i++) { + char c = (char) buffer[i]; + + if (isNmea) { + gps.encode(c); + } else { + serialReceived += c; + } + } + + if (serialReceived.isEmpty()) { + return; + } + + shouldSendToLoRa = true; // because we can't send here + } + + void sendToLoRa() { + if (!shouldSendToLoRa) { + return; + } + + logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "BT TX", "%s", serialReceived.c_str()); + show_display("BT Tx >>", "", serialReceived, 1000); + + LoRa_Utils::sendNewPacket(serialReceived); + + shouldSendToLoRa = false; + } + + void sendPacket(const String& packet) { + if (Config.bluetooth && !packet.isEmpty()) { + SerialBT.println(packet); + } + } +} \ No newline at end of file diff --git a/src/bluetooth_utils.h b/src/bluetooth_utils.h new file mode 100644 index 0000000..321f0e3 --- /dev/null +++ b/src/bluetooth_utils.h @@ -0,0 +1,14 @@ +#ifndef BLUETOOTH_UTILS_H +#define BLUETOOTH_UTILS_H + +#include + +namespace BLUETOOTH_Utils { + void setup(); + void bluetoothCallback(esp_spp_cb_event_t event, esp_spp_cb_param_t *param); + void getData(const uint8_t *buffer, size_t size); + void sendToLoRa(); + void sendPacket(const String& packet); +} + +#endif diff --git a/src/configuration.cpp b/src/configuration.cpp index 7b074fe..5d0c278 100644 --- a/src/configuration.cpp +++ b/src/configuration.cpp @@ -68,6 +68,8 @@ void Configuration::readFile(fs::FS &fs, const char *fileName) { standingUpdateTime = data["other"]["standingUpdateTime"].as(); sendAltitude = data["other"]["sendAltitude"].as(); sendBatteryInfo = data["other"]["sendBatteryInfo"].as(); + bluetooth = data["other"]["bluetooth"].as(); + disableGps = data["other"]["disableGps"].as(); configFile.close(); } diff --git a/src/configuration.h b/src/configuration.h index ac62d32..0329c51 100644 --- a/src/configuration.h +++ b/src/configuration.h @@ -58,6 +58,8 @@ public: int standingUpdateTime; bool sendAltitude; bool sendBatteryInfo; + bool bluetooth; + bool disableGps; Configuration(); void validateConfigFile(String currentBeaconCallsign); diff --git a/src/gps_utils.cpp b/src/gps_utils.cpp index 2750451..b66e34f 100644 --- a/src/gps_utils.cpp +++ b/src/gps_utils.cpp @@ -30,7 +30,12 @@ extern uint32_t lastTx; namespace GPS_Utils { void setup() { - neo6m_gps.begin(9600, SERIAL_8N1, GPS_TX, GPS_RX); + if (Config.disableGps) { + logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "Main", "GPS disabled"); + return; + } + + neo6m_gps.begin(9600, SERIAL_8N1, GPS_TX, GPS_RX); } void calculateDistanceCourse(String Callsign, double checkpointLatitude, double checkPointLongitude) { @@ -102,6 +107,10 @@ void getReceivedGPS(String packet, String sender) { } void getData() { + if (Config.disableGps) { + return; + } + while (neo6m_gps.available() > 0) { gps.encode(neo6m_gps.read()); } @@ -141,6 +150,10 @@ void calculateHeadingDelta(int speed) { } void checkStartUpFrames() { + if (Config.disableGps) { + return; + } + if ((millis() > 8000 && gps.charsProcessed() < 10)) { logger.log(logging::LoggerLevel::LOGGER_LEVEL_ERROR, "GPS", "No GPS frames detected! Try to reset the GPS Chip with this " diff --git a/src/menu_utils.cpp b/src/menu_utils.cpp index 167df4a..6a14e0d 100644 --- a/src/menu_utils.cpp +++ b/src/menu_utils.cpp @@ -117,7 +117,7 @@ void showOnScreen() { if (powerManagement.getBatteryInfoIsConnected()) { String batteryVoltage = powerManagement.getBatteryInfoVoltage(); String batteryChargeCurrent = powerManagement.getBatteryInfoCurrent(); - #ifdef TTGO_T_Beam_V1_0 + #if defined(TTGO_T_Beam_V1_0) || defined(TTGO_T_LORA_V2_1) if (batteryChargeCurrent.toInt() == 0) { sixthRowMainMenu = "Battery Charged " + batteryVoltage + "V"; } else if (batteryChargeCurrent.toInt() > 0) { diff --git a/src/msg_utils.cpp b/src/msg_utils.cpp index a3c49d3..4975f90 100644 --- a/src/msg_utils.cpp +++ b/src/msg_utils.cpp @@ -7,6 +7,7 @@ #include "gps_utils.h" #include "display.h" #include "logger.h" +#include "bluetooth_utils.h" extern Beacon *currentBeacon; extern logging::Logger logger; @@ -141,6 +142,7 @@ void checkReceivedMessage(String packetReceived) { } String Sender, AddresseeAndMessage, Addressee, receivedMessage, ackMessage; if (packetReceived.substring(0,3) == "\x3c\xff\x01") { // its an APRS packet + BLUETOOTH_Utils::sendPacket(packetReceived.substring(3)); Sender = packetReceived.substring(3,packetReceived.indexOf(">")); if (Sender != currentBeacon->callsign) { // avoid listening yourself by digirepeating if (packetReceived.indexOf("::") > 10) { // its a Message! diff --git a/src/pins_config.h b/src/pins_config.h index dae0706..e3f67b0 100644 --- a/src/pins_config.h +++ b/src/pins_config.h @@ -9,16 +9,25 @@ #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 + +#define BUTTON_PIN 38 // The middle button GPIO on the T-Beam #endif #if defined(TTGO_T_Beam_V1_0) || defined(TTGO_T_Beam_V1_2) #define GPS_RX 12 #define GPS_TX 34 + +#define BUTTON_PIN 38 // The middle button GPIO on the T-Beam +#endif + +#ifdef TTGO_T_LORA_V2_1 +#define GPS_RX -1 +#define GPS_TX -1 + +#define BUTTON_PIN -1 #endif #endif diff --git a/src/power_utils.cpp b/src/power_utils.cpp index 578e37f..9b646de 100644 --- a/src/power_utils.cpp +++ b/src/power_utils.cpp @@ -1,14 +1,16 @@ #include "power_utils.h" #include "logger.h" +#include "configuration.h" #define I2C_SDA 21 #define I2C_SCL 22 +extern Configuration Config; extern logging::Logger logger; // cppcheck-suppress unusedFunction bool PowerManagement::begin(TwoWire &port) { -#ifdef TTGO_T_Beam_V1_0 +#if defined(TTGO_T_Beam_V1_0) || defined(TTGO_T_LORA_V2_1) bool result = axp.begin(port, AXP192_SLAVE_ADDRESS); if (!result) { axp.setDCDC1Voltage(3300); @@ -38,7 +40,7 @@ bool PowerManagement::begin(TwoWire &port) { // cppcheck-suppress unusedFunction void PowerManagement::activateLoRa() { -#ifdef TTGO_T_Beam_V1_0 +#if defined(TTGO_T_Beam_V1_0) || defined(TTGO_T_LORA_V2_1) axp.setPowerOutPut(AXP192_LDO2, AXP202_ON); #endif #ifdef TTGO_T_Beam_V1_2 @@ -49,7 +51,7 @@ void PowerManagement::activateLoRa() { // cppcheck-suppress unusedFunction void PowerManagement::deactivateLoRa() { -#ifdef TTGO_T_Beam_V1_0 +#if defined(TTGO_T_Beam_V1_0) || defined(TTGO_T_LORA_V2_1) axp.setPowerOutPut(AXP192_LDO2, AXP202_OFF); #endif #ifdef TTGO_T_Beam_V1_2 @@ -59,8 +61,8 @@ void PowerManagement::deactivateLoRa() { // cppcheck-suppress unusedFunction void PowerManagement::activateGPS() { -#ifdef TTGO_T_Beam_V1_0 - axp.setPowerOutPut(AXP192_LDO2, AXP202_ON); +#if defined(TTGO_T_Beam_V1_0) || defined(TTGO_T_LORA_V2_1) + axp.setPowerOutPut(AXP192_LDO3, AXP202_ON); #endif #ifdef TTGO_T_Beam_V1_2 PMU.setALDO3Voltage(3300); @@ -70,8 +72,8 @@ void PowerManagement::activateGPS() { // cppcheck-suppress unusedFunction void PowerManagement::deactivateGPS() { -#ifdef TTGO_T_Beam_V1_0 - axp.setPowerOutPut(AXP192_LDO2, AXP202_OFF); +#if defined(TTGO_T_Beam_V1_0) || defined(TTGO_T_LORA_V2_1) + axp.setPowerOutPut(AXP192_LDO3, AXP202_OFF); #endif #ifdef TTGO_T_Beam_V1_2 PMU.disableALDO3(); @@ -80,35 +82,35 @@ void PowerManagement::deactivateGPS() { // cppcheck-suppress unusedFunction void PowerManagement::activateOLED() { -#ifdef TTGO_T_Beam_V1_0 +#if defined(TTGO_T_Beam_V1_0) || defined(TTGO_T_LORA_V2_1) axp.setPowerOutPut(AXP192_DCDC1, AXP202_ON); #endif } // cppcheck-suppress unusedFunction void PowerManagement::decativateOLED() { -#ifdef TTGO_T_Beam_V1_0 +#if defined(TTGO_T_Beam_V1_0) || defined(TTGO_T_LORA_V2_1) axp.setPowerOutPut(AXP192_DCDC1, AXP202_OFF); #endif } // cppcheck-suppress unusedFunction void PowerManagement::disableChgLed() { -#ifdef TTGO_T_Beam_V1_0 +#if defined(TTGO_T_Beam_V1_0) || defined(TTGO_T_LORA_V2_1) axp.setChgLEDMode(AXP20X_LED_OFF); #endif } // cppcheck-suppress unusedFunction void PowerManagement::enableChgLed() { -#ifdef TTGO_T_Beam_V1_0 +#if defined(TTGO_T_Beam_V1_0) || defined(TTGO_T_LORA_V2_1) axp.setChgLEDMode(AXP20X_LED_LOW_LEVEL); #endif } // cppcheck-suppress unusedFunction void PowerManagement::activateMeasurement() { -#ifdef TTGO_T_Beam_V1_0 +#if defined(TTGO_T_Beam_V1_0) || defined(TTGO_T_LORA_V2_1) axp.adc1Enable(AXP202_BATT_CUR_ADC1 | AXP202_BATT_VOL_ADC1, true); #endif #ifdef TTGO_T_Beam_V1_2 @@ -122,14 +124,14 @@ void PowerManagement::activateMeasurement() { // cppcheck-suppress unusedFunction void PowerManagement::deactivateMeasurement() { -#ifdef TTGO_T_Beam_V1_0 +#if defined(TTGO_T_Beam_V1_0) || defined(TTGO_T_LORA_V2_1) axp.adc1Enable(AXP202_BATT_CUR_ADC1 | AXP202_BATT_VOL_ADC1, false); #endif } // cppcheck-suppress unusedFunction double PowerManagement::getBatteryVoltage() { -#ifdef TTGO_T_Beam_V1_0 +#if defined(TTGO_T_Beam_V1_0) || defined(TTGO_T_LORA_V2_1) return axp.getBattVoltage() / 1000.0; #endif #ifdef TTGO_T_Beam_V1_2 @@ -139,7 +141,7 @@ double PowerManagement::getBatteryVoltage() { // cppcheck-suppress unusedFunction double PowerManagement::getBatteryChargeDischargeCurrent() { -#ifdef TTGO_T_Beam_V1_0 +#if defined(TTGO_T_Beam_V1_0) || defined(TTGO_T_LORA_V2_1) if (axp.isChargeing()) { return axp.getBattChargeCurrent(); } @@ -151,7 +153,7 @@ double PowerManagement::getBatteryChargeDischargeCurrent() { } bool PowerManagement::isBatteryConnected() { -#ifdef TTGO_T_Beam_V1_0 +#if defined(TTGO_T_Beam_V1_0) || defined(TTGO_T_LORA_V2_1) return axp.isBatteryConnect(); #endif #ifdef TTGO_T_Beam_V1_2 @@ -160,7 +162,7 @@ bool PowerManagement::isBatteryConnected() { } bool PowerManagement::isChargeing() { -#ifdef TTGO_T_Beam_V1_0 +#if defined(TTGO_T_Beam_V1_0) || defined(TTGO_T_LORA_V2_1) return axp.isChargeing(); #endif #ifdef TTGO_T_Beam_V1_2 @@ -169,7 +171,7 @@ bool PowerManagement::isChargeing() { } void PowerManagement::setup() { -#ifdef TTGO_T_Beam_V1_0 +#if defined(TTGO_T_Beam_V1_0) || defined(TTGO_T_LORA_V2_1) Wire.begin(SDA, SCL); if (!begin(Wire)) { logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "AXP192", "init done!"); @@ -178,7 +180,11 @@ void PowerManagement::setup() { } activateLoRa(); activateOLED(); - activateGPS(); + if (Config.disableGps) { + deactivateGPS(); + } else { + activateGPS(); + } activateMeasurement(); #endif #ifdef TTGO_T_Beam_V1_2 @@ -190,7 +196,11 @@ void PowerManagement::setup() { } activateLoRa(); activateOLED(); - activateGPS(); + if (Config.disableGps) { + deactivateGPS(); + } else { + activateGPS(); + } activateMeasurement(); PMU.setChargeTargetVoltage(XPOWERS_AXP2101_CHG_VOL_4V2); PMU.setChargerConstantCurr(XPOWERS_AXP2101_CHG_CUR_500MA); @@ -199,7 +209,7 @@ void PowerManagement::setup() { } void PowerManagement::lowerCpuFrequency() { -#if defined(TTGO_T_Beam_V1_0) || defined(TTGO_T_Beam_V1_2) +#if defined(TTGO_T_Beam_V1_0) || defined(TTGO_T_Beam_V1_2) || defined(TTGO_T_LORA_V2_1) if (setCpuFrequencyMhz(80)) { logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "Main", "CPU frequency set to 80MHz"); } else { @@ -221,7 +231,7 @@ void PowerManagement::obtainBatteryInfo() { if (!(rate_limit_check_battery++ % 60)) BatteryIsConnected = isBatteryConnected(); if (BatteryIsConnected) { - #ifdef TTGO_T_Beam_V1_0 + #if defined(TTGO_T_Beam_V1_0) || defined(TTGO_T_LORA_V2_1) batteryVoltage = String(getBatteryVoltage(), 2); #endif #ifdef TTGO_T_Beam_V1_2 diff --git a/src/power_utils.h b/src/power_utils.h index cea14f1..84dd45d 100644 --- a/src/power_utils.h +++ b/src/power_utils.h @@ -2,7 +2,7 @@ #define POWER_UTILS_H_ #include -#ifdef TTGO_T_Beam_V1_0 +#if defined(TTGO_T_Beam_V1_0) || defined(TTGO_T_LORA_V2_1) #include #endif #ifdef TTGO_T_Beam_V1_2 @@ -49,7 +49,7 @@ private: bool isBatteryConnected(); - #ifdef TTGO_T_Beam_V1_0 + #if defined(TTGO_T_Beam_V1_0) || defined(TTGO_T_LORA_V2_1) AXP20X_Class axp; #endif #ifdef TTGO_T_Beam_V1_2