Helligkeit: zwei Werte und eine Schwelle, zur Laufzeit aenderbar

Die vier abgestuften Helligkeiten des alten MSP430-Programms (5/15/50/100 %)
entfallen. Fuer die Sternwarte genuegt der Wechsel Tag/Nacht: unterhalb der
Schwelle laeuft die Anzeige mit dunkel_prozent, oberhalb mit hell_prozent.

Die Werte stehen nicht mehr im Code, sondern in settings.json auf dem Geraet --
Vorbereitung fuer die spaetere MQTT-Anbindung:

    settings.load()                        # mit Vorgaben aufgefuellt
    settings.update({"hell_prozent": 60})  # aendern und speichern

Vorgaben: dunkel 5 %, hell 50 %, Schwelle 1800, Hysterese 150. Die Schwelle liegt
mit Abstand zwischen den gemessenen Lagen (abgedeckt 0, Raumlicht 2453).

Beibehalten, weil die Messung vom 27.07. sie verlangt: der gleitende Mittelwert
und eine Hysterese um die Schwelle. Der ADC rauscht mit +/-32 (nach Mittelung,
1 Sigma) -- ohne Hysterese wuerde die Anzeige am Umschaltpunkt springen.

settings.py prueft jeden Wert beim Laden UND beim Schreiben auf Typ und Bereich.
Das ist der eigentliche Zweck: Mit MQTT kommen die Werte von aussen, und ein
Helligkeitswert von 500 % oder eine Schwelle von -1 wuerde die Anzeige sonst
unbrauchbar machen. Eine fehlende oder beschaedigte Datei faellt auf die Vorgaben
zurueck, ein einzelner kaputter Wert verwirft nur diesen -- ein Geraet in der
Sternwarte soll sich nicht an seiner Konfigurationsdatei aufhaengen.

settings.json wird von deploy.sh bewusst NICHT mitkopiert und steht in
.gitignore: Es ist Laufzeitzustand, kein Quellcode. Sonst waeren die
eingestellten Werte nach jedem Deployen weg.

Am Geraet bestaetigt: schreiben, wiederlesen, Controller uebernimmt die Werte,
beide Helligkeiten auf der Anzeige sichtbar. 119 Tests gruen.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-27 16:34:47 +02:00
parent e8a5b5d5e3
commit 0a893d0684
9 changed files with 549 additions and 210 deletions
+71 -43
View File
@@ -25,9 +25,9 @@ python3 run_display.py --mock --move --ldr 2000 # zusätzlich mit Helligkeitsre
python3 mount_mock.py --move # Mock separat starten
# Helligkeit aus einem LDR-Wert setzen:
python3 brightness.py --ldr 1500 # LDR-Rohwert -> Stufe/Prozent -> senden
python3 brightness.py --ldr 1500 # LDR-Rohwert -> Helligkeit -> senden
python3 -m unittest test_migra test_lx200 test_brightness test_display test_esp32
python3 -m unittest discover -p 'test_*.py' # alle Tests
python3 probe.py ruler # Geometrie der Anzeige ausmessen
```
@@ -56,9 +56,10 @@ beiden Startprogrammen.
| `ticks.py` | Timeouts, die unter CPython und MicroPython gleich funktionieren. | ✓ |
| `display.py` | Treiber: Telegramm senden, Antwort auswerten. | ✓ |
| `coords.py` | Koordinaten einlesen und auf die Zeilenbreite formatieren. | ✓ |
| `config.py` | Port/Pins, Adresse, Geometrie, Helligkeit, WLAN — alles an einer Stelle. | ✓ |
| `config.py` | Port/Pins, Adresse, Geometrie, WLAN — fest, ändert sich nur beim Deployen. | ✓ |
| `settings.py` | Zur Laufzeit änderbare Werte (Helligkeiten, Schwelle) in `settings.json`. | ✓ |
| `displayloop.py` | Die Poll-Schleife: abfragen, formatieren, anzeigen, Fehler abfangen. | ✓ |
| `brightness.py` | Helligkeitsregelung: LDR-Wert → Stufe/Prozent. | ✓ |
| `brightness.py` | Helligkeit: LDR-Wert → dunkel/hell, Schwelle aus `settings.json`. | ✓ |
| `mount_client.py` | TCP-Client zur GM4000 (LX200). | ✓ |
| `lx200.py` | LX200-Kommandorahmen. | ✓ |
| `wifi.py` | WLAN-Anbindung des ESP32 (verbinden, Verbindung halten). | ✓ |
@@ -69,7 +70,7 @@ beiden Startprogrammen.
| `mount_mock.py` | Simulator der Montierung (LX200) fürs Testen ohne Teleskop. | |
| `probe.py` | Testmuster zum Ausmessen von Zeilenbreite und Zeilenposition. | |
| `deploy.sh` | Kopiert die ESP32-Dateien per `mpremote` auf das Gerät. | |
| `test_migra.py` / `test_lx200.py` / `test_brightness.py` / `test_display.py` / `test_esp32.py` | Tests. | |
| `test_*.py` | Tests (migra, lx200, brightness, settings, display, esp32). | |
## Protokoll (Kurzfassung)
@@ -247,11 +248,14 @@ Mac:
## Offene Punkte
- **LDR-Schwellen in der Sternwarte gegenprüfen.** Die Grenzen sind am realen
Aufbau gemessen (2026-07-27, siehe *Helligkeitsregelung*), aber am Schreibtisch.
Vor Ort bei Tag und Nacht die Rohwerte ablesen und `HELL_LOW`/`HELL_HIGH`/
`HELL_MAX` bestätigen — besonders die Grenzlage bei 2400. Bis dahin läuft die
Anzeige mit fester Helligkeit (`run_esp32.main()` ohne `with_ldr`).
- **Umschaltschwelle in der Sternwarte gegenprüfen.** Die Schwelle (1800) ist am
realen Aufbau gemessen (2026-07-27, siehe *Helligkeitsregelung*), aber am
Schreibtisch. Vor Ort bei Tag und Nacht die Rohwerte ablesen und die Schwelle in
`settings.json` bestätigen. Bis dahin läuft die Anzeige mit fester Helligkeit
(`run_esp32.main()` ohne `with_ldr`).
- **MQTT-Anbindung.** Die Einstellungen liegen bereits zur Laufzeit änderbar in
`settings.json` (`settings.update(...)`, geprüft auf Typ und Bereich); der
MQTT-Client, der sie von außen setzt, fehlt noch.
- **Netzwerk zur echten Montierung.** Die GM4000 steht in `192.168.1.115`, das
Heimnetz des ESP32 ist `192.168.178.x` — der Mac erreicht sie per VPN, der
ESP32 so nicht. Muss geklärt werden, bevor es an die echte Montierung geht.
@@ -343,51 +347,75 @@ zerlegt (Trennzeichen `:`, `*`, `0xDF`, `'` sind alle egal).
## Helligkeitsregelung
Portiert aus dem alten MSP430-Programm (`DoDimmen_V`). Ein LDR-Rohwert
(12-Bit-ADC, 04095) wird über `raw_to_ldr` in die Zählrichtung „groß = hell"
gebracht, über einen gleitenden Mittelwert (8 Werte) geglättet und auf eine
Helligkeitsstufe mit Hysterese abgebildet:
**Zwei Helligkeiten, ein Umschaltpunkt.** Unterhalb der Schwelle läuft die Anzeige
mit `dunkel_prozent`, oberhalb mit `hell_prozent`. Die vier abgestuften Helligkeiten
des alten MSP430-Programms (5/15/50/100 %) sind bewusst aufgegeben — für die
Sternwarte genügt der Wechsel Tag/Nacht.
| Stufe | Helligkeit | erreicht ab (hoch / runter) |
|------|-----------|------------------------------|
| 0 | 5 % | < 1050 |
| 1 | 15 % | ≥ 1350 / < 1050 |
| 2 | 50 % | ≥ 2550 / < 2250 |
| 3 | 100 % | ≥ 3350 / < 3050 |
Ein LDR-Rohwert (12-Bit-ADC, 04095) wird über `raw_to_ldr` in die Zählrichtung
„groß = hell" gebracht, über einen gleitenden Mittelwert (8 Werte) geglättet und
gegen die Schwelle gehalten:
Schwellen-Mitten `1200 / 2400 / 3200`, Hysterese einheitlich `±150`.
| Bereich | Helligkeit | Umschaltpunkt |
|---|---|---|
| unter der Schwelle | `dunkel_prozent` = 5 % | herunter ab < 1650 |
| über der Schwelle | `hell_prozent` = 50 % | hoch ab ≥ 1950 |
Schwelle `1800`, Hysterese `±150`. Beim allerersten Messwert entscheidet die
blanke Schwelle (1800) statt der Kante — sonst startete die Anzeige im Bereich
dazwischen fälschlich dunkel und wachte erst später auf.
### Die Werte stehen in `settings.json`, nicht im Code
`config.py` ist Programmcode und ändert sich nur beim Deployen. Die Helligkeiten
und die Schwelle sollen dagegen im Betrieb verstellbar sein — **später über MQTT**.
Sie liegen deshalb als JSON auf dem Gerät und werden über `settings.py` gelesen und
geschrieben:
```python
import settings
settings.load() # mit Vorgaben aufgefüllt
settings.update({"hell_prozent": 60}) # ändern und speichern
```
Ein laufender `BrightnessController` übernimmt Änderungen mit `reload()`.
Jeder Wert wird beim Laden **und** beim Schreiben geprüft (Typ und Bereich:
Prozente 0100, Schwelle 04095). Das ist der Grund, warum die Prüfung überhaupt
existiert: Sobald MQTT dazukommt, kommen die Werte von außen, und ein
Helligkeitswert von 500 % oder eine Schwelle von 1 würde die Anzeige sonst
unbrauchbar machen. Fehlt die Datei oder ist sie beschädigt, gelten die Vorgaben
aus `settings.DEFAULTS` — ein Gerät in der Sternwarte soll sich nicht an einer
kaputten Konfigurationsdatei aufhängen. Ein einzelner unbrauchbarer Wert verwirft
nur diesen, nicht die ganze Datei.
`settings.json` wird von `deploy.sh` **nicht** mitkopiert und steht in `.gitignore`:
Es ist Laufzeitzustand des Geräts, kein Quellcode. Würde es mitkopiert, wären die
eingestellten Werte nach jedem Deployen weg.
### Die Schwelle stammt aus einer Messung
**Am realen Aufbau gemessen (2026-07-27)**, XIAO ESP32-C3 mit LDR an GPIO2,
je 200 Messwerte, Median — Werte nach `raw_to_ldr`:
| Lichtsituation | roh | nach `raw_to_ldr` | Stufe |
|---|---|---|---|
| abgedeckt | 4095 (ADC-Anschlag) | 0 | 0 → 5 % |
| Raumlicht | 1642 | 2453 | 115 % |
| angeleuchtet (Taschenlampe) | 543 | 3552 | 3100 % |
| abgedeckt | 4095 (ADC-Anschlag) | 0 | dunkel → 5 % |
| Raumlicht | 1642 | 2453 | hell → 50 % |
| angeleuchtet (Taschenlampe) | 543 | 3552 | hell50 % |
Die alten Werte `300 / 2500 / 3500` stammten vom MSP430-Aufbau und passten nicht:
Stufe 3 begann bei 3650 und war **unerreichbar** — selbst eine Taschenlampe direkt
auf dem Sensor kam nur auf 3552. Die Aufteilung ist jetzt so gewählt, dass der
5-%-Bereich bis 1200 reicht („nachts länger dunkel", schont die Dunkeladaption).
Die Schwelle 1800 liegt mit Abstand zwischen dem Dunkelwert und dem Raumlicht.
Auch die **Hysterese war zu klein**: Der Rohwert streut bei Raumlicht um ±270,
nach der Mittelung bleiben rund ±32 (1σ). Die früheren `±30` am untersten Übergang
lagen darunter — die unterste Stufe hätte geflattert. Jetzt einheitlich ±150 (≈4,7σ)
und damit weit unter dem Stufenabstand von 800.
Ein Helligkeitstelegramm wird nur bei **Stufenwechsel** gesendet (`ESC H 2 <ppp>`),
wie im Original. Am Gerät bestätigt: Abdecken schaltet herunter, Freigeben wieder
hoch, ohne Flattern.
> **Bekannte Grenzlage:** Der gemessene Raumlichtwert 2453 liegt nur 53 Zähler über
> `HELL_HIGH` (2400) und damit innerhalb der Hysterese — Raumlicht landet deshalb auf
> Stufe 1 (15 %), nicht auf 50 %. Ob das so gewollt ist, entscheidet sich erst an den
> Lichtverhältnissen in der Sternwarte. `test_brightness.py` hält das Verhalten fest.
**Die Hysterese ist nicht optional.** Der Rohwert streut bei Raumlicht um ±270,
nach der Mittelung bleiben rund ±32 (1σ). Ohne Hysterese würde die Anzeige am
Umschaltpunkt zwischen den beiden Helligkeiten hin- und herspringen; ±150 liegt
sicher darüber (≈4,7σ). Ein Helligkeitstelegramm wird ohnehin nur bei
**Stufenwechsel** gesendet (`ESC H 2 <ppp>`), wie im Original.
**Im Dunkeln steht der ADC am Anschlag** (4095). Der Teiler treibt A0 über den
Messbereich, „Dämmerung" und „stockdunkel" sind daher nicht unterscheidbar. Für die
Regelung unkritisch — nachts ist ohnehin die dunkelste Stufe gewollt. Eine feinere
Messbereich, „Dämmerung" und „stockdunkel" sind daher nicht unterscheidbar. Bei
nur zwei Helligkeiten ist das ohne Belang — unterschieden werden muss nur hell
gegen dunkel, und dafür reicht die Auflösung weit. Eine feinere
Nachtabstufung ginge nur über den Vorwiderstand: sättigungsfrei wäre
`R2 ≥ R_LDR(dunkel) × (3,3 V U_max)/U_max`, also grob `R2 ≥ 0,32 × R_LDR(dunkel)`.
Bei einem typischen LDR (0,51 MΩ im Dunkeln) liefe das auf 160330 kΩ hinaus — dann