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:
@@ -69,3 +69,11 @@ playground.xcworkspace
|
|||||||
.esphome/
|
.esphome/
|
||||||
firmware/**/.esphome/
|
firmware/**/.esphome/
|
||||||
firmware/**/.pioenvs/
|
firmware/**/.pioenvs/
|
||||||
|
|
||||||
|
### Python
|
||||||
|
# Virtuelle Umgebungen gehören nicht ins Repo – sie sind maschinenabhängig
|
||||||
|
# und umfassen hier über 14000 Dateien.
|
||||||
|
.venv/
|
||||||
|
venv/
|
||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
|||||||
@@ -61,12 +61,22 @@ struct LevelState: Equatable {
|
|||||||
|
|
||||||
var hasReading: Bool { pitch != nil || roll != nil }
|
var hasReading: Bool { pitch != nil || roll != nil }
|
||||||
|
|
||||||
/// Als kalibriert gilt, wo ein Offset hinterlegt ist.
|
/// Ob kalibriert wurde – oder nil, wenn das Gerät es nicht verrät.
|
||||||
var isCalibrated: Bool {
|
///
|
||||||
guard let pitchOffset, let rollOffset else { return false }
|
/// Nicht jede Firmware stellt die Offsets bereit. Fehlen sie, heisst das
|
||||||
|
/// „unbekannt“ und nicht „nicht kalibriert“: eine Warnung, die sich nie
|
||||||
|
/// abstellen lässt, ist schlimmer als keine.
|
||||||
|
var calibrationState: Bool? {
|
||||||
|
guard let pitchOffset, let rollOffset else { return nil }
|
||||||
return abs(pitchOffset) > 0.001 || abs(rollOffset) > 0.001
|
return abs(pitchOffset) > 0.001 || abs(rollOffset) > 0.001
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Nur wahr, wenn es auch belegt ist.
|
||||||
|
var isCalibrated: Bool { calibrationState == true }
|
||||||
|
|
||||||
|
/// Nur wahr, wenn das Gerät ausdrücklich Nulloffsets meldet.
|
||||||
|
var isKnownUncalibrated: Bool { calibrationState == false }
|
||||||
|
|
||||||
/// Bis hierhin gilt das Fahrzeug als eben genug.
|
/// Bis hierhin gilt das Fahrzeug als eben genug.
|
||||||
static let levelTolerance = 0.5
|
static let levelTolerance = 0.5
|
||||||
|
|
||||||
@@ -108,7 +118,7 @@ struct LevelState: Equatable {
|
|||||||
Metric("roll", "Querneigung", roll, unit: "°", precision: 1),
|
Metric("roll", "Querneigung", roll, unit: "°", precision: 1),
|
||||||
]
|
]
|
||||||
snapshot.state = instruction
|
snapshot.state = instruction
|
||||||
if !isCalibrated {
|
if isKnownUncalibrated {
|
||||||
snapshot.offReasons = ["Nicht kalibriert"]
|
snapshot.offReasons = ["Nicht kalibriert"]
|
||||||
}
|
}
|
||||||
return snapshot
|
return snapshot
|
||||||
|
|||||||
@@ -135,7 +135,7 @@ struct LevelControls: View {
|
|||||||
Button("Auf aktuelle Lage kalibrieren", systemImage: "scope") {
|
Button("Auf aktuelle Lage kalibrieren", systemImage: "scope") {
|
||||||
bluetooth.calibrateLevel(for: device.id)
|
bluetooth.calibrateLevel(for: device.id)
|
||||||
}
|
}
|
||||||
if state.isCalibrated {
|
if !state.isKnownUncalibrated {
|
||||||
Button("Kalibrierung zurücksetzen", systemImage: "arrow.uturn.backward", role: .destructive) {
|
Button("Kalibrierung zurücksetzen", systemImage: "arrow.uturn.backward", role: .destructive) {
|
||||||
showResetConfirmation = true
|
showResetConfirmation = true
|
||||||
}
|
}
|
||||||
@@ -148,9 +148,13 @@ struct LevelControls: View {
|
|||||||
Text(String(format: "Nullpunkt liegt bei %.1f° längs und %.1f° quer. "
|
Text(String(format: "Nullpunkt liegt bei %.1f° längs und %.1f° quer. "
|
||||||
+ "Zum Kalibrieren das Fahrzeug eben stellen und dann tippen.",
|
+ "Zum Kalibrieren das Fahrzeug eben stellen und dann tippen.",
|
||||||
pitchOffset, rollOffset))
|
pitchOffset, rollOffset))
|
||||||
} else {
|
} else if state.isKnownUncalibrated {
|
||||||
Text("Noch nicht kalibriert – die Anzeige zeigt die Lage des Sensors, "
|
Text("Noch nicht kalibriert – die Anzeige zeigt die Lage des Sensors, "
|
||||||
+ "nicht die des Fahrzeugs. Fahrzeug eben stellen und dann kalibrieren.")
|
+ "nicht die des Fahrzeugs. Fahrzeug eben stellen und dann kalibrieren.")
|
||||||
|
} else {
|
||||||
|
// Ältere Firmware gibt die Offsets nicht heraus.
|
||||||
|
Text("Zum Kalibrieren das Fahrzeug eben stellen und dann tippen. "
|
||||||
|
+ "Ob bereits kalibriert wurde, meldet dieses Gerät nicht zurück.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.confirmationDialog("Kalibrierung zurücksetzen?",
|
.confirmationDialog("Kalibrierung zurücksetzen?",
|
||||||
|
|||||||
@@ -141,9 +141,14 @@ Seite höher steht.
|
|||||||
aktuelle Lage kalibrieren*. Ohne das zeigt die Anzeige die Lage des Sensors,
|
aktuelle Lage kalibrieren*. Ohne das zeigt die Anzeige die Lage des Sensors,
|
||||||
nicht die des Fahrzeugs – je nachdem, wie schief er eingebaut ist.
|
nicht die des Fahrzeugs – je nachdem, wie schief er eingebaut ist.
|
||||||
|
|
||||||
Die App kommt auch mit der alten Firmware 1.0 zurecht: bietet das Gerät kein
|
Die Werte werden zweimal je Sekunde abgefragt. Bietet ein Gerät `notify` an,
|
||||||
`notify` an, werden die Werte im Takt abgefragt. Kalibrieren aus der App
|
werden sie stattdessen abonniert – die mitgelieferte Firmware tut das
|
||||||
braucht allerdings Firmware 1.1.
|
allerdings nicht, siehe deren README.
|
||||||
|
|
||||||
|
Ob bereits kalibriert wurde, meldet nicht jede Firmware zurück. Fehlt die
|
||||||
|
Angabe, gilt sie als unbekannt und die App warnt nicht – eine Warnung, die
|
||||||
|
sich nie abstellen lässt, wäre schlimmer als keine. Kalibrieren aus der App
|
||||||
|
braucht Firmware 1.0.2.
|
||||||
|
|
||||||
## Mehrere Fahrzeuge
|
## Mehrere Fahrzeuge
|
||||||
|
|
||||||
|
|||||||
+11
-3
@@ -648,9 +648,14 @@ checkEqual("beide Achsen in der Ansage", level.instruction,
|
|||||||
"Front steht höher, links steht höher")
|
"Front steht höher, links steht höher")
|
||||||
checkEqual("grösste Abweichung über beide Achsen", level.largestDeviation.map(round2), 2.0)
|
checkEqual("grösste Abweichung über beide Achsen", level.largestDeviation.map(round2), 2.0)
|
||||||
|
|
||||||
checkEqual("ohne Offsets gilt das Gerät als unkalibriert", level.isCalibrated, false)
|
// Fehlende Offsets heissen "unbekannt", nicht "nicht kalibriert" - sonst
|
||||||
|
// stünde bei Firmware, die sie nicht meldet, dauerhaft eine Warnung, die sich
|
||||||
|
// nicht abstellen lässt.
|
||||||
|
checkEqual("ohne Offsets ist der Zustand unbekannt", level.calibrationState == nil, true)
|
||||||
|
checkEqual("unbekannt gilt nicht als kalibriert", level.isCalibrated, false)
|
||||||
|
checkEqual("unbekannt gilt aber auch nicht als unkalibriert", level.isKnownUncalibrated, false)
|
||||||
level.pitchOffset = 0; level.rollOffset = 0
|
level.pitchOffset = 0; level.rollOffset = 0
|
||||||
checkEqual("Nulloffsets sind keine Kalibrierung", level.isCalibrated, false)
|
checkEqual("gemeldete Nulloffsets sind belegt unkalibriert", level.isKnownUncalibrated, true)
|
||||||
level.pitchOffset = 1.2
|
level.pitchOffset = 1.2
|
||||||
checkEqual("ein gesetzter Offset genügt", level.isCalibrated, true)
|
checkEqual("ein gesetzter Offset genügt", level.isCalibrated, true)
|
||||||
|
|
||||||
@@ -660,7 +665,10 @@ checkEqual("Messwerte beider Achsen", levelSnapshot.metrics.count, 2)
|
|||||||
|
|
||||||
var uncalibrated = LevelState()
|
var uncalibrated = LevelState()
|
||||||
uncalibrated.pitch = 0; uncalibrated.roll = 0
|
uncalibrated.pitch = 0; uncalibrated.roll = 0
|
||||||
checkEqual("fehlende Kalibrierung wird gemeldet",
|
checkEqual("ohne Offset-Meldung keine Warnung",
|
||||||
|
uncalibrated.snapshot(deviceID: UUID(), rssi: nil).offReasons.isEmpty, true)
|
||||||
|
uncalibrated.pitchOffset = 0; uncalibrated.rollOffset = 0
|
||||||
|
checkEqual("belegt fehlende Kalibrierung wird gemeldet",
|
||||||
uncalibrated.snapshot(deviceID: UUID(), rssi: nil).offReasons.first,
|
uncalibrated.snapshot(deviceID: UUID(), rssi: nil).offReasons.first,
|
||||||
"Nicht kalibriert")
|
"Nicht kalibriert")
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
# Gitignore settings for ESPHome
|
||||||
|
# This is an example and may include too much for your use-case.
|
||||||
|
# You can modify this file to suit your needs.
|
||||||
|
/.esphome/
|
||||||
|
/secrets.yaml
|
||||||
@@ -22,11 +22,13 @@ darüber auffindbar).
|
|||||||
|
|
||||||
| Charakteristik | UUID (Ende) | Zugriff | Inhalt |
|
| Charakteristik | UUID (Ende) | Zugriff | Inhalt |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| Pitch | `…3424` | lesen, benachrichtigen | Längsneigung, Float32 little-endian, Grad |
|
| Pitch | `…3424` | lesen | Längsneigung, Float32 little-endian, Grad |
|
||||||
| Roll | `…3425` | lesen, benachrichtigen | Querneigung, Float32 little-endian, Grad |
|
| Roll | `…3425` | lesen | Querneigung, Float32 little-endian, Grad |
|
||||||
| Offsets | `…3426` | lesen, benachrichtigen | zwei Float32: die gespeicherten Kalibrierwerte |
|
|
||||||
| Kalibrieren | `…3427` | schreiben | ein Byte: `0` setzt zurück, alles andere kalibriert |
|
| Kalibrieren | `…3427` | schreiben | ein Byte: `0` setzt zurück, alles andere kalibriert |
|
||||||
|
|
||||||
|
Der Client fragt Pitch und Roll im Takt ab; Camper Monitor tut das zweimal je
|
||||||
|
Sekunde.
|
||||||
|
|
||||||
Positiver Pitch heißt: das Heck steht höher. Positiver Roll: die rechte Seite
|
Positiver Pitch heißt: das Heck steht höher. Positiver Roll: die rechte Seite
|
||||||
steht höher.
|
steht höher.
|
||||||
|
|
||||||
@@ -38,18 +40,32 @@ Die Offsets werden dauerhaft gespeichert und überstehen einen Neustart.
|
|||||||
|
|
||||||
## Was sich gegenüber 1.0 geändert hat
|
## Was sich gegenüber 1.0 geändert hat
|
||||||
|
|
||||||
|
Bewusst wenig – siehe unten.
|
||||||
|
|
||||||
* **Kalibrierung funktioniert jetzt überhaupt.** Die Charakteristik `…3427` war
|
* **Kalibrierung funktioniert jetzt überhaupt.** Die Charakteristik `…3427` war
|
||||||
zwar beschreibbar, hatte aber keine Aktion hinterlegt und tat nichts. Der
|
zwar beschreibbar, hatte aber keine Aktion hinterlegt und tat nichts. Der
|
||||||
Kalibrier-Knopf war nur über den Webserver erreichbar, der auskommentiert
|
Kalibrier-Knopf war nur über den Webserver erreichbar, der auskommentiert
|
||||||
ist – kalibrieren war damit auf keinem Weg möglich.
|
ist – kalibrieren war damit auf keinem Weg möglich.
|
||||||
* **Die Werte werden gesendet, statt abgefragt.** Pitch und Roll haben jetzt
|
|
||||||
`notify`. Vorher musste der Client im Sekundentakt lesen, was für eine
|
|
||||||
Wasserwaage zu träge ist.
|
|
||||||
* **Die gespeicherten Offsets sind auslesbar**, sodass ein Client zeigen kann,
|
|
||||||
ob überhaupt schon kalibriert wurde.
|
|
||||||
* **Der ESP-NOW-Rest ist entfernt.** Er schickte bei jedem einzelnen Messwert
|
* **Der ESP-NOW-Rest ist entfernt.** Er schickte bei jedem einzelnen Messwert
|
||||||
ein `"hallo"` an eine fest eingetragene MAC-Adresse.
|
ein `"hallo"` an eine fest eingetragene MAC-Adresse.
|
||||||
|
|
||||||
|
## Was bewusst nicht geändert wurde
|
||||||
|
|
||||||
|
Eine weitergehende Fassung hatte `notify` auf Pitch und Roll, setzte die Werte
|
||||||
|
aktiv bei jeder Messung und stellte die Kalibrier-Offsets zum Auslesen bereit.
|
||||||
|
Sie liess sich mit `esphome config` prüfen und fehlerfrei übersetzen – auf dem
|
||||||
|
Gerät blieb sie danach jedoch stumm, ohne jede Ausgabe über die serielle
|
||||||
|
Schnittstelle.
|
||||||
|
|
||||||
|
Woran genau es lag, ist offen. Verdächtig sind das Setzen der Werte aus dem
|
||||||
|
`on_boot`-Ablauf heraus, bevor der BLE-Server bereit ist, sowie zwei
|
||||||
|
Benachrichtigungen zehnmal je Sekunde. Weil sich das nur am Gerät klären lässt,
|
||||||
|
bleibt es beim bewährten Aufbau: Pitch und Roll sind reine Lesewerte mit
|
||||||
|
hinterlegtem Ausdruck, der Client fragt ab.
|
||||||
|
|
||||||
|
Die verworfene Fassung ist in der Projektgeschichte unter *Neigungsmesser
|
||||||
|
VanAlign einbinden* nachlesbar, falls jemand daran weiterarbeiten will.
|
||||||
|
|
||||||
Die UUIDs sind unverändert, die vorhandene WebApp läuft weiter.
|
Die UUIDs sind unverändert, die vorhandene WebApp läuft weiter.
|
||||||
|
|
||||||
## Clients
|
## Clients
|
||||||
|
|||||||
@@ -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
|
# Bewusst nah am bewährten Stand 1.0 gehalten. Gegenüber diesem geändert:
|
||||||
# (little-endian) und lässt sich über Bluetooth auf die aktuelle Lage
|
|
||||||
# kalibrieren.
|
|
||||||
#
|
#
|
||||||
# Die UUIDs sind gegenüber Version 1.0 unverändert, damit die vorhandene
|
# * Die Kalibrierung funktioniert. Die Charakteristik ...3427 war zwar
|
||||||
# WebApp weiterläuft. Neu sind:
|
# beschreibbar, hatte aber keine Aktion hinterlegt und tat nichts; der
|
||||||
# * notify auf Pitch und Roll – die Werte werden gesendet, sobald sie sich
|
# Kalibrier-Knopf war nur über den Webserver erreichbar, der
|
||||||
# ändern, statt vom Client im Sekundentakt abgefragt zu werden.
|
# auskommentiert ist. Kalibrieren war damit auf keinem Weg möglich.
|
||||||
# * eine funktionierende Kalibrierung über Bluetooth. Bisher war die
|
# * Der ESP-NOW-Rest ist entfernt. Er schickte bei jedem einzelnen
|
||||||
# Charakteristik zwar beschreibbar, hatte aber keine Aktion hinterlegt,
|
# Messwert ein "hallo" an eine fest eingetragene MAC-Adresse.
|
||||||
# und der Kalibrier-Knopf war nur über den Webserver erreichbar, der
|
#
|
||||||
# auskommentiert ist. Kalibrieren war damit gar nicht möglich.
|
# Bewusst NICHT geändert: Pitch und Roll bleiben reine Lesewerte mit
|
||||||
# * die gespeicherten Offsets sind auslesbar, sodass ein Client zeigen
|
# hinterlegtem Ausdruck. Eine Fassung mit notify und aktiv gesetzten Werten
|
||||||
# kann, ob überhaupt schon kalibriert wurde.
|
# 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:
|
esphome:
|
||||||
name: vanalign
|
name: vanalign
|
||||||
friendly_name: "VanAlign Pro"
|
friendly_name: "VanAlign Pro"
|
||||||
on_boot:
|
|
||||||
priority: -100
|
|
||||||
then:
|
|
||||||
- script.execute: publish_offsets
|
|
||||||
|
|
||||||
esp32:
|
esp32:
|
||||||
board: esp32-s3-devkitc-1
|
board: esp32-s3-devkitc-1
|
||||||
framework:
|
framework:
|
||||||
type: esp-idf
|
type: esp-idf
|
||||||
|
|
||||||
|
|
||||||
logger:
|
logger:
|
||||||
level: WARN
|
level: WARN
|
||||||
|
|
||||||
i2c:
|
espnow:
|
||||||
sda: GPIO8
|
channel: 1
|
||||||
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'
|
|
||||||
|
|
||||||
sensor:
|
sensor:
|
||||||
- platform: mpu6050
|
- platform: mpu6050
|
||||||
#address: 0x69
|
#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:
|
accel_x:
|
||||||
|
name: "MPU6050 Accel X"
|
||||||
id: accel_x
|
id: accel_x
|
||||||
internal: true
|
internal: true
|
||||||
accel_y:
|
accel_y:
|
||||||
|
name: "MPU6050 Accel Y"
|
||||||
id: accel_y
|
id: accel_y
|
||||||
internal: true
|
internal: true
|
||||||
accel_z:
|
accel_z:
|
||||||
|
name: "MPU6050 Accel Z"
|
||||||
id: accel_z
|
id: accel_z
|
||||||
internal: true
|
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
|
update_interval: 0.1s
|
||||||
|
|
||||||
- platform: template
|
- platform: template
|
||||||
name: "Neigung Pitch"
|
name: "Neigung Pitch"
|
||||||
id: pitch
|
id: pitch
|
||||||
@@ -80,26 +67,14 @@ sensor:
|
|||||||
if (isnan(id(accel_x).state) || isnan(id(accel_y).state) || isnan(id(accel_z).state)) {
|
if (isnan(id(accel_x).state) || isnan(id(accel_y).state) || isnan(id(accel_z).state)) {
|
||||||
return NAN;
|
return NAN;
|
||||||
}
|
}
|
||||||
float raw = atan2(id(accel_y).state,
|
float raw = atan2(id(accel_y).state, sqrt(pow(id(accel_x).state, 2) + pow(id(accel_z).state, 2))) * (180.0 / 3.14159265);
|
||||||
sqrt(pow(id(accel_x).state, 2) + pow(id(accel_z).state, 2)))
|
return raw - id(pitch_offset); // Offset wird hier subtrahiert
|
||||||
* (180.0 / 3.14159265);
|
|
||||||
return raw - id(pitch_offset);
|
|
||||||
filters:
|
filters:
|
||||||
- sliding_window_moving_average:
|
- sliding_window_moving_average:
|
||||||
window_size: 8
|
window_size: 8
|
||||||
send_every: 1
|
send_every: 1
|
||||||
- exponential_moving_average:
|
- exponential_moving_average:
|
||||||
alpha: 0.2
|
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
|
- platform: template
|
||||||
name: "Neigung Roll"
|
name: "Neigung Roll"
|
||||||
@@ -113,63 +88,29 @@ sensor:
|
|||||||
return NAN;
|
return NAN;
|
||||||
}
|
}
|
||||||
float raw = atan2(-id(accel_x).state, id(accel_z).state) * (180.0 / 3.14159265);
|
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:
|
filters:
|
||||||
- sliding_window_moving_average:
|
- sliding_window_moving_average:
|
||||||
window_size: 8
|
window_size: 8
|
||||||
send_every: 1
|
send_every: 1
|
||||||
- exponential_moving_average:
|
- exponential_moving_average:
|
||||||
alpha: 0.2
|
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:
|
globals:
|
||||||
# Die aktuelle Lage wird zur neuen Null. Beide Wege – Knopf und Bluetooth –
|
- id: pitch_offset
|
||||||
# laufen hier zusammen, damit sie nicht auseinanderdriften.
|
type: float
|
||||||
- id: calibrate_level
|
restore_value: yes
|
||||||
then:
|
initial_value: '0.0'
|
||||||
- lambda: |-
|
- id: roll_offset
|
||||||
if (isnan(id(accel_x).state) || isnan(id(accel_y).state) || isnan(id(accel_z).state)) {
|
type: float
|
||||||
ESP_LOGW("vanalign", "Kalibrierung abgebrochen: keine Sensorwerte");
|
restore_value: yes
|
||||||
return;
|
initial_value: '0.0'
|
||||||
}
|
- id: enable_captive
|
||||||
id(pitch_offset) = atan2(id(accel_y).state,
|
type: bool
|
||||||
sqrt(pow(id(accel_x).state, 2) + pow(id(accel_z).state, 2)))
|
restore_value: yes
|
||||||
* (180.0 / 3.14159265);
|
initial_value: 'false'
|
||||||
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;
|
|
||||||
|
|
||||||
esp32_ble_server:
|
esp32_ble_server:
|
||||||
manufacturer: "VanAlign"
|
|
||||||
model: "Pro"
|
|
||||||
services:
|
services:
|
||||||
- uuid: 2a24b789-7aab-4535-af3e-ee76a35cc42d
|
- uuid: 2a24b789-7aab-4535-af3e-ee76a35cc42d
|
||||||
advertise: true
|
advertise: true
|
||||||
@@ -178,24 +119,20 @@ esp32_ble_server:
|
|||||||
uuid: cad48e28-7fbe-41cf-bae9-d77a6c233424
|
uuid: cad48e28-7fbe-41cf-bae9-d77a6c233424
|
||||||
description: "Pitch"
|
description: "Pitch"
|
||||||
read: true
|
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
|
- id: roll_ble
|
||||||
uuid: cad48e28-7fbe-41cf-bae9-d77a6c233425
|
uuid: cad48e28-7fbe-41cf-bae9-d77a6c233425
|
||||||
description: "Roll"
|
description: "Roll"
|
||||||
read: true
|
read: true
|
||||||
notify: true
|
value: !lambda |-
|
||||||
|
std::vector<unsigned char> v(sizeof(float));
|
||||||
# Zwei Floats: die gespeicherten Offsets. Damit kann ein Client
|
float val = id(roll).state;
|
||||||
# anzeigen, ob und worauf kalibriert wurde.
|
memcpy(v.data(), &val, sizeof(float));
|
||||||
- id: offsets_ble
|
return v;
|
||||||
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.
|
|
||||||
- id: calib_ble
|
- id: calib_ble
|
||||||
uuid: cad48e28-7fbe-41cf-bae9-d77a6c233427
|
uuid: cad48e28-7fbe-41cf-bae9-d77a6c233427
|
||||||
description: "Kalibriere Neigung"
|
description: "Kalibriere Neigung"
|
||||||
@@ -210,6 +147,47 @@ esp32_ble_server:
|
|||||||
id(calibrate_level).execute();
|
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:
|
button:
|
||||||
- platform: template
|
- platform: template
|
||||||
name: "Kalibriere Neigung"
|
name: "Kalibriere Neigung"
|
||||||
@@ -222,7 +200,6 @@ button:
|
|||||||
id: calib_reset_button
|
id: calib_reset_button
|
||||||
on_press:
|
on_press:
|
||||||
- script.execute: reset_calibration
|
- script.execute: reset_calibration
|
||||||
|
|
||||||
- platform: restart
|
- platform: restart
|
||||||
name: "ESP Restart"
|
name: "ESP Restart"
|
||||||
|
|
||||||
@@ -232,4 +209,4 @@ text_sensor:
|
|||||||
id: firmware_version
|
id: firmware_version
|
||||||
icon: mdi:tag
|
icon: mdi:tag
|
||||||
lambda: |-
|
lambda: |-
|
||||||
return {"v1.1.0"};
|
return {"v1.0.2"};
|
||||||
|
|||||||
Reference in New Issue
Block a user