migration repo

This commit is contained in:
FCS
2025-12-01 17:55:44 +01:00
commit 9667455bf1
13 changed files with 719 additions and 0 deletions

View File

@@ -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"

6
.gitignore vendored Normal file
View File

@@ -0,0 +1,6 @@
.pio
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch
.vscode/

35
CHANGELOG.md Normal file
View File

@@ -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)

28
README.md Normal file
View File

@@ -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

8
extra_script.py Normal file
View File

@@ -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))

46
lib/README Normal file
View File

@@ -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 <Foo.h>
#include <Bar.h>
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

119
lib/sailplane/airspeed.cpp Normal file
View File

@@ -0,0 +1,119 @@
#include "../windentelemetry.h"
#include "airspeed.h"
#include <ms4525do.h>
#include <airdata.h>
#include <units.h>
#include <Wire.h>
#include <Adafruit_BMP085.h>
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;
}

25
lib/sailplane/airspeed.h Normal file
View File

@@ -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

82
lib/winde/tacho.cpp Normal file
View File

@@ -0,0 +1,82 @@
#include "../windentelemetry.h"
#include "tacho.h"
#include <ESP32Servo.h>
#include <Adafruit_NeoPixel.h>
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;
}

15
lib/winde/tacho.h Normal file
View File

@@ -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

48
lib/windentelemetry.h Normal file
View File

@@ -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

61
platformio.ini Normal file
View File

@@ -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

182
src/main.cpp Normal file
View File

@@ -0,0 +1,182 @@
#include "../lib/windentelemetry.h"
#include <Arduino.h>
#include <U8g2lib.h>
#include <RadioLib.h>
#ifdef DEVICE_WINDE
#include <tacho.h>
#endif
#ifdef DEVICE_SAILPLANE
#include <airspeed.h>
#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
}