Kennst du dich mit Arduino aus?

Stellt euch vor, ihr sitzt gemütlich beim OV-Abend (für die Nicht-Eingeweihten: das regelmäßige Klassentreffen der Funkamateure vom Ortsverband des DARC – S51). Die Stimmung ist gut, das Bier ist kalt und plötzlich kommt ein OM (Old Man = Standardanrede für Amateurfunker, egal wie jung) auf dich zu. Die alles entscheidende Frage:

„Du sag mal, kennst du dich eigentlich mit Arduino aus?“

Meine Antwort, in weiser Voraussicht auf kommende Kopfschmerzen: „Kommt drauf an…“ (Die diplomatische Version von: „Ich weiß, wie man eine LED zum Blinken bringt, ohne die Bude abzufackeln“).

Die Mission: Mondlandung (fast)

Es ging um eine Antennensteuerung. Der Aufbau klang eigentlich simpel: Ein Windows-Programm namens PstRotor schickt Befehle via USB an einen Arduino, der wiederum den Rotor dreht. Das Problem? Das Programm ist präzise wie ein Schweizer Uhrwerk und liefert 0,1°-Schritte. Der Arduino hingegen war eher Team „Grobmotorik“ und reagierte nur auf ganze Zahlen.

Für Satelliten reicht das, aber der OM will zum Mond! Also, via EME (Erde-Mond-Erde). Da nutzt man den Mond quasi als gigantischen, kalten Spiegel im All. Wenn die Antenne da auch nur ein bisschen daneben schielt, funkst du ins Leere – und der Mond denkt sich seinen Teil.

Challenge Accepted (oder: Warum sage ich nie Nein?)

Ich dachte mir: „Komm, wie schwer kann das sein?“ Details angefordert, Sketch (Quellcode) erhalten. Der Autor viorelracoviteanu hat das Projekt dankenswerterweise als Open Source freigegeben, also durfte ich legal im Code-Maschinenraum herumschrauben.

Meine erste Vermutung war goldrichtig: Der Code war vollgestopft mit int (Integer). Für alle Nicht-Nerds: Das sind Ganzzahlen. Die kennen kein „ein bisschen“, die kennen nur 1, 2, 3. Also habe ich erst mal eine Runde float spendiert – Gleitkommazahlen für alle! Endlich durfte die Nachkommastelle mitspielen.

Die Tücken des Objekts

Ganz so einfach war es dann doch nicht:

  1. Das Display: Das wollte partout weiterhin nur ganze Zahlen anzeigen (Eitelkeit muss sein). Also Rundungsfunktionen eingebaut.
  2. Die Drehgeber: Die sollten beim manuellen Kurbeln weiterhin schön knackig in 1°-Schritten rasten.
  3. Der Endgegner – Der Parser: Hier wurde es knifflig. Der Parser ist das Bauteil im Code, das die Befehle vom PC liest. Der Schlingel hat einfach alles nach dem Dezimalpunkt abgeschnitten. „0,1 Grad? Ich mach mal ne 0 draus“, dachte er sich wohl. Also musste auch hier die Chirurgen-Zange ran.

Blindflug mit Happy End

Jetzt kommt der Teil, bei dem jeder Software-Entwickler Schweißausbrüche bekommt: Ich besitze diese Hardware gar nicht. Ich hatte zwar irgendwo einen einsamen Arduino rumliegen, aber keinen Rotor-Controller zum Testen. Zudem entwickle ich eigentlich mit PlatformIO und nicht mit der Standard-Arduino-App da sind die aufgerufenen Funktionen unten.

Also: Code geändert, tief durchgeatmet, „Mut zum Risiko“ gemurmelt und die Datei per E-Mail zurück an den OM geschickt. Mit dem Hinweis schon mal eine Hand an der Hauptsicherung zu haben.

Das Ergebnis? Er hat den Sketch eingespielt und… es läuft! Die Antenne bewegt sich jetzt so geschmeidig, dass selbst Patrick Swayze in Dirty Dancing beeindruckt wäre.

Ob der Mond jetzt auch wirklich brav alles zurückreflektiert, was der OM hochschickt? Das ist eine andere Geschichte. Ich sitze erst mal hier, genieße meinen Whisky und warte auf die nächste „Kennst du dich eigentlich mit…“-Frage. Ich werde wieder mit „Kommt drauf an“ antworten. Sicher ist sicher.

Und hier noch der Link zu Github.

# Änderungen: `.ino.bak` → `.ino`

Umstellung auf `float`-Typen für Azimuth/Elevation-Kommandowerte (0,1°-Auflösung via EasyComm II).

---

## Zeile 101–104 — Variablentypen `int` → `float`

```diff
- int ComAzim = 0;
- int ComElev = 0;
- int OldComAzim = ComAzim;
- int OldComElev = ComElev;
+ float ComAzim = 0.0;
+ float ComElev = 0.0;
+ float OldComAzim = ComAzim;
+ float OldComElev = ComElev;
```

---

## Zeile 124 — `increment` auf `float`

```diff
- int increment = 1;                  // degrees to increment at encoder rotation
+ float increment = 1.0;              // degrees to increment at encoder rotation (reserved for future use)
```

---

## Zeilen 174, 176, 209, 219, 361, 363, 375, 384, 399, 400 — `DisplValue`-Aufrufe mit `round()`

Alle `DisplValue(ComAzim, ...)` und `DisplValue(ComElev, ...)` werden auf Integer gerundet, da die Display-Funktion Integer erwartet.

```diff
- DisplValue(ComAzim,12,0);
+ DisplValue(round(ComAzim),12,0);

- DisplValue(ComElev,12,1);
+ DisplValue(round(ComElev),12,1);
```
*(betrifft Zeilen 174, 176, 209, 219, 361, 363, 375, 384, 399, 400)*

---

## Zeilen 370–375 — Encoder-Inkrementierung Azimuth

Statt einfacher `+= increment`-Addition wird auf den nächsten ganzzahligen Grad gerundet, um Floating-Point-Drift zu vermeiden.

```diff
- if (AzEncUp) ComAzim += increment;
- else ComAzim -= increment;
- ComAzim = ((ComAzim + 360) % 360);
- ComAzim = constrain (ComAzim, 0, 359);
+ if (AzEncUp) ComAzim = floor(ComAzim) + 1.0; // round to next integer upwards
+ else         ComAzim = ceil(ComAzim)  - 1.0; // round to next integer downwards
+ ComAzim = fmod((ComAzim + 360.0), 360.0);
+ ComAzim = constrain(ComAzim, 0.0, 359.0);
```

---

## Zeilen 380–384 — Encoder-Inkrementierung Elevation

Gleiche Logik wie bei Azimuth.

```diff
- if (ElEncUp) ComElev += increment;
- else ComElev -= increment;
- ComElev = constrain(ComElev, 0, 90);
- DisplValue(ComElev,12,1);
+ if (ElEncUp) ComElev = floor(ComElev) + 1.0; // round to next integer upwards
+ else         ComElev = ceil(ComElev)  - 1.0; // round to next integer downwards
+ ComElev = constrain(ComElev, 0.0, 90.0);
+ DisplValue(round(ComElev),12,1);
```

---

## Zeilen 461–466 — EasyComm-Parser: Dezimalpunkt in Azimuth-Eingabe

Neuer `bool azDotSeen`-Flag; Parser akzeptiert jetzt genau einen Dezimalpunkt in der Azimuth-Zeichenkette.

```diff
+ bool azDotSeen = false;
  ...
- if (isDigit(ComputerRead.charAt(j))) {
-   Azimuth = Azimuth + ComputerRead.charAt(j);
- }
- else {break;}
+ char c = ComputerRead.charAt(j);
+ if (isDigit(c)) {
+   Azimuth = Azimuth + c;
+ } else if (c == '.' && !azDotSeen) {
+   azDotSeen = true;
+   Azimuth = Azimuth + c;
+ } else {break;}
```

---

## Zeilen 477–482 — EasyComm-Parser: Dezimalpunkt in Elevation-Eingabe

Analog zur Azimuth-Änderung.

```diff
+ bool elDotSeen = false;
  ...
- if (isDigit(ComputerRead.charAt(j))) {
-   Elevation = Elevation + ComputerRead.charAt(j);
- }
- else {break;}
+ char c = ComputerRead.charAt(j);
+ if (isDigit(c)) {
+   Elevation = Elevation + c;
+ } else if (c == '.' && !elDotSeen) {
+   elDotSeen = true;
+   Elevation = Elevation + c;
+ } else {break;}
```

---

## Zeilen 489–490, 495–496 — `toInt()` → `toFloat()` + `constrain` auf float

```diff
- ComAzim = Azimuth.toInt();
- ComAzim = constrain(ComAzim, 0, 359);
+ ComAzim = Azimuth.toFloat();
+ ComAzim = constrain(ComAzim, 0.0, 359.0);

- ComElev = Elevation.toInt();
- ComElev = constrain(ComElev, 0, 90);
+ ComElev = Elevation.toFloat();
+ ComElev = constrain(ComElev, 0.0, 90.0);
```

---

## Zeilen 503, 518 — EasyComm-Antwortformat mit `dtostrf`

Position-Antwort (`AZ EL`) gibt jetzt eine echte Dezimalstelle aus statt hartcodiertem `.0`.

```diff
- ComputerWrite = "+"+String(round(TruAzim))+".0 "+String(round(TruElev))+".0";
+ char azBuf[8], elBuf[6];
+ dtostrf(round(TruAzim * 10.0) / 10.0, 5, 1, azBuf);  // e.g. "180.4"
+ dtostrf(round(TruElev * 10.0) / 10.0, 4, 1, elBuf);  // e.g. "45.2"
+ ComputerWrite = "+" + String(azBuf) + " " + String(elBuf);
```
*(betrifft Zeilen 503 und 518 — beide AZ/EL-Abfrage-Handler)*

Ein Kommentar zu “Kennst du dich mit Arduino aus?”