From ed83354dfbd5ec48f59580820d06e119cda1e057 Mon Sep 17 00:00:00 2001 From: FCS Date: Mon, 1 Dec 2025 17:55:44 +0100 Subject: [PATCH] migration repo --- .gitea/workflows/build_release.yaml | 64 ++++++++++ .gitignore | 6 + CHANGELOG.md | 35 ++++++ README.md | 28 +++++ extra_script.py | 8 ++ lib/README | 46 +++++++ lib/sailplane/airspeed.cpp | 119 ++++++++++++++++++ lib/sailplane/airspeed.h | 25 ++++ lib/winde/tacho.cpp | 82 +++++++++++++ lib/winde/tacho.h | 15 +++ lib/windentelemetry.h | 48 ++++++++ platformio.ini | 61 ++++++++++ src/main.cpp | 182 ++++++++++++++++++++++++++++ 13 files changed, 719 insertions(+) create mode 100644 .gitea/workflows/build_release.yaml create mode 100644 .gitignore create mode 100644 CHANGELOG.md create mode 100644 README.md create mode 100644 extra_script.py create mode 100644 lib/README create mode 100644 lib/sailplane/airspeed.cpp create mode 100644 lib/sailplane/airspeed.h create mode 100644 lib/winde/tacho.cpp create mode 100644 lib/winde/tacho.h create mode 100644 lib/windentelemetry.h create mode 100644 platformio.ini create mode 100644 src/main.cpp diff --git a/.gitea/workflows/build_release.yaml b/.gitea/workflows/build_release.yaml new file mode 100644 index 0000000..fc20416 --- /dev/null +++ b/.gitea/workflows/build_release.yaml @@ -0,0 +1,64 @@ +on: + push: + tags: + - "*" +jobs: + build: + env: + RUNNER_TOOL_CACHE: /toolcache + PLATFORMIO_BUILD_CACHE_DIR: ~/.platformio/.build_cache + PLATFORMIO_CACHE_DIR: ~/.platformio/.cachedir + name: Build + runs-on: ubuntu-latest + container: catthehacker/ubuntu:act-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + - name: Cache + uses: actions/cache@v4 + with: + path: | + ~/.cache/pip + ~/.platformio/.cache + ~/.platformio/.build_cache + ~/.platformio/.cachedir + key: ${{ runner.os }}-pio + - name: Set up python + uses: actions/setup-python@v5 + with: + python-version: "3.14.0" + architecture: "x64" + - name: Install PlatformIO + run: python3 -m pip install platformio==6.1.18 + - name: Build firmwares + run: FIRMWARE_VERSION=${{gitea.ref_name}} platformio run + - name: Archive + uses: christopherhx/gitea-upload-artifact@v4 + with: + name: firmware + path: .pio/build/*/firmware_*.bin + retention-days: 1 + release: + name: Release + # if: startsWith(github.event.ref, 'refs/tags/v') + needs: build + permissions: + contents: write + runs-on: ubuntu-latest + container: catthehacker/ubuntu:act-latest + steps: + - name: Checkout repo for CHANGELOG + uses: actions/checkout@v4 + - name: Download artifacts + uses: christopherhx/gitea-download-artifact@v4 + with: + name: firmware + path: firmware + # - name: Display structure of downloaded files + # run: ls -R + - name: release + uses: akkuman/gitea-release-action@v1 + with: + files: |- + **/firmware_*.bin + body_path: "CHANGELOG.md" \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..632967d --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +.pio +.vscode/.browse.c_cpp.db* +.vscode/c_cpp_properties.json +.vscode/launch.json +.vscode/ipch +.vscode/ \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..80e6489 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,35 @@ +CHANGELOG + +## 0.0.5 (30.11.2025) ++ Formatierung Code +### Segeflug Modul ++ Luftdrucksensor (BMP180) hinzugefügt, für Höhe und Vario ++ Durchschnittswerte der letzen 10 Messungen (Druck, Höhe, Vario) ++ aktuelle Werte der Messungen (Druck, Höhe, Vario) ++ Höhenunterschied seit Device Start + +--- +## v0.0.4 (28.11.2025) ++ LORA Module Pins Variable ++ OLED Module Pins Variable ++ Cache Funktion in Platformio +### Segeflug Modul ++ Ausslagern differenz Drucksensor Funktion +### Winden Modul ++ erweitern der LED Anzeige, für zulangsam und zuschnell, bzw im Grenzbereich + +--- +## v0.0.3 (28.11.2025) ++ Autobuild and Release in Git ++ aktivieren cache im Autobuild + +--- +## v0.0.2 (27.11.2025) ++ Auslagern Funtion für Servo und Pixel ++ verschiedene Libs für Winde und Segelflug + +--- +## v0.0.1 (23.11.2025) ++ initial Version ++ Sende IAS via LORA ++ Anzeige Tacho über Servo und LEDs (nur innerhalb zulässiger Schleppgeschwindigkeit) diff --git a/README.md b/README.md new file mode 100644 index 0000000..dfe5e1e --- /dev/null +++ b/README.md @@ -0,0 +1,28 @@ +[![build status](https://gitea.rabe11.de/FCS/Winden-Telemetry/actions/workflows/build_release.yaml/badge.svg?event=push)](https://gitea.rabe11.de/FCS/Winden-Telemetry/releases) + +# Winden-Telemetry + + +Übertragung der IAS des Segelflugzeuges an die Winde + +Hardware: +- [Heltec LoRa 32 (V3)](https://heltec.org/project/wifi-lora-32-v3/) +- SG90 Servo +- MS4525 differenz Drucksensor +- 12 LED Ring WS2812b +- BMP180 Temperatur und Luftdrucksensor +- DC-DC Step Down Wandler 3A MP1584 +- Relais + +Features: +- [x] LORA zum Übertragen +- [x] Airspeed via differenz Drucksensor +- [ ] Webinterface +- [ ] min/max speed einstellbar +- [ ] Kennzeichen einstellbar +- [x] Analoge Anzeige in der Winde +- [x] Farbige LEDs für Limits +- [ ] Übertragungszeit Begrenzen +- [ ] Übertragungszeit einstellbar +- [ ] Übertragung Rundumleuchten Signal an Start +- [x] Luftdrucksensor für Höhe und Vario diff --git a/extra_script.py b/extra_script.py new file mode 100644 index 0000000..cd1042f --- /dev/null +++ b/extra_script.py @@ -0,0 +1,8 @@ +import os +Import("env") + +# Access to global construction environment +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 diff --git a/lib/README b/lib/README new file mode 100644 index 0000000..9379397 --- /dev/null +++ b/lib/README @@ -0,0 +1,46 @@ + +This directory is intended for project specific (private) libraries. +PlatformIO will compile them to static libraries and link into the executable file. + +The source code of each library should be placed in a separate directory +("lib/your_library_name/[Code]"). + +For example, see the structure of the following example libraries `Foo` and `Bar`: + +|--lib +| | +| |--Bar +| | |--docs +| | |--examples +| | |--src +| | |- Bar.c +| | |- Bar.h +| | |- library.json (optional. for custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html +| | +| |--Foo +| | |- Foo.c +| | |- Foo.h +| | +| |- README --> THIS FILE +| +|- platformio.ini +|--src + |- main.c + +Example contents of `src/main.c` using Foo and Bar: +``` +#include +#include + +int main (void) +{ + ... +} + +``` + +The PlatformIO Library Dependency Finder will find automatically dependent +libraries by scanning project source files. + +More information about PlatformIO Library Dependency Finder +- https://docs.platformio.org/page/librarymanager/ldf.html diff --git a/lib/sailplane/airspeed.cpp b/lib/sailplane/airspeed.cpp new file mode 100644 index 0000000..c91a135 --- /dev/null +++ b/lib/sailplane/airspeed.cpp @@ -0,0 +1,119 @@ +#include "../windentelemetry.h" +#include "airspeed.h" +#include +#include +#include +#include +#include + +extern TwoWire Wire1; +bfs::Ms4525do pres; +Adafruit_BMP085 bmp; + +float pressure_avg[10]; +float basicaltitude; +void init_2ndi2c(){ + Wire1.begin(dp_sda, dp_scl); + Wire1.setClock(400000); +} +void init_airspeed_sensor(void) +{ + + /* + * I2C address of 0x28, on bus 0, with a -1 to +1 PSI range + */ + pres.Config(&Wire1, 0x28, 1.0f, -1.0f); + /* Starting communication with the pressure transducer */ + if (!pres.Begin()) + { + Serial.println("Error communicating with sensor"); + while (1) + { + } + } +} + +int get_airspeed_kmh(void) +{ + int vel_kmh = round(bfs::convvel(get_airspeed(), bfs::LinVelUnit::MPS, bfs::LinVelUnit::KPH)); + return vel_kmh; +} +float get_airspeed(void) +{ + if (pres.Read()) + { + float ms = bfs::Ias_mps(pres.pres_pa()); + + return ms; + } + else + { + return 0; + } +} +void init_pressure_sensor(void) +{ + bmp.begin(BMP085_ULTRAHIGHRES, &Wire1); + basicaltitude=bmp.readPressure(); + Serial.print("Pressure = "); + Serial.print(basicaltitude); + Serial.println(" Pa"); + + // Calculate altitude assuming 'standard' barometric + // pressure of 1013.25 millibar = 101325 Pascal + Serial.print("Altitude = "); + Serial.print(get_altitude(basicaltitude)); + Serial.println(" meters"); +} + +float get_pressure() +{ + float pressure; + pressure = bmp.readPressure(); + memcpy(pressure_avg, &pressure_avg[1], sizeof(pressure_avg) - sizeof(int)); + pressure_avg[9] = pressure; + return pressure; +} + +float get_pressure_avg(void) +{ + int i; + float total; + for (i = 0; i <10; i++) + { + total = total + pressure_avg[i]; + } + return (total / 10); +} +float get_vario(void) +{ + float vario; + vario = get_altitude(pressure_avg[9]) - get_altitude(pressure_avg[8]); + return vario; +} + +float get_vario_avg(void) +{ + float vario; + + int i; + float total; + 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); +} + +float get_altitude(float pressure) +{ + float altitude; + altitude = 44330 * (1.0 - pow(pressure / 101325, 0.1903)); + return altitude; +} + +float get_altitude_diff(void){ + return bmp.readPressure() - basicaltitude; +} diff --git a/lib/sailplane/airspeed.h b/lib/sailplane/airspeed.h new file mode 100644 index 0000000..e8a6c95 --- /dev/null +++ b/lib/sailplane/airspeed.h @@ -0,0 +1,25 @@ +#ifndef WINDENTELEMETRY_AIRSPEED_H +#define WINDENTELEMETRY_AIRSPEED_H + +void init_2ndi2c(void); +void init_airspeed_sensor(void); + +int get_airspeed_kmh(void); +/*! +@brief Holt die Indikates Airspeed vom Sensor über Druckunterschied und gibt m/s +*/ +float get_airspeed(void); + +void init_pressure_sensor(void); +float get_pressure(void); +float get_pressure_adv(void); +float get_altitude(float pressure); +float get_vario(void); +float get_vario_avg(void); +/*! +@brief Gibt den Höhenunterschied seit Device Start +*/ +float get_altitude_diff(void); + + +#endif diff --git a/lib/winde/tacho.cpp b/lib/winde/tacho.cpp new file mode 100644 index 0000000..d0b0f8a --- /dev/null +++ b/lib/winde/tacho.cpp @@ -0,0 +1,82 @@ +#include "../windentelemetry.h" +#include "tacho.h" +#include +#include +Servo myservo; +Adafruit_NeoPixel pixels(NUMPIXELS, NEOPIN, NEO_GRB + NEO_KHZ800); +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); +#endif + myservo.write(val); + pixels.clear(); + // normal speed + if ((speed >= speed_min) && (speed <= speed_max)) + { + int n; + for (n = speed_min + 5; n < speed_max - 5; n = n + 10) + { + int i = map(n, 0, 220, 1, 12); + pixels.setPixelColor(setoffsetpixel(i), pixels.Color(0, 150, 0)); + } + } + // knapp am minimum + if ((speed <= speed_min + 5) && (speed >= speed_min)) + { + int i = map(speed, 0, 220, 1, 12); + pixels.setPixelColor(setoffsetpixel(i - 1), pixels.Color(150, 150, 0)); + } + // knapp über max + if ((speed <= speed_max) && (speed >= speed_max - 5)) + { + int i = map(speed, 0, 220, 1, 12); + pixels.setPixelColor(setoffsetpixel(i), pixels.Color(150, 0, 0)); + } + // kleiner minimium + if ((speed < speed_min) && (speed > 20)) + { + int n; + for (n = 0; n < speed_min; n = n + 10) + { + int i = map(n, 0, 220, 1, 12); + pixels.setPixelColor(setoffsetpixel(i - 1), pixels.Color(150, 150, 0)); + } + } + // größer max + if (speed > speed_max) + { + int n; + for (n = speed_max; n <= 220; n = n + 10) + { + int i = map(n, 0, 220, 1, 12); + pixels.setPixelColor(setoffsetpixel(i), pixels.Color(150, 0, 0)); + } + } + pixels.show(); +} + +void init_pixel(void) +{ + pixels.begin(); +} + +int setoffsetpixel(int pixel) +{ + int newpixel; + newpixel = pixel + pixeloffset; + if (newpixel >= NUMPIXELS) + { + newpixel = newpixel - NUMPIXELS; + } + return newpixel; +} \ No newline at end of file diff --git a/lib/winde/tacho.h b/lib/winde/tacho.h new file mode 100644 index 0000000..be37a97 --- /dev/null +++ b/lib/winde/tacho.h @@ -0,0 +1,15 @@ +#ifndef WINDENTELEMETRY_TACHO_H +#define WINDENTELEMETRY_TACHO_H +#include "../windentelemetry.h" + +void init_servo(void); +/*! + @brief Set die Geschwidigkeit des Tachos über den Servo + @param speed die Geschwindigkeit als int in km/h, kt oder m/s je nach Tacho +*/ +void set_tacho(int speed, int speed_min, int speed_max); + +void init_pixel(void); + +int setoffsetpixel(int pixel); +#endif \ No newline at end of file diff --git a/lib/windentelemetry.h b/lib/windentelemetry.h new file mode 100644 index 0000000..10f03da --- /dev/null +++ b/lib/windentelemetry.h @@ -0,0 +1,48 @@ +#ifndef WINDENTELEMETRY_CONFIG_H +#define WINDENTELEMETRY_CONFIG_H + +#define LORA_FREQ 868.0 +#ifdef HELTEC_WIFI_LORA_32_V3 +// SX1262 on HELTEC_WIFI_LORA_32_V3 has the following connections: +// NSS pin: 8 +// DIO1 pin: 14 +// NRST pin: 12 +// BUSY pin: 13 +#define LORA_NSS 8 +#define LORA_DIO1 14 +#define LORA_NRST 12 +#define LORA_BUSY 13 + +// OLED SSD1306 on HELTEC_WIFI_LORA_32_V3 has the following connections: +#define OLED_RST 21 +#define OLED_CLK 18 +#define OLED_DATA 17 + +#endif + + + +#ifdef DEVICE_SAILPLANE +/*********************************devices*********************************************/ +/**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 +/*********************************devices*********************************************/ +/**Servo**/ +#define servoPin 7 +/**WS8212 **/ +// Which pin on is connected to the WS8212? +#define NEOPIN 6 + +// How many NeoPixels are attached +#define NUMPIXELS 12 +#define pixeloffset 6 +#endif + + +#endif diff --git a/platformio.ini b/platformio.ini new file mode 100644 index 0000000..f5f0190 --- /dev/null +++ b/platformio.ini @@ -0,0 +1,61 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + +[platformio] +build_cache_dir=./.pio/.build_cache +cache_dir=./.pio/.cache_dir + +[common] +platform = espressif32 +framework = arduino +build_flags = + '-DPIOENV="${PIOENV}"' + -D HELTEC_BOARD=30 + -D HELTEC_WIFI_LORA_32_V3 + -D WIFI_LORA_32_V3 + -D MCU_ESP32_S3 + -D RADIO_CHIP_SX1262 + -D CORE_DEBUG_LEVEL=5 + -D SLOW_CLK_TPYE=0 + -D REGION_EU868 +lib_deps = + olikraus/U8g2@^2.36.15 + jgromes/RadioLib@^7.4.0 +monitor_speed = 115200 +extra_scripts = pre:extra_script.py + +[env:heltec_wifi_lora_32_V3_sailplane] +extends = common +board = heltec_wifi_lora_32_V3 +lib_deps = ${common.lib_deps} + bolderflight/Bolder Flight Systems MS4525@^1.1.3 + 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} + -D DEVICE_SAILPLANE + + +[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} + -D DEVICE_WINDE + diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..e5b19cb --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,182 @@ +#include "../lib/windentelemetry.h" +#include +#include +#include + +#ifdef DEVICE_WINDE +#include +#endif + +#ifdef DEVICE_SAILPLANE +#include +#endif + +SX1262 radio = new Module(LORA_NSS, LORA_DIO1, LORA_NRST, LORA_BUSY); + +#ifdef DEVICE_WINDE +volatile bool receivedFlag = false; + +// this function is called when a complete packet +// is received by the module +// IMPORTANT: this function MUST be 'void' type +// and MUST NOT have any arguments! +#if defined(ESP8266) || defined(ESP32) +ICACHE_RAM_ATTR +#endif +void setFlag(void) +{ + // we got a packet, set the flag + receivedFlag = true; +} + +#endif +#ifdef HELTEC_WIFI_LORA_32_V3 +U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, OLED_RST, OLED_CLK, OLED_DATA); +#endif + +void setup() +{ + /* Serial to display data */ + Serial.begin(115200); + Serial.println("start"); + int state = radio.begin(LORA_FREQ); + if (state == RADIOLIB_ERR_NONE) + { + Serial.println(F("success!")); + } + else + { + Serial.print(F("failed, code ")); + Serial.println(state); + while (true) + { + delay(10); + } + } + 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(); +#endif +#ifdef DEVICE_WINDE + u8g2.drawStr(0, 20, "Start Winde"); + init_servo(); + init_pixel(); + + radio.setPacketReceivedAction(setFlag); + + // start listening for LoRa packets + + state = radio.startReceive(); + if (state == RADIOLIB_ERR_NONE) + { + Serial.println(F("success!")); + } + else + { + Serial.print(F("failed, code ")); + Serial.println(state); + while (true) + { + delay(10); + } + } +#endif + u8g2.sendBuffer(); +} + +void loop() +{ +#ifdef DEVICE_SAILPLANE + + /* Read the sensor */ + get_pressure(); + int vel_kmh = get_airspeed_kmh(); + u8g2.clearBuffer(); + u8g2.setFont(u8g2_font_logisoso42_tr); + u8g2.setCursor(0, 60); + u8g2.print(vel_kmh); + u8g2.sendBuffer(); + if (vel_kmh > 30) + { + String data = String(vel_kmh); + int state = radio.transmit(data); + if (state == RADIOLIB_ERR_NONE) + { + // the packet was successfully transmitted + Serial.println(F("success!")); + + // print measured data rate + Serial.print(F("[SX1262] Datarate:\t")); + Serial.print(radio.getDataRate()); + Serial.println(F(" bps")); + } + else if (state == RADIOLIB_ERR_PACKET_TOO_LONG) + { + // the supplied packet was longer than 256 bytes + Serial.println(F("too long!")); + } + else if (state == RADIOLIB_ERR_TX_TIMEOUT) + { + // timeout occured while transmitting packet + Serial.println(F("timeout!")); + } + else + { + // some other error occurred + Serial.print(F("failed, code ")); + Serial.println(state); + } + +#ifdef DEBUG + /* Display the data */ + Serial.print("IAS (km/h): "); + Serial.println(vel_kmh); + Serial.print(pres.pres_pa(), 6); + Serial.print("IAS (m/s): "); + Serial.println(ias); + Serial.println(vel_kmh); + Serial.print("\t"); + Serial.print(pres.die_temp_c(), 6); + Serial.print("\n"); +#endif + delay(1000); + } + +#endif +#ifdef DEVICE_WINDE + if (receivedFlag) + { + // reset flag + receivedFlag = false; + + // you can read received data as an Arduino String + String str; + int state = radio.readData(str); + + // you can also read received data as byte array + /* + byte byteArr[8]; + int numBytes = radio.getPacketLength(); + int state = radio.readData(byteArr, numBytes); + */ + + if (state == RADIOLIB_ERR_NONE) + { + // packet was successfully received + u8g2.clearBuffer(); + u8g2.setFont(u8g2_font_logisoso42_tr); + u8g2.setCursor(0, 60); + u8g2.print(str); + u8g2.sendBuffer(); + Serial.println(str); + int speed = str.toInt(); + set_tacho(speed, 70, 150); + } + } +#endif +} \ No newline at end of file