From 0d2ff764ead4bec66954708794e2dd67eb1a0f9d Mon Sep 17 00:00:00 2001 From: FCS Date: Fri, 28 Aug 2026 18:07:57 +0200 Subject: [PATCH] Dokumentation um Webinterface, Einstellungen und OTA-Update erweitert --- CHANGELOG.md | 18 +++ LICENSE | 21 +++ README.md | 35 ++++- THIRD_PARTY_NOTICES.md | 44 ++++++ extra_script.py | 8 +- lib/sailplane/airspeed.cpp | 53 +++++-- lib/sailplane/airspeed.h | 6 +- lib/webinterface/webinterface.cpp | 242 ++++++++++++++++++++++++++++++ lib/webinterface/webinterface.h | 20 +++ lib/winde/tacho.cpp | 13 +- lib/windentelemetry.h | 8 +- platformio.ini | 23 +-- src/main.cpp | 125 ++++++++++----- 13 files changed, 531 insertions(+), 85 deletions(-) create mode 100644 LICENSE create mode 100644 THIRD_PARTY_NOTICES.md create mode 100644 lib/webinterface/webinterface.cpp create mode 100644 lib/webinterface/webinterface.h diff --git a/CHANGELOG.md b/CHANGELOG.md index ad73617..56ec500 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,23 @@ CHANGELOG +## 0.0.7 (28.08.2026) ++ Webinterface über eigenen WLAN-Access-Point ergänzt ++ Persistente Konfiguration für WLAN-Name, WLAN-Passwort und Callsign ++ Lizenzinformationen für Projektcode und Drittanbieterbibliotheken ergänzt ++ Firmware-Upload über das Webinterface für beide Firmwarevarianten ergänzt + +### Segeflug Modul ++ Einstellbare minimale und maximale Schleppgeschwindigkeit ++ Einstellbare maximale Übertragungszeit, Standardwert 90 Sekunden ++ Geladene Einstellungen beim Programmstart verwendet ++ Sensorinitialisierung und Messwertverarbeitung robustifiziert + +### Winden Modul ++ Einstellbare WLAN-Konfiguration über das Webinterface ++ Standard-WLAN-Name `Winde-Telemetry` ++ Initialisierung von Servo und LED-Ring korrigiert ++ NMEA-Nachrichtenprüfung und Fehlerbehandlung beim Empfang verbessert + ## 0.0.6 (18.01.2025) + Anpassung Programmablaufplan diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..c6d2ccb --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Winden-Telemetry contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index c2f28b8..320e1c3 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,12 @@ # Winden-Telemetry +Der eigene Projektcode steht unter der MIT-Lizenz. Die verwendeten +Drittanbieterbibliotheken bleiben unter ihren jeweiligen Original-Lizenzen. +Eine Übersicht der Komponenten, Versionen und Lizenzbedingungen steht in +[THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md); der vollständige Lizenztext +für den Projektcode steht in [LICENSE](LICENSE). + Übertragung der IAS des Segelflugzeuges an die Winde @@ -19,16 +25,37 @@ ### Features: - [x] LORA zum Übertragen - [x] Airspeed via differenz Drucksensor -- [ ] Webinterface -- [ ] min/max speed einstellbar -- [ ] Kennzeichen einstellbar +- [x] Webinterface über eigenen Access Point +- [x] min/max speed per Webinterface einstellbar +- [x] Kennzeichen per Webinterface einstellbar +- [x] maximale Übertragungszeit per Webinterface einstellbar (Standard: 90 Sekunden) +- [x] Firmware-Upload per Webinterface für beide Firmwarevarianten - [x] Analoge Anzeige in der Winde - [x] Farbige LEDs für Limits - [x] Übertragungszeit Begrenzen -- [ ] Übertragungszeit einstellbar - [ ] Übertragung Rundumleuchten Signal an Start - [x] Luftdrucksensor für Höhe und Vario +### Webinterface und Firmware-Update + +Beide Firmwarevarianten starten einen eigenen WLAN-Access-Point. Der +voreingestellte WLAN-Name ist bei Sailplane `Sailplane-D1234` und bei der +Winde `Winde-Telemetry`. + +- Passwort: `telemetry` +- Webadresse: `http://192.168.4.1/` + +Im Webinterface können WLAN-Name und WLAN-Passwort sowie Callsign, minimale und maximale Geschwindigkeit +gespeichert werden. Die Werte bleiben nach einem Neustart erhalten. Die +Sailplane-Firmware lädt sie beim Start und verwendet sie für die Telemetrie, +die Schlepp-Erkennung und die maximale Übertragungszeit. Die Winden-Firmware verwendet die gespeicherten +Grenzen für die Anzeige beim Start; empfangene Telemetriegrenzen werden danach +weiterhin aus dem LoRa-Paket übernommen. + +Für ein Firmware-Update die zur jeweiligen Hardware passende `.bin`-Datei aus +`.pio/build//` auswählen. Nach erfolgreichem Upload startet das +Gerät automatisch neu. + ## Aufbau Pin Layout Heltec WiFi LoRa (v3) diff --git a/THIRD_PARTY_NOTICES.md b/THIRD_PARTY_NOTICES.md new file mode 100644 index 0000000..34d33e4 --- /dev/null +++ b/THIRD_PARTY_NOTICES.md @@ -0,0 +1,44 @@ +# Third-party notices + +This project contains or links to the following third-party software. The +listed versions are the versions resolved by PlatformIO on 2026-08-28. +Original license texts and copyright notices must be retained when the +corresponding software is redistributed. + +## Direct and transitive library dependencies + +| Component | Version | Used by | License | Source | +| --- | --- | --- | --- | --- | +| U8g2 | 2.36.18 | Sailplane, Winde | BSD-2-Clause; bundled fonts may have additional licenses | https://github.com/olikraus/u8g2 | +| RadioLib | 7.6.0 | Sailplane, Winde | MIT | https://github.com/jgromes/RadioLib | +| ArduinoJson | 7.4.3 | Sailplane, Winde | MIT | https://github.com/bblanchon/ArduinoJson | +| Bolder Flight Systems Airdata Calculations | 4.0.0 | Sailplane | MIT | https://github.com/bolderflight/airdata_calculations | +| Bolder Flight Systems MS4525 | 1.1.3 | Sailplane | MIT | https://github.com/bolderflight/ms4525 | +| Bolder Flight Systems Unit Conversions | 5.0.0 | Sailplane | MIT | https://github.com/bolderflight/units | +| Adafruit BMP085 Library | 1.2.4 | Sailplane | BSD | https://github.com/adafruit/Adafruit-BMP085-Library | +| Adafruit BusIO | 1.17.4 | Sailplane, transitive | MIT | https://github.com/adafruit/Adafruit_BusIO | +| Adafruit Unified Sensor | 1.1.15 | Sailplane, transitive | Apache-2.0 | https://github.com/adafruit/Adafruit_Sensor | +| Adafruit NeoPixel | 1.15.4 | Winde | LGPL-3.0-or-later | https://github.com/adafruit/Adafruit_NeoPixel | +| ESP32Servo | 3.1.3 | Winde | LGPL-2.1-or-later | https://github.com/madhephaestos/ESP32Servo | + +## Build framework + +| Component | Version | License | Source | +| --- | --- | --- | --- | +| Arduino core for ESP32 | 3.20017.241212+sha.dcc1105b | LGPL-2.1-or-later | https://github.com/espressif/arduino-esp32 | + +The PlatformIO platform and build tools are development dependencies. Their +licenses are distributed with their respective PlatformIO packages and are +not relicensed by this project. + +## License compatibility + +The source code in this repository is licensed under the MIT License in +`LICENSE`. MIT is compatible with the MIT, BSD, Apache-2.0 and LGPL components +listed above. The LGPL components remain under their original licenses; this +project does not change or relicense them. + +For a release containing compiled firmware, distribute this file together +with `LICENSE` and retain the copyright and license notices supplied by the +third-party components. U8g2 fonts must be checked separately when changing +fonts, because their licensing terms can differ from the U8g2 library code. diff --git a/extra_script.py b/extra_script.py index cd1042f..5c66bd8 100644 --- a/extra_script.py +++ b/extra_script.py @@ -5,4 +5,10 @@ Import("env") build_tag = env['PIOENV'] version_tag = os.getenv("FIRMWARE_VERSION") -env.Replace(PROGNAME="firmware_%s_%s" % (build_tag, version_tag)) \ No newline at end of file +env.Replace(PROGNAME="firmware_%s_%s" % (build_tag, version_tag)) + +# include toolchain paths +env.Replace(COMPILATIONDB_INCLUDE_TOOLCHAIN=True) + +# override compilation DB path +env.Replace(COMPILATIONDB_PATH=os.path.join("$BUILD_DIR", "compile_commands.json")) \ No newline at end of file diff --git a/lib/sailplane/airspeed.cpp b/lib/sailplane/airspeed.cpp index c91a135..53acb70 100644 --- a/lib/sailplane/airspeed.cpp +++ b/lib/sailplane/airspeed.cpp @@ -11,12 +11,19 @@ bfs::Ms4525do pres; Adafruit_BMP085 bmp; float pressure_avg[10]; -float basicaltitude; -void init_2ndi2c(){ - Wire1.begin(dp_sda, dp_scl); +float basicaltitude = 0.0f; +bool pressure_sensor_ready = false; + +bool init_2ndi2c(){ + if (!Wire1.begin(dp_sda, dp_scl)) + { + Serial.println("Error starting I2C"); + return false; + } Wire1.setClock(400000); + return true; } -void init_airspeed_sensor(void) +bool init_airspeed_sensor(void) { /* @@ -27,10 +34,9 @@ void init_airspeed_sensor(void) if (!pres.Begin()) { Serial.println("Error communicating with sensor"); - while (1) - { - } + return false; } + return true; } int get_airspeed_kmh(void) @@ -51,10 +57,22 @@ float get_airspeed(void) return 0; } } -void init_pressure_sensor(void) +bool init_pressure_sensor(void) { - bmp.begin(BMP085_ULTRAHIGHRES, &Wire1); + if (!bmp.begin(BMP085_ULTRAHIGHRES, &Wire1)) + { + Serial.println("Error communicating with pressure sensor"); + return false; + } basicaltitude=bmp.readPressure(); + if (basicaltitude <= 0.0f) + { + Serial.println("Invalid initial pressure"); + return false; + } + for (float &sample : pressure_avg) + sample = basicaltitude; + pressure_sensor_ready = true; Serial.print("Pressure = "); Serial.print(basicaltitude); Serial.println(" Pa"); @@ -64,21 +82,28 @@ void init_pressure_sensor(void) Serial.print("Altitude = "); Serial.print(get_altitude(basicaltitude)); Serial.println(" meters"); + return true; } float get_pressure() { + if (!pressure_sensor_ready) + return 0.0f; + float pressure; pressure = bmp.readPressure(); - memcpy(pressure_avg, &pressure_avg[1], sizeof(pressure_avg) - sizeof(int)); + if (pressure <= 0.0f) + return pressure_avg[9]; + + memmove(pressure_avg, &pressure_avg[1], sizeof(pressure_avg) - sizeof(pressure_avg[0])); pressure_avg[9] = pressure; return pressure; } float get_pressure_avg(void) { + float total = 0.0f; int i; - float total; for (i = 0; i <10; i++) { total = total + pressure_avg[i]; @@ -97,14 +122,14 @@ float get_vario_avg(void) float vario; int i; - float total; + float total = 0.0f; for (i = 0; i <9; i++) { int n=i+1; vario = get_altitude(pressure_avg[n]) - get_altitude(pressure_avg[i]); total = total + vario; } - return (total / 10); + return (total / 9); } float get_altitude(float pressure) @@ -115,5 +140,5 @@ float get_altitude(float pressure) } float get_altitude_diff(void){ - return bmp.readPressure() - basicaltitude; + return get_altitude(pressure_avg[9]) - get_altitude(basicaltitude); } diff --git a/lib/sailplane/airspeed.h b/lib/sailplane/airspeed.h index e8a6c95..03a7b90 100644 --- a/lib/sailplane/airspeed.h +++ b/lib/sailplane/airspeed.h @@ -1,8 +1,8 @@ #ifndef WINDENTELEMETRY_AIRSPEED_H #define WINDENTELEMETRY_AIRSPEED_H -void init_2ndi2c(void); -void init_airspeed_sensor(void); +bool init_2ndi2c(void); +bool init_airspeed_sensor(void); int get_airspeed_kmh(void); /*! @@ -10,7 +10,7 @@ int get_airspeed_kmh(void); */ float get_airspeed(void); -void init_pressure_sensor(void); +bool init_pressure_sensor(void); float get_pressure(void); float get_pressure_adv(void); float get_altitude(float pressure); diff --git a/lib/webinterface/webinterface.cpp b/lib/webinterface/webinterface.cpp new file mode 100644 index 0000000..11b0428 --- /dev/null +++ b/lib/webinterface/webinterface.cpp @@ -0,0 +1,242 @@ +#include "webinterface.h" +#include +#include +#include +#include +#include + +namespace +{ +constexpr char default_access_point_password[] = "telemetry"; +constexpr char settings_namespace[] = "telemetry"; +constexpr char default_callsign[] = "D1234"; +constexpr int default_min_speed = 80; +constexpr int default_max_speed = 150; +constexpr unsigned int default_active_time_seconds = 90; + +String default_access_point_name() +{ +#ifdef DEVICE_SAILPLANE + return String("Sailplane-") + default_callsign; +#else + return "Winde-Telemetry"; +#endif +} + +WebServer server(80); +Preferences preferences; +TelemetrySettings *active_settings = nullptr; +bool restart_after_update = false; +bool update_failed = false; +bool access_point_restart_pending = false; + +String html_escape(const String &value) +{ + String escaped = value; + escaped.replace("&", "&"); + escaped.replace("<", "<"); + escaped.replace(">", ">"); + escaped.replace("\"", """); + escaped.replace("'", "'"); + return escaped; +} + +void send_page(const String &message = String()) +{ + String page; + page.reserve(2600); + page += F("" + "" + "Winden-Telemetry

Winden-Telemetry

"); + if (message.length() > 0) + { + page += F("

"); + page += html_escape(message); + page += F("

"); + } + page += F("
" + "
" + "

Firmware aktualisieren

" + "" + "

Access Point: "); + page += html_escape(active_settings->access_point_name); + page += F("

"); + server.send(200, "text/html; charset=utf-8", page); +} + +bool parse_settings(TelemetrySettings &settings) +{ + String callsign = server.arg("callsign"); + String access_point_name = server.arg("ap_name"); + String access_point_password = server.arg("ap_password"); + int min_speed = server.arg("min").toInt(); + int max_speed = server.arg("max").toInt(); + int active_time_seconds = server.arg("active_time").toInt(); + callsign.trim(); + access_point_name.trim(); + access_point_password.trim(); + if (access_point_name.length() == 0 || access_point_name.length() > 32 || access_point_password.length() < 8 || access_point_password.length() > 63 || callsign.length() == 0 || callsign.length() > 16 || min_speed < 0 || max_speed > 300 || + min_speed >= max_speed || active_time_seconds < 1 || active_time_seconds > 600) + return false; + + settings.callsign = callsign; + settings.access_point_name = access_point_name; + settings.access_point_password = access_point_password; + settings.min_speed = min_speed; + settings.max_speed = max_speed; + settings.active_time_seconds = active_time_seconds; + return true; +} + +void handle_root() +{ + send_page(); +} + +void handle_settings() +{ + TelemetrySettings updated = *active_settings; + if (!parse_settings(updated)) + { + server.send(400, "text/plain; charset=utf-8", "Ungueltige Einstellungen"); + return; + } + + active_settings->callsign = updated.callsign; + active_settings->access_point_name = updated.access_point_name; + active_settings->access_point_password = updated.access_point_password; + active_settings->min_speed = updated.min_speed; + active_settings->max_speed = updated.max_speed; + active_settings->active_time_seconds = updated.active_time_seconds; + preferences.putString("callsign", active_settings->callsign); + preferences.putString("ap_name", active_settings->access_point_name); + preferences.putString("ap_password", active_settings->access_point_password); + preferences.putInt("min", active_settings->min_speed); + preferences.putInt("max", active_settings->max_speed); + preferences.putUInt("active_time", active_settings->active_time_seconds); + send_page("Einstellungen gespeichert."); + access_point_restart_pending = true; +} + +void handle_update_upload() +{ + HTTPUpload &upload = server.upload(); + if (upload.status == UPLOAD_FILE_START) + { + restart_after_update = false; + update_failed = false; + Serial.printf("Firmware-Upload: %s\n", upload.filename.c_str()); + if (!Update.begin(UPDATE_SIZE_UNKNOWN)) + { + update_failed = true; + Update.printError(Serial); + } + } + else if (upload.status == UPLOAD_FILE_WRITE) + { + if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) + { + update_failed = true; + Update.printError(Serial); + } + } + else if (upload.status == UPLOAD_FILE_END) + { + if (Update.end(true)) + { + restart_after_update = true; + Serial.printf("Firmware-Upload abgeschlossen: %u Bytes\n", upload.totalSize); + } + else + { + update_failed = true; + Update.printError(Serial); + } + } +} + +void handle_update_result() +{ + if (!update_failed && restart_after_update) + { + server.send(200, "text/plain; charset=utf-8", "Upload erfolgreich. Neustart..."); + if (restart_after_update) + { + delay(500); + ESP.restart(); + } + } + else + server.send(500, "text/plain; charset=utf-8", "Firmware-Upload fehlgeschlagen"); +} +} // namespace + +bool webinterface_begin(TelemetrySettings &settings) +{ + active_settings = &settings; + preferences.begin(settings_namespace, false); + settings.callsign = preferences.getString("callsign", default_callsign); + settings.access_point_name = preferences.getString("ap_name", default_access_point_name()); + settings.access_point_password = preferences.getString("ap_password", default_access_point_password); + settings.min_speed = preferences.getInt("min", default_min_speed); + settings.max_speed = preferences.getInt("max", default_max_speed); + settings.active_time_seconds = preferences.getUInt("active_time", default_active_time_seconds); + if (settings.access_point_name.length() == 0 || settings.access_point_name.length() > 32 || settings.access_point_password.length() < 8 || settings.access_point_password.length() > 63 || settings.callsign.length() == 0 || settings.min_speed < 0 || settings.min_speed >= settings.max_speed || + settings.max_speed > 300 || settings.active_time_seconds < 1 || settings.active_time_seconds > 600) + { + settings.callsign = default_callsign; + settings.access_point_name = default_access_point_name(); + settings.access_point_password = default_access_point_password; + settings.min_speed = default_min_speed; + settings.max_speed = default_max_speed; + settings.active_time_seconds = default_active_time_seconds; + } + + WiFi.mode(WIFI_AP); + bool ap_started = WiFi.softAP(settings.access_point_name, settings.access_point_password); + if (!ap_started) + return false; + + server.on("/", HTTP_GET, handle_root); + server.on("/settings", HTTP_POST, handle_settings); + server.on("/update", HTTP_POST, handle_update_result, handle_update_upload); + server.begin(); + Serial.print("Webinterface: http://"); + Serial.println(WiFi.softAPIP()); + return true; +} + +void webinterface_handle(void) +{ + server.handleClient(); + if (access_point_restart_pending) + { + access_point_restart_pending = false; + WiFi.softAPdisconnect(true); + delay(100); + if (!WiFi.softAP(active_settings->access_point_name, active_settings->access_point_password)) + Serial.println("Access Point konnte nicht neu gestartet werden"); + } +} + +const TelemetrySettings &webinterface_settings(void) +{ + return *active_settings; +} diff --git a/lib/webinterface/webinterface.h b/lib/webinterface/webinterface.h new file mode 100644 index 0000000..9dbdf60 --- /dev/null +++ b/lib/webinterface/webinterface.h @@ -0,0 +1,20 @@ +#ifndef WINDENTELEMETRY_WEBINTERFACE_H +#define WINDENTELEMETRY_WEBINTERFACE_H + +#include + +struct TelemetrySettings +{ + String callsign; + String access_point_name; + String access_point_password; + int min_speed; + int max_speed; + unsigned int active_time_seconds; +}; + +bool webinterface_begin(TelemetrySettings &settings); +void webinterface_handle(void); +const TelemetrySettings &webinterface_settings(void); + +#endif diff --git a/lib/winde/tacho.cpp b/lib/winde/tacho.cpp index d0b0f8a..85b800b 100644 --- a/lib/winde/tacho.cpp +++ b/lib/winde/tacho.cpp @@ -8,15 +8,14 @@ void init_servo(void) { myservo.setPeriodHertz(50); myservo.attach(servoPin); - set_tacho(0, 70, 150); } void set_tacho(int speed, int speed_min, int speed_max) { int val = map(speed, -25, 220, 0, 155); #ifdef DEBUG - Serial.print("Tacho Mapping: ") - Serial.println(val); + Serial.print("Tacho Mapping: "); + Serial.println(val); #endif myservo.write(val); pixels.clear(); @@ -72,11 +71,5 @@ void init_pixel(void) int setoffsetpixel(int pixel) { - int newpixel; - newpixel = pixel + pixeloffset; - if (newpixel >= NUMPIXELS) - { - newpixel = newpixel - NUMPIXELS; - } - return newpixel; + return (pixel + pixeloffset + NUMPIXELS) % NUMPIXELS; } \ No newline at end of file diff --git a/lib/windentelemetry.h b/lib/windentelemetry.h index 10f03da..fb2e4bd 100644 --- a/lib/windentelemetry.h +++ b/lib/windentelemetry.h @@ -2,6 +2,12 @@ #define WINDENTELEMETRY_CONFIG_H #define LORA_FREQ 868.0 +#define TELEMETRY_CALLSIGN "D1234" +#define TELEMETRY_SEND_INTERVAL_MS 1000UL +#define TELEMETRY_ACTIVE_TIME_MS 90000UL +#define TELEMETRY_LOW_SPEED_KMH 30 +#define default_min_tow_speed 80 +#define default_max_tow_speed 150 #ifdef HELTEC_WIFI_LORA_32_V3 // SX1262 on HELTEC_WIFI_LORA_32_V3 has the following connections: // NSS pin: 8 @@ -27,8 +33,6 @@ /**difference pressure**/ #define dp_scl 20 #define dp_sda 19 -#define default_min_tow_speed 80 -#define default_max_tow_speed 150 #endif #ifdef DEVICE_WINDE diff --git a/platformio.ini b/platformio.ini index 2741804..83b33a7 100644 --- a/platformio.ini +++ b/platformio.ini @@ -9,8 +9,8 @@ ; https://docs.platformio.org/page/projectconf.html [platformio] -build_cache_dir = ./.pio/.build_cache -cache_dir = ./.pio/.cache_dir +build_cache_dir = ~/.pio/.build_cache +cache_dir = ~/.pio/.cache_dir [common] platform = espressif32 @@ -25,6 +25,8 @@ build_flags = -D CORE_DEBUG_LEVEL=5 -D SLOW_CLK_TPYE=0 -D REGION_EU868 + -Ilib + -Isrc lib_deps = olikraus/U8g2@^2.36.15 jgromes/RadioLib@^7.4.0 @@ -41,22 +43,23 @@ lib_deps = bolderflight/Bolder Flight Systems Unit Conversions@^5.0.0 bolderflight/Bolder Flight Systems Airdata Calculations@^4.0.0 adafruit/Adafruit BMP085 Library @ ^1.2.4 -lib_ignore = - winde -build_flags = ${common.build_flags} +lib_ignore = winde +build_flags = + ${common.build_flags} -D DEVICE_SAILPLANE upload_port = COM8 monitor_port = COM8 + [env:heltec_wifi_lora_32_V3_winde] extends = common board = heltec_wifi_lora_32_V3 lib_deps = ${common.lib_deps} adafruit/Adafruit NeoPixel@^1.15.2 - madhephaestus/ESP32Servo@^3.0.9 -lib_ignore = - sailplane -build_flags = ${common.build_flags} + madhephaestus/ESP32Servo@^3.0.9 +lib_ignore = sailplane +build_flags = + ${common.build_flags} -D DEVICE_WINDE upload_port = COM9 -monitor_port = COM9 \ No newline at end of file +monitor_port = COM9 diff --git a/src/main.cpp b/src/main.cpp index 30dab7d..31d3a5d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3,23 +3,25 @@ #include #include #include +#include #ifdef DEVICE_WINDE #include #endif +TelemetrySettings telemetry_settings; + #ifdef DEVICE_SAILPLANE #include unsigned long previousMillisSend = 0; -const long send_interval = 1000; unsigned long previousMillissendtimer = 0; -const long sendtimer_max = 90000; bool send_data = false; bool is_airborn = false; -int lowspeedtimer = 0; -int lowspeedtimermax = 90000; +unsigned long lowspeedtimer = 0; +bool sensors_ready = false; #endif SX1262 radio = new Module(LORA_NSS, LORA_DIO1, LORA_NRST, LORA_BUSY); +bool radio_ready = false; #ifdef DEVICE_WINDE volatile bool receivedFlag = false; #if defined(ESP8266) || defined(ESP32) @@ -50,50 +52,53 @@ void setup() /* Serial to display data */ Serial.begin(115200); Serial.println("start"); + if (!webinterface_begin(telemetry_settings)) + Serial.println("Webinterface konnte nicht gestartet werden"); int state = radio.begin(LORA_FREQ); if (state == RADIOLIB_ERR_NONE) { + radio_ready = true; Serial.println(F("success!")); } else { Serial.print(F("failed, code ")); Serial.println(state); - while (true) - { - delay(10); - } + Serial.println(F("Webinterface bleibt verfuegbar")); } u8g2.begin(); u8g2.clearBuffer(); u8g2.setFont(u8g2_font_ncenB14_tr); #ifdef DEVICE_SAILPLANE u8g2.drawStr(0, 20, "Start Plane"); - init_2ndi2c(); - init_airspeed_sensor(); - init_pressure_sensor(); + sensors_ready = init_2ndi2c() && init_airspeed_sensor() && init_pressure_sensor(); + if (!sensors_ready) + { + u8g2.drawStr(0, 40, "Sensor error"); + } #endif #ifdef DEVICE_WINDE u8g2.drawStr(0, 20, "Start Winde"); - init_servo(); init_pixel(); + init_servo(); + set_tacho(0, telemetry_settings.min_speed, telemetry_settings.max_speed); radio.setPacketReceivedAction(setFlag); // start listening for LoRa packets - state = radio.startReceive(); - if (state == RADIOLIB_ERR_NONE) + if (radio_ready) { - Serial.println(F("success!")); - } - else - { - Serial.print(F("failed, code ")); - Serial.println(state); - while (true) + state = radio.startReceive(); + if (state == RADIOLIB_ERR_NONE) { - delay(10); + Serial.println(F("success!")); + } + else + { + Serial.print(F("failed, code ")); + Serial.println(state); + radio_ready = false; } } #endif @@ -105,10 +110,9 @@ String nmea_checksum(String str) byte cs = 0; for (unsigned int n = 1; n < str.length(); n++) { - if (str[n] != '$' || str[n] != '!' || str[n] != '*') - { - cs ^= str[n]; - } + if (str[n] == '*') + break; + cs ^= str[n]; } if (cs < 0x10) @@ -150,6 +154,9 @@ void draw_rssi(int Rssi){ void send_lora(String data) { + if (!radio_ready) + return; + int state = radio.transmit(data); if (state == RADIOLIB_ERR_NONE) { @@ -158,7 +165,7 @@ void send_lora(String data) // print measured data rate Serial.print(F("[SX1262] Datarate:\t")); - Serial.print(radio.getDataRate()); + //Serial.print(radio.getDataRate()); Serial.println(F(" bps")); } else if (state == RADIOLIB_ERR_PACKET_TOO_LONG) @@ -184,7 +191,7 @@ void send_speed(int vel_kmh) String data; // serializeJson(sailplane_data, data); // sprintf(data,"$PWTLS,%s,%u,%u,%u","D-1234",vel_kmh,70,140); - data = "$PWTLS,D1234," + String(vel_kmh) + ",70,140"; + data = "$PWTLS," + telemetry_settings.callsign + "," + String(vel_kmh) + "," + String(telemetry_settings.min_speed) + "," + String(telemetry_settings.max_speed); data += nmea_checksum(data); Serial.println(data); send_lora(data); @@ -194,7 +201,7 @@ void send_airborn() { String data; // serializeJson(sailplane_data, data); - data = "$PWTLA,D1234,1"; + data = "$PWTLA," + telemetry_settings.callsign + ",1"; data += nmea_checksum(data); Serial.println(data); send_lora(data); @@ -215,6 +222,8 @@ String getSplitValue(String data, char separator, int index) } return found > index ? data.substring(strIndex[0], strIndex[1]) : ""; } +#ifdef DEVICE_WINDE + void decode_start(String data) { data_from = getSplitValue(data, ',', 1); @@ -229,28 +238,59 @@ void decode_airborn(String data) data_min = 0; data_max = 0; } -void decode_msg(String data) + +bool valid_nmea(String data) +{ + int checksum_start = data.indexOf('*'); + if (checksum_start <= 1 || checksum_start + 2 >= data.length()) + return false; + + byte checksum = 0; + for (int i = 1; i < checksum_start; ++i) + checksum ^= data[i]; + + String received_checksum = data.substring(checksum_start + 1, checksum_start + 3); + received_checksum.toUpperCase(); + char expected_checksum[3]; + snprintf(expected_checksum, sizeof(expected_checksum), "%02X", checksum); + return received_checksum == expected_checksum; +} + +bool decode_msg(String data) { String pos1 = getSplitValue(data, ',', 0); if (pos1 == "$PWTLS") { + if (getSplitValue(data, ',', 4).length() == 0) + return false; decode_start(data); + return true; } else if (pos1 == "$PWTLA") { decode_airborn(data); + return true; } else { Serial.println("Nachricht nicht erkannt"); + return false; } } - +#endif void loop() { + webinterface_handle(); #ifdef DEVICE_SAILPLANE + if (!sensors_ready) + { + delay(1000); + return; + } + unsigned long currentMillis = millis(); - if (currentMillis - previousMillisSend >= send_interval) + unsigned long active_time_ms = telemetry_settings.active_time_seconds * 1000UL; + if (currentMillis - previousMillisSend >= TELEMETRY_SEND_INTERVAL_MS) { previousMillisSend = currentMillis; /* Read the sensor */ @@ -264,13 +304,13 @@ void loop() if (send_data == false) { - if (vel_kmh < 30) + if (vel_kmh < telemetry_settings.min_speed) { - lowspeedtimer++; - if (lowspeedtimer >= lowspeedtimermax) + lowspeedtimer += TELEMETRY_SEND_INTERVAL_MS; + if (lowspeedtimer >= active_time_ms) { is_airborn = false; - lowspeedtimer = lowspeedtimermax; + lowspeedtimer = active_time_ms; } } else @@ -289,7 +329,7 @@ void loop() } else { - if (currentMillis - previousMillissendtimer < sendtimer_max) + if (currentMillis - previousMillissendtimer < active_time_ms) { send_speed(vel_kmh); } @@ -308,7 +348,7 @@ void loop() Serial.println(vel_kmh); Serial.print(pres.pres_pa(), 6); Serial.print("IAS (m/s): "); - Serial.println(ias); + Serial.println(get_airspeed()); Serial.println(vel_kmh); Serial.print("\t"); Serial.print(pres.die_temp_c(), 6); @@ -318,7 +358,7 @@ void loop() #endif #ifdef DEVICE_WINDE - if (receivedFlag) + if (radio_ready && receivedFlag) { // reset flag receivedFlag = false; @@ -329,7 +369,11 @@ void loop() if (state == RADIOLIB_ERR_NONE) { - decode_msg(str); + if (!valid_nmea(str) || !decode_msg(str)) + { + Serial.println("Ungueltige Telemetrie-Nachricht"); + return; + } // packet was successfully received u8g2.clearBuffer(); u8g2.setFont(u8g2_font_ncenB10_tr); @@ -345,7 +389,6 @@ void loop() draw_rssi(radio.getRSSI()); u8g2.sendBuffer(); Serial.println(str); - int speed = str.toInt(); set_tacho(data_speed, data_min, data_max); } }