neues Module SKP, Übertragung der Signalleuchte zustand von Winde an SKP

This commit is contained in:
FCS committed 2026-08-30 12:40:52 +02:00
1 parent f468f09ad2
commit b4e7119289
7 files changed
+237 -46

No files matched your search

+15
View File
@@ -20,6 +20,7 @@ jobs:
environment:
- heltec_wifi_lora_32_V3_sailplane
- heltec_wifi_lora_32_V3_winde
- heltec_wifi_lora_32_V3_skp
runs-on: ubuntu-latest
container: catthehacker/ubuntu:act-latest
steps:
@@ -84,23 +85,35 @@ jobs:
with:
name: firmware-heltec_wifi_lora_32_V3_winde
path: firmware/winde
- name: Download SKP artifact
uses: christopherhx/gitea-download-artifact@75635f32b4c1c41c4b3d64e8f85210112ed4c9c7 # v4
with:
name: firmware-heltec_wifi_lora_32_V3_skp
path: firmware/skp
- name: Verify release assets
run: |
RELEASE_VERSION="${{ gitea.ref_name }}"
SAILPLANE_FILE=$(find firmware/sailplane -type f -name 'firmware_*.bin' -print -quit)
WINDE_FILE=$(find firmware/winde -type f -name 'firmware_*.bin' -print -quit)
SKP_FILE=$(find firmware/skp -type f -name 'firmware_*.bin' -print -quit)
test -n "$SAILPLANE_FILE"
test -n "$WINDE_FILE"
test -n "$SKP_FILE"
cp "$SAILPLANE_FILE" "firmware/firmware_sailplane_${RELEASE_VERSION}.bin"
cp "$WINDE_FILE" "firmware/firmware_winde_${RELEASE_VERSION}.bin"
cp "$SKP_FILE" "firmware/firmware_skp_${RELEASE_VERSION}.bin"
(cd firmware && sha256sum "firmware_sailplane_${RELEASE_VERSION}.bin" > "firmware_sailplane_${RELEASE_VERSION}.sha256")
(cd firmware && sha256sum "firmware_winde_${RELEASE_VERSION}.bin" > "firmware_winde_${RELEASE_VERSION}.sha256")
(cd firmware && sha256sum "firmware_skp_${RELEASE_VERSION}.bin" > "firmware_skp_${RELEASE_VERSION}.sha256")
test -s "firmware/firmware_sailplane_${RELEASE_VERSION}.bin"
test -s "firmware/firmware_winde_${RELEASE_VERSION}.bin"
test -s "firmware/firmware_skp_${RELEASE_VERSION}.bin"
test -s "firmware/firmware_sailplane_${RELEASE_VERSION}.sha256"
test -s "firmware/firmware_winde_${RELEASE_VERSION}.sha256"
test -s "firmware/firmware_skp_${RELEASE_VERSION}.sha256"
(cd firmware && sha256sum -c "firmware_sailplane_${RELEASE_VERSION}.sha256")
(cd firmware && sha256sum -c "firmware_winde_${RELEASE_VERSION}.sha256")
(cd firmware && sha256sum -c "firmware_skp_${RELEASE_VERSION}.sha256")
test -s LICENSE
test -s THIRD_PARTY_NOTICES.md
- name: Create release
@@ -109,8 +122,10 @@ jobs:
files: |-
firmware/firmware_sailplane_${{ gitea.ref_name }}.bin
firmware/firmware_winde_${{ gitea.ref_name }}.bin
firmware/firmware_skp_${{ gitea.ref_name }}.bin
firmware/firmware_sailplane_${{ gitea.ref_name }}.sha256
firmware/firmware_winde_${{ gitea.ref_name }}.sha256
firmware/firmware_skp_${{ gitea.ref_name }}.sha256
LICENSE
THIRD_PARTY_NOTICES.md
body_path: "CHANGELOG.md"
+22
View File
@@ -1,9 +1,24 @@
CHANGELOG
## v0.1.0 (30.08.2026)
+ modulabhängige Einstellungen im Webinterface nur noch für genutzte Werte sichtbar
+ gemeinsame AP-/OTA-Logik für die Module beibehalten
### Winden Modul
+ Warnleuchten-Erkennung ergänzt
+ Statusmeldung via LoRa an das SKP-Modul senden
### SKP Modul
+ neues Modul, ebenfalls mit Webinterface für Settings und OTA
+ empfangene Warnleuchten-Status via LoRa ausgewertet
+ Relais Steuerung
---
## v0.0.8a (30.08.2026)
+ fix: Wifi Auto off Nachrichten Schleife
+ Verbesserung Redirect to Einstellungs Seite
+ Verbesserung AutoBuild
---
## v0.0.8 (30.08.2026)
+ Firmware-Version als Build-Tag im Code und im Webinterface eingebaut
+ WLAN-Name für Segelflug- und Winden-Module angepasst (`Telemetry-<Callsign>` bzw. `Telemetry-Winch`)
@@ -12,18 +27,23 @@ CHANGELOG
+ Captive-Portal / DNS-Weiterleitung für das Webinterface ergänzt
+ Access Point bleibt nur 5 Minuten nach PowerOn aktiv, wenn kein Client verbunden ist, und wird dann automatisch deaktiviert
---
## v0.0.7d (28.08.2026)
+ AutoBuild und Release-Prozess für Versionsnamen, Prüfsummen und Release-Artefakte korrigiert
---
## v0.0.7c (28.08.2026)
+ korrektur AutoBuild (Checksum)
---
## v0.0.7b (28.08.2026)
+ korrektur AutoBuild
---
## v0.0.7a (28.08.2026)
+ verbessertes AutoBuild
---
## v0.0.7 (28.08.2026)
+ Webinterface über eigenen WLAN-Access-Point ergänzt
+ Persistente Konfiguration für WLAN-Name, WLAN-Passwort und Callsign
@@ -42,6 +62,7 @@ CHANGELOG
+ Initialisierung von Servo und LED-Ring korrigiert
+ NMEA-Nachrichtenprüfung und Fehlerbehandlung beim Empfang verbessert
---
## v0.0.6 (18.01.2026)
+ Anpassung Programmablaufplan
@@ -52,6 +73,7 @@ CHANGELOG
### Winden Modul
+ empfangen im NMEA Format
---
## v0.0.5 (30.11.2025)
+ Formatierung Code
### Segeflug Modul
+15 -12
View File
@@ -4,6 +4,8 @@
# Winden-Telemetry
Aktuelle Version: v0.1.0
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
@@ -33,7 +35,10 @@ für den Projektcode steht in [LICENSE](LICENSE).
- [x] Analoge Anzeige in der Winde
- [x] Farbige LEDs für Limits
- [x] Übertragungszeit Begrenzen
- [ ] Übertragung Rundumleuchten Signal an Start
- [x] 12V-Warnleuchten-Erkennung am Wind-Modul via GPIO 5
- [x] NMEA-Statusübertragung zum SKP-Modul mit Prefix $PWTLW
- [x] SKP-Modul mit Relaissteuerung über GPIO 7 und Timeout-Sicherung
- [x] eigenes SKP-Webinterface mit WLAN- und OTA-Funktionen
- [x] Luftdrucksensor für Höhe und Vario
### Webinterface und Firmware-Update
@@ -45,16 +50,14 @@ 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.
Im Webinterface lassen sich die WLAN-Einstellungen verwalten. Je nach Modul
werden nur die tatsächlich genutzten Werte angezeigt. Die Werte bleiben nach
einem Neustart erhalten. Das Sailplane-Modul verwendet sie für Telemetrie und
Schlepp-Erkennung, während die Wind- und SKP-Module die passenden Betriebswerte
für ihren jeweiligen Betrieb übernehmen.
Für ein Firmware-Update die zur jeweiligen Hardware passende `.bin`-Datei aus
`.pio/build/<environment>/` auswählen. Nach erfolgreichem Upload startet das
Gerät automatisch neu.
Für ein Firmware-Update die passende `.bin`-Datei aus `.pio/build/<environment>/`
auswählen. Nach einem erfolgreichen Upload startet das Gerät automatisch neu.
## Aufbau
@@ -95,9 +98,9 @@ Die Platine ist so gestaltet das sie individuell bestückt werden kann, je nach
| I2C_4 | optional| optional| optional|
| GPIO3 | optional| optional| optional|
| GPIO4 | optional| optional| optional|
| GPIO5 | optional| Eingang Warnleuchte |optional|
| GPIO5 | optional| Eingang Warnleuchte | optional|
| GPIO6 | optional| LED Ring | optional|
| GPIO7 | optional| Servo | für Relais|
| GPIO7 | optional| Servo | Relaissteuerung |
+4
View File
@@ -22,6 +22,8 @@ String default_access_point_name()
{
#ifdef DEVICE_SAILPLANE
return String(telemetry_ap_prefix) + default_callsign;
#elif defined(DEVICE_SKP)
return String(telemetry_ap_prefix) + "SKP";
#else
return String(telemetry_ap_prefix) + "Winch";
#endif
@@ -34,6 +36,8 @@ String generated_access_point_name(const String &callsign)
if (safe_callsign.length() == 0)
safe_callsign = default_callsign;
return String(telemetry_ap_prefix) + safe_callsign;
#elif defined(DEVICE_SKP)
return String(telemetry_ap_prefix) + "SKP";
#else
return String(telemetry_ap_prefix) + "Winch";
#endif
+11 -2
View File
@@ -41,11 +41,20 @@
#define servoPin 7
/**WS8212 **/
// Which pin on is connected to the WS8212?
#define NEOPIN 6
#define NEOPIN 6
// How many NeoPixels are attached
#define NUMPIXELS 12
#define NUMPIXELS 12
#define pixeloffset 6
#define WARN_LIGHT_SENSE_PIN 5
#define WARN_LIGHT_DETECT_THRESHOLD 1000
#define WARN_LIGHT_TX_INTERVAL_MS 5000UL
#endif
#ifdef DEVICE_SKP
#define RELAY_PIN 7
#define RELAY_PULSE_MS 500UL
#define WARNING_TIMEOUT_MS 5000UL
#endif
+10
View File
@@ -63,3 +63,13 @@ build_flags =
-D DEVICE_WINDE
upload_port = COM9
monitor_port = COM9
[env:heltec_wifi_lora_32_V3_skp]
extends = common
board = heltec_wifi_lora_32_V3
lib_deps =
${common.lib_deps}
lib_ignore = sailplane, winde
build_flags =
${common.build_flags}
-D DEVICE_SKP
+160 -32
View File
@@ -22,17 +22,87 @@ bool sensors_ready = false;
SX1262 radio = new Module(LORA_NSS, LORA_DIO1, LORA_NRST, LORA_BUSY);
bool radio_ready = false;
String nmea_checksum(String str)
{
byte cs = 0;
for (unsigned int n = 1; n < str.length(); n++)
{
if (str[n] == '*')
break;
cs ^= str[n];
}
if (cs < 0x10)
return "*0" + String(cs, HEX);
else
return "*" + String(cs, HEX);
}
#ifdef DEVICE_WINDE
volatile bool receivedFlag = false;
bool warning_light_active = false;
bool last_warning_light_state = false;
unsigned long last_warning_light_tx_ms = 0UL;
#if defined(ESP8266) || defined(ESP32)
ICACHE_RAM_ATTR
#endif
void setFlag(void)
{
// we got a packet, set the flag
receivedFlag = true;
}
bool read_warning_light_voltage()
{
int raw = analogRead(WARN_LIGHT_SENSE_PIN);
return raw > WARN_LIGHT_DETECT_THRESHOLD;
}
void send_warning_light_state(bool active)
{
String payload = "$PWTLW," + String(active ? "1" : "0");
payload += nmea_checksum(payload);
Serial.println(payload);
int state = radio.transmit(payload);
if (state != RADIOLIB_ERR_NONE)
{
Serial.print("Warnleuchte Status senden fehlgeschlagen, code ");
Serial.println(state);
}
}
#endif
#ifdef DEVICE_SKP
volatile bool skpReceivedFlag = false;
bool relay_active = false;
unsigned long relay_off_deadline = 0UL;
unsigned long last_valid_warning_ms = 0UL;
#if defined(ESP8266) || defined(ESP32)
ICACHE_RAM_ATTR
#endif
void skpSetFlag(void)
{
skpReceivedFlag = true;
}
void set_relay_state(bool active)
{
relay_active = active;
digitalWrite(RELAY_PIN, active ? HIGH : LOW);
if (active)
relay_off_deadline = millis() + RELAY_PULSE_MS;
else
relay_off_deadline = 0UL;
}
void trigger_relay_pulse()
{
last_valid_warning_ms = millis();
if (relay_active)
digitalWrite(RELAY_PIN, LOW);
delay(20);
set_relay_state(true);
}
#endif
#ifdef HELTEC_WIFI_LORA_32_V3
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, OLED_RST, OLED_CLK, OLED_DATA);
@@ -66,6 +136,29 @@ void setup()
Serial.println(state);
Serial.println(F("Webinterface bleibt verfuegbar"));
}
#ifdef DEVICE_WINDE
pinMode(WARN_LIGHT_SENSE_PIN, INPUT);
analogReadResolution(12);
analogSetPinAttenuation(WARN_LIGHT_SENSE_PIN, ADC_11db);
last_warning_light_state = read_warning_light_voltage();
#endif
#ifdef DEVICE_SKP
pinMode(RELAY_PIN, OUTPUT);
digitalWrite(RELAY_PIN, LOW);
relay_active = false;
last_valid_warning_ms = millis();
radio.setPacketReceivedAction(skpSetFlag);
if (radio_ready)
{
state = radio.startReceive();
if (state != RADIOLIB_ERR_NONE)
{
Serial.print(F("SKP Receive failed, code "));
Serial.println(state);
radio_ready = false;
}
}
#endif
u8g2.begin();
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_ncenB14_tr);
@@ -105,21 +198,6 @@ void setup()
u8g2.sendBuffer();
}
String nmea_checksum(String str)
{
byte cs = 0;
for (unsigned int n = 1; n < str.length(); n++)
{
if (str[n] == '*')
break;
cs ^= str[n];
}
if (cs < 0x10)
return "*0" + String(cs, HEX);
else
return "*" + String(cs, HEX);
}
void draw_rssi(int Rssi){
u8g2.drawFrame(102,8,4,3);
u8g2.drawFrame(107,6,4,5);
@@ -222,22 +300,6 @@ 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);
data_speed = (getSplitValue(data, ',', 2)).toInt();
data_min = (getSplitValue(data, ',', 3)).toInt();
data_max = (getSplitValue(data, ',', 4)).toInt();
}
void decode_airborn(String data)
{
data_from = getSplitValue(data, ',', 1);
data_speed = 0;
data_min = 0;
data_max = 0;
}
bool valid_nmea(String data)
{
@@ -256,6 +318,23 @@ bool valid_nmea(String data)
return received_checksum == expected_checksum;
}
#ifdef DEVICE_WINDE
void decode_start(String data)
{
data_from = getSplitValue(data, ',', 1);
data_speed = (getSplitValue(data, ',', 2)).toInt();
data_min = (getSplitValue(data, ',', 3)).toInt();
data_max = (getSplitValue(data, ',', 4)).toInt();
}
void decode_airborn(String data)
{
data_from = getSplitValue(data, ',', 1);
data_speed = 0;
data_min = 0;
data_max = 0;
}
bool decode_msg(String data)
{
String pos1 = getSplitValue(data, ',', 0);
@@ -281,6 +360,55 @@ bool decode_msg(String data)
void loop()
{
webinterface_handle();
#ifdef DEVICE_WINDE
bool current_warning_light_state = read_warning_light_voltage();
if (current_warning_light_state != last_warning_light_state)
{
last_warning_light_state = current_warning_light_state;
last_warning_light_tx_ms = millis();
send_warning_light_state(current_warning_light_state);
}
#endif
#ifdef DEVICE_SKP
if (relay_active && millis() >= relay_off_deadline)
{
set_relay_state(false);
}
if (radio_ready && skpReceivedFlag)
{
skpReceivedFlag = false;
String str;
int state = radio.readData(str);
if (state == RADIOLIB_ERR_NONE)
{
if (!valid_nmea(str))
{
Serial.println("Ungueltige SKP-Nachricht");
}
else if (getSplitValue(str, ',', 0) == "$PWTLW")
{
String status_field = getSplitValue(str, ',', 1);
status_field.trim();
bool warning_active = status_field == "1" || status_field == "ON" || status_field == "true";
if (warning_active)
{
trigger_relay_pulse();
Serial.println("Warnleuchte aktiv -> Relais an");
}
else
{
set_relay_state(false);
last_valid_warning_ms = millis();
Serial.println("Warnleuchte inaktiv -> Relais aus");
}
}
}
}
if (millis() - last_valid_warning_ms > WARNING_TIMEOUT_MS)
{
set_relay_state(false);
}
#endif
#ifdef DEVICE_SAILPLANE
if (!sensors_ready)
{