Neigungsmesser: zurück auf den bewährten Firmware-Aufbau

Die überarbeitete Firmware liess sich prüfen und fehlerfrei übersetzen,
blieb auf dem Gerät aber stumm - keine Werte, keine Ausgabe über die
serielle Schnittstelle. Der Umbau war zu gross für das, was sich ohne
Gerätezugriff absichern lässt: config und compile sagen nichts über das
Laufzeitverhalten.

Die Firmware setzt jetzt wieder auf dem bewährten Stand 1.0 auf und ändert
daran nur zweierlei: die Kalibrierung über Bluetooth funktioniert (die
Charakteristik war beschreibbar, hatte aber keine Aktion hinterlegt und
tat nichts), und der ESP-NOW-Rest ist entfernt, der bei jedem Messwert ein
"hallo" an eine feste MAC schickte. Kein notify, kein aktives Setzen der
Werte, kein Zugriff auf den BLE-Server aus dem Boot-Ablauf heraus.

In der App fällt damit ein Denkfehler auf: fehlende Kalibrier-Offsets
galten als "nicht kalibriert" statt als "unbekannt". Bei Firmware, die
sie nicht meldet, stünde dauerhaft eine Warnung, die sich nicht abstellen
lässt. Gewarnt wird jetzt nur, wenn das Gerät ausdrücklich Nulloffsets
meldet. Der Rücksetz-Knopf hing an derselben Bedingung und wäre sonst
unerreichbar gewesen.

Das Abfragen im Takt war bereits als Rückfallebene eingebaut und trägt
diesen Aufbau ohne Änderung.

Ausserdem nimmt die .gitignore jetzt virtuelle Python-Umgebungen aus. Eine
solche war versehentlich mitversioniert worden - über 14000 Dateien, die
maschinenabhängig sind und im Repo nichts zu suchen haben.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
BiasF
2026-08-30 15:39:20 +02:00
co-authored by Claude Opus 5
parent 9406a749e5
commit 826a201a36
8 changed files with 174 additions and 141 deletions
+98 -121
View File
@@ -1,74 +1,61 @@
# VanAlign Pro Neigungsmessung über BLE
# VanAlign Pro - Neigungsmessung über BLE
#
# ESP32-S3 mit MPU6050. Liefert Pitch und Roll als 32-Bit-Float
# (little-endian) und lässt sich über Bluetooth auf die aktuelle Lage
# kalibrieren.
# Bewusst nah am bewährten Stand 1.0 gehalten. Gegenüber diesem geändert:
#
# Die UUIDs sind gegenüber Version 1.0 unverändert, damit die vorhandene
# WebApp weiterläuft. Neu sind:
# * notify auf Pitch und Roll die Werte werden gesendet, sobald sie sich
# ändern, statt vom Client im Sekundentakt abgefragt zu werden.
# * eine funktionierende Kalibrierung über Bluetooth. Bisher war die
# Charakteristik zwar beschreibbar, hatte aber keine Aktion hinterlegt,
# und der Kalibrier-Knopf war nur über den Webserver erreichbar, der
# auskommentiert ist. Kalibrieren war damit gar nicht möglich.
# * die gespeicherten Offsets sind auslesbar, sodass ein Client zeigen
# kann, ob überhaupt schon kalibriert wurde.
# * Die Kalibrierung funktioniert. Die Charakteristik ...3427 war zwar
# beschreibbar, hatte aber keine Aktion hinterlegt und tat nichts; der
# Kalibrier-Knopf war nur über den Webserver erreichbar, der
# auskommentiert ist. Kalibrieren war damit auf keinem Weg möglich.
# * Der ESP-NOW-Rest ist entfernt. Er schickte bei jedem einzelnen
# Messwert ein "hallo" an eine fest eingetragene MAC-Adresse.
#
# Bewusst NICHT geändert: Pitch und Roll bleiben reine Lesewerte mit
# hinterlegtem Ausdruck. Eine Fassung mit notify und aktiv gesetzten Werten
# liess das Gerät verstummen - sie ist in der Projektgeschichte unter
# "Neigungsmesser VanAlign einbinden" nachlesbar, aber nicht in Betrieb.
# Der Client fragt die Werte stattdessen im Takt ab.
esphome:
name: vanalign
friendly_name: "VanAlign Pro"
on_boot:
priority: -100
then:
- script.execute: publish_offsets
esp32:
board: esp32-s3-devkitc-1
framework:
type: esp-idf
logger:
level: WARN
i2c:
sda: GPIO8
scl: GPIO9
scan: true
globals:
- id: pitch_offset
type: float
restore_value: yes
initial_value: '0.0'
- id: roll_offset
type: float
restore_value: yes
initial_value: '0.0'
espnow:
channel: 1
sensor:
- platform: mpu6050
#address: 0x69
gyro_x:
name: "MPU6050 Gyro X-Achse"
id: mpu_gyro_x
gyro_y:
name: "MPU6050 Gyro Y-Achse"
id: mpu_gyro_y
gyro_z:
name: "MPU6050 Gyro Z-Achse"
id: mpu_gyro_z
accel_x:
name: "MPU6050 Accel X"
id: accel_x
internal: true
accel_y:
name: "MPU6050 Accel Y"
id: accel_y
internal: true
accel_z:
name: "MPU6050 Accel Z"
id: accel_z
internal: true
gyro_x:
id: mpu_gyro_x
internal: true
gyro_y:
id: mpu_gyro_y
internal: true
gyro_z:
id: mpu_gyro_z
internal: true
update_interval: 0.1s
- platform: template
name: "Neigung Pitch"
id: pitch
@@ -80,26 +67,14 @@ sensor:
if (isnan(id(accel_x).state) || isnan(id(accel_y).state) || isnan(id(accel_z).state)) {
return NAN;
}
float raw = atan2(id(accel_y).state,
sqrt(pow(id(accel_x).state, 2) + pow(id(accel_z).state, 2)))
* (180.0 / 3.14159265);
return raw - id(pitch_offset);
float raw = atan2(id(accel_y).state, sqrt(pow(id(accel_x).state, 2) + pow(id(accel_z).state, 2))) * (180.0 / 3.14159265);
return raw - id(pitch_offset); // Offset wird hier subtrahiert
filters:
- sliding_window_moving_average:
window_size: 8
send_every: 1
- exponential_moving_average:
alpha: 0.2
on_value:
# Ohne dieses Senden müsste der Client die Werte abfragen; die Anzeige
# ruckelt dann im Takt der Abfrage statt der Messung.
- ble_server.characteristic.set_value:
id: pitch_ble
value: !lambda |-
std::vector<uint8_t> v(sizeof(float));
float val = x;
memcpy(v.data(), &val, sizeof(float));
return v;
- platform: template
name: "Neigung Roll"
@@ -113,63 +88,29 @@ sensor:
return NAN;
}
float raw = atan2(-id(accel_x).state, id(accel_z).state) * (180.0 / 3.14159265);
return raw - id(roll_offset);
return raw - id(roll_offset); // Offset wird hier subtrahiert
filters:
- sliding_window_moving_average:
window_size: 8
send_every: 1
- exponential_moving_average:
alpha: 0.2
on_value:
- ble_server.characteristic.set_value:
id: roll_ble
value: !lambda |-
std::vector<uint8_t> v(sizeof(float));
float val = x;
memcpy(v.data(), &val, sizeof(float));
return v;
script:
# Die aktuelle Lage wird zur neuen Null. Beide Wege Knopf und Bluetooth
# laufen hier zusammen, damit sie nicht auseinanderdriften.
- id: calibrate_level
then:
- lambda: |-
if (isnan(id(accel_x).state) || isnan(id(accel_y).state) || isnan(id(accel_z).state)) {
ESP_LOGW("vanalign", "Kalibrierung abgebrochen: keine Sensorwerte");
return;
}
id(pitch_offset) = atan2(id(accel_y).state,
sqrt(pow(id(accel_x).state, 2) + pow(id(accel_z).state, 2)))
* (180.0 / 3.14159265);
id(roll_offset) = atan2(-id(accel_x).state, id(accel_z).state) * (180.0 / 3.14159265);
ESP_LOGI("vanalign", "Kalibriert: pitch_offset=%.2f roll_offset=%.2f",
id(pitch_offset), id(roll_offset));
- script.execute: publish_offsets
- id: reset_calibration
then:
- lambda: |-
id(pitch_offset) = 0.0f;
id(roll_offset) = 0.0f;
ESP_LOGI("vanalign", "Kalibrierung zurückgesetzt");
- script.execute: publish_offsets
# Die gespeicherten Offsets für lesende Clients bereitstellen.
- id: publish_offsets
then:
- ble_server.characteristic.set_value:
id: offsets_ble
value: !lambda |-
std::vector<uint8_t> v(2 * sizeof(float));
float values[2] = { id(pitch_offset), id(roll_offset) };
memcpy(v.data(), values, sizeof(values));
return v;
globals:
- id: pitch_offset
type: float
restore_value: yes
initial_value: '0.0'
- id: roll_offset
type: float
restore_value: yes
initial_value: '0.0'
- id: enable_captive
type: bool
restore_value: yes
initial_value: 'false'
esp32_ble_server:
manufacturer: "VanAlign"
model: "Pro"
services:
- uuid: 2a24b789-7aab-4535-af3e-ee76a35cc42d
advertise: true
@@ -178,24 +119,20 @@ esp32_ble_server:
uuid: cad48e28-7fbe-41cf-bae9-d77a6c233424
description: "Pitch"
read: true
notify: true
value: !lambda |-
std::vector<unsigned char> v(sizeof(float));
float val = id(pitch).state;
memcpy(v.data(), &val, sizeof(float));
return v;
- id: roll_ble
uuid: cad48e28-7fbe-41cf-bae9-d77a6c233425
description: "Roll"
read: true
notify: true
# Zwei Floats: die gespeicherten Offsets. Damit kann ein Client
# anzeigen, ob und worauf kalibriert wurde.
- id: offsets_ble
uuid: cad48e28-7fbe-41cf-bae9-d77a6c233426
description: "Kalibrier-Offsets"
read: true
notify: true
# Ein Byte: 0 setzt die Kalibrierung zurück, alles andere kalibriert
# auf die aktuelle Lage.
value: !lambda |-
std::vector<unsigned char> v(sizeof(float));
float val = id(roll).state;
memcpy(v.data(), &val, sizeof(float));
return v;
- id: calib_ble
uuid: cad48e28-7fbe-41cf-bae9-d77a6c233427
description: "Kalibriere Neigung"
@@ -210,6 +147,47 @@ esp32_ble_server:
id(calibrate_level).execute();
}
i2c:
sda: GPIO8
scl: GPIO9
scan: true
#web_server:
# port: 80
#ota:
# platform: web_server
#wifi:
# ap:
# ssid: "VanAlign-Setup"
# password: "kalibrierung"
script:
# Die aktuelle Lage wird zur neuen Null. Knopf und Bluetooth laufen hier
# zusammen, damit sie nicht auseinanderdriften.
- id: calibrate_level
then:
- lambda: |-
if (isnan(id(accel_x).state) || isnan(id(accel_y).state) || isnan(id(accel_z).state)) {
ESP_LOGW("vanalign", "Kalibrierung abgebrochen: keine Sensorwerte");
return;
}
id(pitch_offset) = atan2(id(accel_y).state, sqrt(pow(id(accel_x).state, 2) + pow(id(accel_z).state, 2))) * (180.0 / 3.14159265);
id(roll_offset) = atan2(-id(accel_x).state, id(accel_z).state) * (180.0 / 3.14159265);
ESP_LOGI("vanalign", "Kalibriert: pitch_offset=%.2f roll_offset=%.2f", id(pitch_offset), id(roll_offset));
- id: reset_calibration
then:
- lambda: |-
id(pitch_offset) = 0.0f;
id(roll_offset) = 0.0f;
ESP_LOGI("vanalign", "Kalibrierung zurückgesetzt");
button:
- platform: template
name: "Kalibriere Neigung"
@@ -222,7 +200,6 @@ button:
id: calib_reset_button
on_press:
- script.execute: reset_calibration
- platform: restart
name: "ESP Restart"
@@ -232,4 +209,4 @@ text_sensor:
id: firmware_version
icon: mdi:tag
lambda: |-
return {"v1.1.0"};
return {"v1.0.2"};