Firmware des Neigungsmessers ins Projekt übernehmen
Die ESPHome-Firmware lag bisher im eigenständigen Projekt VanAllign-Pro. Da die App der einzige Client ist, gehören Firmware und App zusammen: ändert sich die Bluetooth-Schnittstelle, muss beides gemeinsam angefasst werden. Übernommen sind die Firmware selbst und die beiden Experimente zu WLAN/BLE-Umschaltung und ESP-NOW, letztere nach experimente/ einsortiert und als nicht betriebsnotwendig gekennzeichnet. Die WebBLE-Oberfläche bleibt im Ursprungsprojekt - sie ist durch die App abgelöst und bringt 2 MB Bilder mit, die in einem iOS-Projekt nichts zu suchen haben. Die .gitignore schliesst jetzt die ESPHome-Build-Verzeichnisse aus; im Ursprungsprojekt fehlte das, dort liegen inzwischen 233 MB Bauartefakte neben den Quellen. Am neuen Ort mit esphome config gegengeprüft. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -8,6 +8,7 @@
|
||||
# Custom folder icons
|
||||
Icon
|
||||
|
||||
|
||||
# Volume root files
|
||||
.DocumentRevisions-V100
|
||||
.fseventsd
|
||||
@@ -63,3 +64,8 @@ playground.xcworkspace
|
||||
|
||||
# Swift Package Manager
|
||||
# Package.resolved
|
||||
### ESPHome
|
||||
# Build-Verzeichnis und Zustandsdaten; das Abbild wird aus der YAML erzeugt.
|
||||
.esphome/
|
||||
firmware/**/.esphome/
|
||||
firmware/**/.pioenvs/
|
||||
|
||||
@@ -12,6 +12,9 @@ iOS-App, die per Bluetooth LE die Energieanlage im Wohnmobil ausliest:
|
||||
|
||||
Ein Victron SmartShunt/BMV wird ebenfalls unterstützt, falls später einer dazukommt.
|
||||
|
||||
Die Firmware des Neigungsmessers liegt mit im Projekt, siehe
|
||||
[firmware/vanalign](firmware/vanalign/README.md).
|
||||
|
||||
## Bauen und installieren
|
||||
|
||||
```bash
|
||||
@@ -170,6 +173,10 @@ und die Prüfsummen beider Daly-Dialekte.
|
||||
## Aufbau
|
||||
|
||||
```
|
||||
firmware/vanalign/ Firmware des Neigungsmessers (ESPHome)
|
||||
├── esp32_ble.yaml Neigungsmessung und Bluetooth-Schnittstelle
|
||||
└── experimente/ Nicht für den Betrieb nötig
|
||||
|
||||
CamperMonitor/
|
||||
├── Models/
|
||||
│ ├── Profile.swift Fahrzeug
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
# VanAlign Pro – Neigungsmessung für den Camper
|
||||
|
||||
ESP32-S3 mit MPU6050, der Längs- und Querneigung des Fahrzeugs misst und über
|
||||
Bluetooth bereitstellt. Zum Ausrichten beim Parken: die Anzeige zeigt, welche
|
||||
Seite höher steht und wie weit es noch ist.
|
||||
|
||||
> Teile dieses Projekts wurden mit KI-Unterstützung erstellt.
|
||||
|
||||
## Firmware
|
||||
|
||||
`esp32_ble.yaml` – die einzige Datei, die für den Betrieb gebraucht wird.
|
||||
|
||||
```bash
|
||||
cd firmware/vanalign
|
||||
esphome run esp32_ble.yaml
|
||||
```
|
||||
|
||||
### Bluetooth-Schnittstelle
|
||||
|
||||
Dienst `2a24b789-7aab-4535-af3e-ee76a35cc42d` (wird beworben, das Gerät ist
|
||||
darüber auffindbar).
|
||||
|
||||
| Charakteristik | UUID (Ende) | Zugriff | Inhalt |
|
||||
|---|---|---|---|
|
||||
| Pitch | `…3424` | lesen, benachrichtigen | Längsneigung, Float32 little-endian, Grad |
|
||||
| Roll | `…3425` | lesen, benachrichtigen | 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 |
|
||||
|
||||
Positiver Pitch heißt: das Heck steht höher. Positiver Roll: die rechte Seite
|
||||
steht höher.
|
||||
|
||||
### Kalibrierung
|
||||
|
||||
Fahrzeug eben stellen, dann ein beliebiges Byte ungleich `0` auf `…3427`
|
||||
schreiben – oder in Camper Monitor auf *Auf aktuelle Lage kalibrieren* tippen.
|
||||
Die Offsets werden dauerhaft gespeichert und überstehen einen Neustart.
|
||||
|
||||
## Was sich gegenüber 1.0 geändert hat
|
||||
|
||||
* **Kalibrierung funktioniert jetzt überhaupt.** 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.
|
||||
* **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
|
||||
ein `"hallo"` an eine fest eingetragene MAC-Adresse.
|
||||
|
||||
Die UUIDs sind unverändert, die vorhandene WebApp läuft weiter.
|
||||
|
||||
## Clients
|
||||
|
||||
* **Camper Monitor** (iOS) – die App in diesem Projekt. Geräteart
|
||||
*Nivellierung*: grafische Libelle, Kalibrierung aus der App, zusammen mit
|
||||
den übrigen Geräten im Fahrzeug.
|
||||
* Die ursprüngliche WebBLE-Oberfläche liegt weiterhin im Projekt
|
||||
`VanAllign-Pro` unter `WebAPP/`.
|
||||
|
||||
## Weitere Dateien
|
||||
|
||||
Unter `experimente/` liegen `esp32_switch.yaml` (Umschalten zwischen WLAN und
|
||||
BLE) und `esp32_EspNow-rec.yaml` (ESP-NOW-Empfänger). Beide gehören nicht zum
|
||||
Neigungsmesser und werden für den Betrieb nicht gebraucht – sie sind nur
|
||||
aufgehoben, weil sie Ideen aus der Entstehungszeit festhalten.
|
||||
|
||||
## Herkunft
|
||||
|
||||
Übernommen aus dem eigenständigen Projekt `VanAllign-Pro`. Dort liegen
|
||||
weiterhin die WebBLE-Oberfläche (`WebAPP/`, durch die iOS-App abgelöst) und
|
||||
unter `bin/` die gebauten Binärdateien der Version 1.0.
|
||||
|
||||
## Voraussetzungen
|
||||
|
||||
* ESP32-S3, MPU6050 an I²C (SDA GPIO8, SCL GPIO9)
|
||||
* ESPHome (geprüft mit 2026.6.5)
|
||||
@@ -0,0 +1,235 @@
|
||||
# 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.
|
||||
#
|
||||
# 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.
|
||||
|
||||
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'
|
||||
|
||||
sensor:
|
||||
- platform: mpu6050
|
||||
#address: 0x69
|
||||
accel_x:
|
||||
id: accel_x
|
||||
internal: true
|
||||
accel_y:
|
||||
id: accel_y
|
||||
internal: true
|
||||
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
|
||||
icon: mdi:caravan
|
||||
unit_of_measurement: "°"
|
||||
accuracy_decimals: 1
|
||||
update_interval: 0.1s
|
||||
lambda: |-
|
||||
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);
|
||||
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"
|
||||
id: roll
|
||||
icon: mdi:axis-x-rotate-clockwise
|
||||
unit_of_measurement: "°"
|
||||
accuracy_decimals: 1
|
||||
update_interval: 0.1s
|
||||
lambda: |-
|
||||
if (isnan(id(accel_x).state) || isnan(id(accel_z).state)) {
|
||||
return NAN;
|
||||
}
|
||||
float raw = atan2(-id(accel_x).state, id(accel_z).state) * (180.0 / 3.14159265);
|
||||
return raw - id(roll_offset);
|
||||
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;
|
||||
|
||||
esp32_ble_server:
|
||||
manufacturer: "VanAlign"
|
||||
model: "Pro"
|
||||
services:
|
||||
- uuid: 2a24b789-7aab-4535-af3e-ee76a35cc42d
|
||||
advertise: true
|
||||
characteristics:
|
||||
- id: pitch_ble
|
||||
uuid: cad48e28-7fbe-41cf-bae9-d77a6c233424
|
||||
description: "Pitch"
|
||||
read: true
|
||||
notify: true
|
||||
|
||||
- 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.
|
||||
- id: calib_ble
|
||||
uuid: cad48e28-7fbe-41cf-bae9-d77a6c233427
|
||||
description: "Kalibriere Neigung"
|
||||
write: true
|
||||
on_write:
|
||||
then:
|
||||
- lambda: |-
|
||||
bool reset = !x.empty() && (x[0] == 0x00 || x[0] == '0');
|
||||
if (reset) {
|
||||
id(reset_calibration).execute();
|
||||
} else {
|
||||
id(calibrate_level).execute();
|
||||
}
|
||||
|
||||
button:
|
||||
- platform: template
|
||||
name: "Kalibriere Neigung"
|
||||
id: calib_button
|
||||
on_press:
|
||||
- script.execute: calibrate_level
|
||||
|
||||
- platform: template
|
||||
name: "Kalibrierung zurücksetzen"
|
||||
id: calib_reset_button
|
||||
on_press:
|
||||
- script.execute: reset_calibration
|
||||
|
||||
- platform: restart
|
||||
name: "ESP Restart"
|
||||
|
||||
text_sensor:
|
||||
- platform: template
|
||||
name: "Firmware Version"
|
||||
id: firmware_version
|
||||
icon: mdi:tag
|
||||
lambda: |-
|
||||
return {"v1.1.0"};
|
||||
@@ -0,0 +1,22 @@
|
||||
esphome:
|
||||
name: vanalign
|
||||
friendly_name: "VanAlign Pro"
|
||||
|
||||
|
||||
esp32:
|
||||
board: esp32-s3-devkitc-1
|
||||
|
||||
logger:
|
||||
level: WARN
|
||||
|
||||
espnow:
|
||||
channel: 1
|
||||
on_broadcast:
|
||||
- logger.log:
|
||||
format: "Sent to %s from %s: %s RSSI: %ddBm"
|
||||
args:
|
||||
- format_mac_address_pretty(info.des_addr).c_str()
|
||||
- format_mac_address_pretty(info.src_addr).c_str()
|
||||
- format_hex_pretty(data, size).c_str()
|
||||
- info.rx_ctrl->rssi
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
esphome:
|
||||
name: dual-net-webble
|
||||
|
||||
esp32:
|
||||
board: esp32-s3-devkitc-1
|
||||
framework:
|
||||
type: esp-idf
|
||||
|
||||
logger:
|
||||
baud_rate: 0
|
||||
level: WARN
|
||||
|
||||
wifi:
|
||||
ssid: "DEIN_SSID"
|
||||
password: "DEIN_PASSWORT"
|
||||
enable_on_boot: false
|
||||
|
||||
api:
|
||||
web_server:
|
||||
port: 80
|
||||
# ota: true <-- entfernen!
|
||||
|
||||
ota:
|
||||
- platform: esphome # klassisches OTA (benötigt 'api:')
|
||||
- platform: web_server # OTA im Web-Interface
|
||||
|
||||
|
||||
# BLE-Stack nur für eigenen GATT-Server
|
||||
esp32_ble:
|
||||
enable_on_boot: false
|
||||
|
||||
esp32_ble_server:
|
||||
manufacturer: "MyLab"
|
||||
model: "WebBLE"
|
||||
services:
|
||||
- uuid: "12345678-1234-5678-1234-56789abcdef0"
|
||||
characteristics:
|
||||
# Write "1" -> WLAN-Fenster starten/verlängern
|
||||
- uuid: "12345678-1234-5678-1234-56789abcdef1"
|
||||
name: "StartMaintenance"
|
||||
value: "0"
|
||||
on_write:
|
||||
then:
|
||||
- lambda: |-
|
||||
if (x.get_value().find("1") != std::string::npos) {
|
||||
id(start_wifi_window).execute();
|
||||
}
|
||||
# Status bei Bedarf lesbar (z. B. "WIFI,14" oder "BLE,0")
|
||||
- uuid: "12345678-1234-5678-1234-56789abcdef2"
|
||||
name: "Status"
|
||||
value: "BLE,0"
|
||||
on_read:
|
||||
then:
|
||||
- lambda: |-
|
||||
char buf[16];
|
||||
snprintf(buf, sizeof(buf), "%s,%d",
|
||||
id(_netmode_is_wifi) ? "WIFI" : "BLE",
|
||||
(int) id(_wifi_minutes_left));
|
||||
x.set_value(std::string(buf));
|
||||
|
||||
number:
|
||||
- platform: template
|
||||
id: wifi_window_minu
|
||||
Reference in New Issue
Block a user