From 5234c1c79558e6608ffd66305b6d1f27027b4af7 Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Tue, 15 Aug 2023 15:26:40 +0200 Subject: Adapt tracker-stm32 to hercules --- tracker-stm32/platformio.ini | 4 +- tracker-stm32/src/main.cpp | 228 ++++++++++++++++++++++++++++++++----------- 2 files changed, 172 insertions(+), 60 deletions(-) diff --git a/tracker-stm32/platformio.ini b/tracker-stm32/platformio.ini index 1e3f966..a50655e 100644 --- a/tracker-stm32/platformio.ini +++ b/tracker-stm32/platformio.ini @@ -7,6 +7,6 @@ upload_protocol = stlink framework = arduino lib_deps = jgromes/RadioLib@^6.0.0 - mikalhart/TinyGPSPlus @ 1.0.3 + mikalhart/TinyGPSPlus@1.0.3 arduino-libraries/SD@^1.2.4 - + koendv/STM32duino-Semihosting@1.0.5 diff --git a/tracker-stm32/src/main.cpp b/tracker-stm32/src/main.cpp index 409c2c1..e452712 100644 --- a/tracker-stm32/src/main.cpp +++ b/tracker-stm32/src/main.cpp @@ -1,10 +1,32 @@ // Licence: MIT +// This file is a bit of a mess, because first it contained aprs434 compressed +// code running on STM32H7, and now it contains traditional APRS-over-LoRa for STM32F1 #include +#include #include #include #include #include #include "compression.h" +#include "utils.h" + +// Pin mapping: outputs +constexpr int LED_STATUSn = PB8; +constexpr int LED_TXn = PB9; +constexpr int EN_PA = PB3; +constexpr int EN_RX = PB4; +constexpr int LORA_RESET = PB11; + +// Pin mapping: inputs +constexpr int BTN1n = PC13; +constexpr int BTN2n = PC14; +constexpr int BTN3n = PC15; +constexpr int ENCODER_SW_1 = PB12; +constexpr int ENCODER_SW_2 = PB13; +constexpr int ENCODER_SW_4 = PB14; +constexpr int ENCODER_SW_8 = PB15; + +constexpr int BAT_MEAS = PA0; // Date Type Codes defined in aprs434.github.io constexpr int DATA_TYPE_CODE_GEOLOCATION = 0; @@ -13,25 +35,27 @@ constexpr int DATA_TYPE_CODE_STATUS_REPORT = 1; // TODO: read these settings from the SD Card constexpr char CALLSIGN[] = "HB9EGM"; constexpr int SSID = 7; +constexpr char CALLSIGN_SSID_PATH[] = "HB9EGM-7>APZEGM"; constexpr int PATH_CODE = 2; // metropolitan mobile constexpr char SYMBOL_TABLE_IDENTIFIER = '/'; constexpr char SYMBOL_CODE_BICYCLE = 'b'; constexpr char SYMBOL_CODE_FOOT = '['; -constexpr long REPORT_TX_INTERVAL = 15000; +constexpr long REPORT_TX_INTERVAL = 181000; constexpr long TEXT_TX_INTERVAL = 47000; constexpr char TEXT_REPORT[] = "mpb.li/git/lora-aprs-hb9egm"; // Max length=28 + #if 0 File myFile; constexpr int SD_CS = 17; #endif -// SX1278 has the following connections: -// NSS pin: PD14 (Arduino 10) -// DIO0 pin: PF3 (Arduino 8) -// RESET pin: PF15 (Arduino 9) -RFM96 radio = new Module(10, 8, 9); +// LoRa module has the following connections: +// NSS pin: PA4 +// DIO0 pin: PB10 +// RESET pin: PB11 +RFM96 radio = new Module(PA4, PB10, PB11); HardwareSerial serialGNSS(PA10, PA9); TinyGPSPlus gps; @@ -40,70 +64,70 @@ long lastGnssPoll = 0; long lastPositionReport = 0; long lastTextReport = 0; -constexpr size_t MAX_REPORT_LEN = 32; +constexpr size_t MAX_REPORT_LEN = 48; size_t report_len = 0; uint8_t report[MAX_REPORT_LEN]; +SemihostingStream sh; + static char letterize(int x) { return (char) x + 65; } void setup() { - Serial.begin(9600); - pinMode(LED_BUILTIN, OUTPUT); + Wire.begin(); + pinMode(LED_STATUSn, OUTPUT); #if 0 pinMode(SD_CS, OUTPUT); if (!SD.begin(SD_CS)) { - Serial.println("SD init failed!"); + sh.println("SD init failed!"); return; } #endif - Wire.begin(); - - + sh.print(F("START")); serialGNSS.begin(9600); - Serial.print(F("[RFM] Init ")); - int state = radio.begin(433.900); + sh.print(F("[RFM] Init ")); + int state = radio.begin(433.775); if (state == RADIOLIB_ERR_NONE) { - Serial.println(F("success!")); + sh.println(F("success!")); } else { - Serial.print(F("failed, code ")); - Serial.println(state); + sh.print(F("failed, code ")); + sh.println(state); while (true); } if (radio.setBandwidth(125.0) == RADIOLIB_ERR_INVALID_BANDWIDTH) { - Serial.println(F("Selected bandwidth is invalid for this module!")); + sh.println(F("Selected bandwidth is invalid for this module!")); while (true); } if (radio.setSpreadingFactor(12) == RADIOLIB_ERR_INVALID_SPREADING_FACTOR) { - Serial.println(F("Selected spreading factor is invalid for this module!")); + sh.println(F("Selected spreading factor is invalid for this module!")); while (true); } if (radio.setCodingRate(5) == RADIOLIB_ERR_INVALID_CODING_RATE) { - Serial.println(F("Selected coding rate is invalid for this module!")); + sh.println(F("Selected coding rate is invalid for this module!")); while (true); } // NOTE: value 0x34 is reserved for LoRaWAN networks and should not be used if (radio.setSyncWord(0x14) != RADIOLIB_ERR_NONE) { - Serial.println(F("Unable to set sync word!")); + sh.println(F("Unable to set sync word!")); while (true); } - if (radio.setOutputPower(16) == RADIOLIB_ERR_INVALID_OUTPUT_POWER) { - Serial.println(F("Selected output power is invalid for this module!")); + if (radio.setOutputPower(20) == RADIOLIB_ERR_INVALID_OUTPUT_POWER) { + sh.println(F("Selected output power is invalid for this module!")); while (true); } if (radio.setPreambleLength(8) == RADIOLIB_ERR_INVALID_PREAMBLE_LENGTH) { - Serial.println(F("Selected preamble length is invalid for this module!")); + sh.println(F("Selected preamble length is invalid for this module!")); while (true); } @@ -113,25 +137,112 @@ void setup() { static void handle_radio_error(int state) { if (state == RADIOLIB_ERR_NONE) { - Serial.print(F(" RFM OK Datarate: ")); - Serial.print(radio.getDataRate()); - Serial.println(F(" bps")); + sh.print(F(" RFM OK Datarate: ")); + sh.print(radio.getDataRate()); + sh.println(F(" bps")); } else if (state == RADIOLIB_ERR_PACKET_TOO_LONG) { - Serial.println(F(" too long!")); + sh.println(F(" too long!")); } else if (state == RADIOLIB_ERR_TX_TIMEOUT) { - Serial.println(F(" timeout!")); + sh.println(F(" timeout!")); } else { - Serial.print(F(" failed, code ")); - Serial.println(state); + sh.print(F(" failed, code ")); + sh.println(state); } } +void append_position_report() +{ + float Tlat, Tlon; + float Tspeed=0, Tcourse=0; + Tlat = gps.location.lat(); + Tlon = gps.location.lng(); + Tcourse = gps.course.deg(); + Tspeed = gps.speed.knots(); + + uint32_t aprs_lat, aprs_lon; + aprs_lat = 900000000 - Tlat * 10000000; + aprs_lat = aprs_lat / 26 - aprs_lat / 2710 + aprs_lat / 15384615; + aprs_lon = 900000000 + Tlon * 10000000 / 2; + aprs_lon = aprs_lon / 26 - aprs_lon / 2710 + aprs_lon / 15384615; + + char helper_base91[] = {"0000\0"}; + int i; + ax25_base91enc(helper_base91, 4, aprs_lat); + for (i=0; i<4; i++) { + report[report_len++] = helper_base91[i]; + } + ax25_base91enc(helper_base91, 4, aprs_lon); + for (i=0; i<4; i++) { + report[report_len++] = helper_base91[i]; + } + + report[report_len++] = SYMBOL_CODE_FOOT; + + // Do not encode altitude, only course and speed + ax25_base91enc(helper_base91, 1, (uint32_t)Tcourse/4 ); + report[report_len++] = helper_base91[0]; + ax25_base91enc(helper_base91, 1, (uint32_t)(log1p(Tspeed)/0.07696)); + report[report_len++] = helper_base91[0]; + report[report_len++] = 0x47; +} + +void loop() +{ + while (serialGNSS.available() > 0) { + gps.encode(serialGNSS.read()); + } + + const auto now = millis(); + if (now - lastGnssPoll > 1000) { + lastGnssPoll = now; + + const long latitude = gps.location.lat(); + sh.print(F("Lat ")); + sh.print(latitude); + + const long longitude = gps.location.lng(); + sh.print(F(" Lon ")); + sh.print(longitude); + + const byte SIV = gps.satellites.value(); + sh.print(F(" SIV: ")); + sh.println(SIV); + if (now - lastPositionReport > REPORT_TX_INTERVAL) { + lastPositionReport = now; + + digitalWrite(LED_STATUSn, LOW); + + report_len = 0; + + report[report_len++] = '<'; + report[report_len++] = 0xFF; + report[report_len++] = 0x01; + + const char* p = CALLSIGN_SSID_PATH; + while (*p) { + report[report_len++] = *p; + p++; + } + + report[report_len++] = ':'; + report[report_len++] = '!'; + report[report_len++] = SYMBOL_TABLE_IDENTIFIER; + append_position_report(); + int state = radio.transmit(report, report_len); + handle_radio_error(state); + + digitalWrite(LED_STATUSn, HIGH); + } + } +} + +#if 0 void loop() { while (serialGNSS.available() > 0) { @@ -144,40 +255,40 @@ void loop() // longitude and latitude are in degrees*1e7 const long latitude = gps.location.lat(); - Serial.print(F("Lat: ")); - Serial.print(latitude); + sh.print(F("Lat: ")); + sh.print(latitude); const long longitude = gps.location.lng(); - Serial.print(F(" Long: ")); - Serial.print(longitude); + sh.print(F(" Long: ")); + sh.print(longitude); const long altitude = gps.altitude.feet(); const int speed = gps.speed.kmph(); - Serial.print(F(" Speed: ")); - Serial.print(speed); - Serial.print(F(" (mm/s)")); + sh.print(F(" Speed: ")); + sh.print(speed); + sh.print(F(" (mm/s)")); // 1m/s = 900/463 knots const double speed_kn = gps.speed.knots(); const long heading = gps.course.deg(); - Serial.print(F(" Heading: ")); - Serial.print(heading); - Serial.print(F(" (degrees * 10^-5)")); + sh.print(F(" Heading: ")); + sh.print(heading); + sh.print(F(" (degrees * 10^-5)")); const byte SIV = gps.satellites.value(); - Serial.print(F(" SIV: ")); - Serial.print(SIV); + sh.print(F(" SIV: ")); + sh.print(SIV); - Serial.print(F(" TX in: ")); - Serial.print(REPORT_TX_INTERVAL - (now - lastPositionReport)); + sh.print(F(" TX in: ")); + sh.print(REPORT_TX_INTERVAL - (now - lastPositionReport)); - Serial.println(); + sh.println(); if (now - lastPositionReport > REPORT_TX_INTERVAL) { lastPositionReport = now; - digitalWrite(LED_BUILTIN, HIGH); + digitalWrite(LED_STATUSn, HIGH); // Encode Compressed Geolocation Frame according to aprs434.github.io const uint32_t callsign_EEEE = encodeCallsign(CALLSIGN); @@ -223,21 +334,21 @@ void loop() report[report_len++] = tmp_base91[0]; // Debug print - Serial.println(F("Bytes: ")); + sh.println(F("Bytes: ")); for (int i = 0; i < report_len; i++) { - Serial.print((uint32_t)report[i]); + sh.print((uint32_t)report[i]); } - Serial.println(F("")); + sh.println(F("")); int state = radio.transmit(report, report_len); handle_radio_error(state); - digitalWrite(LED_BUILTIN, LOW); + digitalWrite(LED_STATUSn, LOW); } if (now - lastTextReport > TEXT_TX_INTERVAL) { lastTextReport = now; - digitalWrite(LED_BUILTIN, HIGH); + digitalWrite(LED_STATUSn, HIGH); // Encode Compressed Status Report Frame according to aprs434.github.io const uint32_t callsign_EEEE = encodeCallsign(CALLSIGN); @@ -261,15 +372,16 @@ void loop() } } - Serial.print(F("TX length ")); - Serial.print(report_len); - Serial.print(F(" bytes: ")); - Serial.println(TEXT_REPORT); + sh.print(F("TX length ")); + sh.print(report_len); + sh.print(F(" bytes: ")); + sh.println(TEXT_REPORT); int state = radio.transmit(report, report_len); handle_radio_error(state); - digitalWrite(LED_BUILTIN, LOW); + digitalWrite(LED_STATUSn, LOW); } } } +#endif -- cgit v1.2.3