Jump to content
Schiffsmodell.net

Multiswitch für Taranis mit Arduino (Teil 2)


Meinolf Höhler

Recommended Posts

Meinolf Höhler

Oh man, dort

hatte ich mal mit dem Projekt angefangen, und das ist schon 9 Monate her. Höchste Zeit mal wieder was dazu zu schreiben.
Ich habe ein neues Thema dafür auf gemacht, weil sich doch ein paar Dinge geändert haben, aber das Projekt dafür jetzt auch rund läuft.

Fangen wir nochmal vorne an, was ist das Ziel?

Da die Taranis X9E nicht über Multiswitch-Möglichkeiten verfügt, wie das z.B. mit dem Modulen von IMTH für Graupner möglich ist, habe ich nach einem Weg gesucht möglichst viele "Funktionen" vom Sender zum Empfänger zu übertragen, ohne mir aber die 32 möglichen PPM Kanäle zuzubauen.
Diese ganzen Funktionen sollen irgendwann auf meinem Rettungskreuzer Eiswette die Beleuchtung, das Radar, und die Scheibenwischer schalten.

ot-pfeif-sp.gif

Scheibenwischer? Bist du komplett wahnsinnig?

ot-pfeif.gif

:sonstbeule:Shush...immer diese Stimmen aus dem Off, die stören wollen: JA SCHEIBENWISCHER! :mrgreen:
Es gibt schöne kleine Microservos und Linearservos, groß, wie eine Briefmarke, warum nicht versuchen die 5 Dinger auch irgendwie unterzubringen. Genug Schaltkanäle sollten sich finden lassen.

Damit die Stimmen noch mehr zu diskutieren haben, möchte ich das Ganze dual steuern können. Nur über den Sender ist langweilig, warum also nicht über ein Smartphone steuern können, wenn die in der Vitrine geparkt ist und einfach nur schön aussehen soll?

Die Beleuchtung soll zudem noch halbwegs vernünftig sein, und einem nicht beim Einschalten hell wie die Sonne entgegen leuchten. Also müssen die LED irgendwie individuell gedimmt werden.

Was braucht man für diese Anforderungen?

Fangen wir an den Ausgängen an:
gedimmte LED, das schreit nach einer PWM Steuerung.
Adafruit (und auch andere als Kopie davon) bieten eine Platine auf Basis des PCA9685 PWM Treiberbausteins an.

https://www.adafruit.com/product/815

Das Modul hat 16 Kanäle, jeder kann unabhängig eingestellt und angesprochen werden. Die Ausgangsspannung ist 3,3V und der Strom wird pro Kanal automatisch auf 10mA begrenzt. Für LED sind diese Eckdaten völlig ausreichend.
Außerdem kann man Servos anschließen und ansteuern. Das Modul arbeitet schließlich mit PWM, was nichts anderes ist, als das Signal, was der Empfänger für Servos generiert. Man muss nur ein wenig nachdenken oder ausprobieren, dass man die übliche Einschaltdauer für Servos hin bekommt. Da die Servos etwas mehr Strom als die LED brauchen, gibt es einen separaten Kontakt zur Stromversorgung auf den Platinen. Dazu später mehr.
Insgesamt bin ich bei der Eiswette mit allen LED für die Navigationsbeleuchtung, Deck- und Arbeitsscheinwerfer, Suchscheinwerfer, 6 LED für das Blaulicht und die Servos auf 39 Ausgänge gekommen. Es werden also drei von den PWM Modulen benötigt.

Der Motor des Radars hat mir zusammen mit den PWM Modulen einen üblen Scherz gespielt. Die Anleitung für den PWM Treiber sagte aus, dass der Strom auf 25mA begrenzt ist. Das sollte für ein kleines Motörchen, wie dieses

https://www.sol-expert-group.de/1zu87modellbau/Motor-und-Getriebe/Aufgebaute-Getriebe/Mikro-Planeten-Getriebe-G700::677.html

locker reichen. An einer Mignon-Batterie als Test lief der schon in einer guten Drehzahl, also habe ich den Motor an das Modul angeschlossen. Mit voll aufgerissener PWM kam der Motor aber nicht annähernd an die Drehzahl, als mit einer Mignonzelle. Hmmm.....Batterie = 1,5V => gute Drehzahl, PWM 3,3V = Miese Drehzahl? Was soll denn der Quatsch? Ich hab alles auf den Kopf gestellt, bis ich mal über das Labornetzteil den Strom gemessen habe, den der Motor bei 1,5V zieht. Der Motorstrom war zu hoch.

Die PWM Boards haben an den Ausgängen für jeweils 4 Kanäle einen 220 Ohm Widerstandsbaustein fest eingebaut. Damit war die Strombegrenzung auf 10mA runtergesetzt, und der Motor hatte zu wenig Dampf, um schnell genug zu drehen.
Also was tun?
Diese SMD Maikäfer auslöten und irgendwas dämliches probieren? Neee, nachher geht was anderes dadurch kaputt, dann ärgere ich mich. In der Sammelkiste hatte ich aber noch ein Relaismodul, was über ein Servosignal zu schalten ist. Also wird das genommen, davor eine regelbare Spannungsquelle gesetzt und gut ist.

Link to post
Meinolf Höhler

Angesteuert werden die PWM Module über den I²C-Bus, und von Adafruit gibt es für deren Module schon fertige Programmbibliotheken, die recht einfach in der Arduino-IDE verwendet werden können.

Arduino? Gutes Stichwort, was für einen Microcontroller nehme ich? Ein Standard-0825-Arduino, also der mit dem 328P-Chip ist zu groß vom Format, und ich müsste dann noch ein separates WiFi Modul anschließen.

In dem anderen Bericht (oben verlinkt) bin ich ja schon beim ESP8266 raus gekommen. Der Chip hat ausreichend Speicher und ist schnell genug die Daten zu verarbeiten. Außerdem hat der WiFi eingebaut und es gibt ausreichend Programmbibliotheken und Codebeispiele.

Adafruit bietet das Modul Feather HUZZAH ESP8266 an. Das Format ist mit 1x2 Zoll schön kompakt, und reicht für meine Zwecke aus.

Die Hardware sieht also in Blockbildern so aus (die benötigten BEC und Servos für die normale Schiffssteuerung sind ausgelassen):

image.png.2d5d48869c0fb75b041a92db7cba5189.png

Als nächstes geht es dann in die Programmierung.
Für die Programmierung des Senders verweise ich mal frech auf den alten Beitrag, weil sich an dem System nichts geändert hat:

 

Link to post
Meinolf Höhler

Auf zur Programmierung des ESP8266. Ich verwende dazu die normale Arduino-IDE Version 1.8.12, und setze voraus, dass man sich etwas mit C/C++ auskennt.

Für den Feather HUZZAH muss man in der Boardverwaltung "esp8166" von der "ESP8266Community" aktivieren.

Aus der Bibliotheksverwaltung brauchen wir einmal "Adafruit PWM Servo Driver Library" und zusätzlich nutze ich für die Fehlersuche ein I²C-Displaymodul, was "Adafruit SSD1306" und die "Adafruit GFX Library" benötigt.

Zur Auswertung des S.Port Signals verwende ich eine Bibliothek von "Pawelsky" ( https://www.rcgroups.com/forums/showthread.php?2245978-FrSky-S-Port-telemetry-library-easy-to-use-and-configurable ).

Die ZIP Datei ist der letzte Stand meiner Programmierung. Der Code ist vielleicht nicht optimal kommentiert, aber dafür schreibe ich ja hier auch noch ein paar Takte dazu.

 

Insgesamt besteht das Programm aus 5 Code-Modulen.
- SK33v2.ino ist die Hauptfunktion (SK33 kommt vom Baunamen der Eiswette der 20m Klasse)
- SK33RcvData ist die Datenhaltung für empfangenen Daten
- FrSkySportTelemetryReader sind Code und Header zum Auslesen der Daten, die über die Telemetrie rein gekommen sind
- SK33AccessPoint ist...naja der Name sagt es, der WiFi-AccessPoint, den der ESP8266 auf macht
- SK33WebServer sagt auch was es ist, der Webserver, der die Seite zum Smartphone oder Tablet übernimmt

 

SK33v2.zip

Link to post
Meinolf Höhler

Ich werde die Programmdateien in der oben genannten Reihenfolge durchgehen. Das macht die Erklärung sicher einfacher, als hin und her zu springen.

Variablen habe ich meist mit englischen Begriffen benannt. Ist einfach eine Gewohnheit von mir.

SK33v2.ino

In den ersten 20 Zeilen werden die diversen, benötigten Bibliotheken eingebunden. Nichts Besonderes ist an der Stelle zu finden.

Als nächstes folgen die #define.

#define BLUELIGHT_SWITCH_TIME 50
#define WIPER_STEP_TIME 10
#define RX_WEB_TOGGLE_SWITCH 13
#define SERVO_FREQ 50 // Analog servos run at ~50 Hz updates

Das Blaulicht des Kreuzers ist mit 6 blauen LED ausgeführt, die auf einem Sechskant aufgeklebt sind, und im Kreis angesteuert werden. Das erste Define ist dabei die Zeit in Millisekunden, in der von einer auf die nächste LED gewechselt wird. Für eine Umrundung braucht es also 300ms.

WIPER_STEP_TIME ist die Berechnungszeit für die Scheibenwischer, mehr dazu später.

Damit sich das WiFi und Signale aus dem Sender nicht in die Quere kommen, habe ich einen Schalter eingeplant, mit dem man zwischen den beiden Modi umschalten kann. Das dritte Define legt den Pin dieses Schalters fest.

Zuletzt wird noch die Standardfrequenz für Servos festgelegt. Diese liegt bei 50Hz, weil sich für Standardservos das PWM-Signal normalerweise jede 20ms wiederholt.

struct PwmChannel {
  bool IsLED;
  uint16_t min;
  uint16_t max;
  uint16_t value;
  uint16_t txInterval;
  unsigned long lastTx;
};

Dieses Struct verwende ich zum Speichern der Informationen für die einzelnen PWM Kanäle. Die Werte beschreiben Minimal- und Maximalwerte, was für die Servos als Wegbegrenzung nützlich ist. "IsLED" ist ein Flag, ob eine LED angeschlossen ist. "value" ist der aktuelle Wert, der an das PWM-Modul übertragen werden soll. "txInterval" und "lastTx" sind für die Übertragung der Daten zuständig.

Verwendet wird das Struct nun in den folgenden drei Arrays (Zeilen 37-86), wobei in der Initialisierung z.B. für LEDs im Max-Wert festgelegt ist, wie hell die LED leuchtet.

In den Zeilen 88-122 folgen einige Variablendefinitionen und -initialisierungen für den Rest des Programms.
Hier ist definitiv Zeile109-110 und 116-118 zu nennen. Die erstgenannten Zeilen initialisieren Objekte für die Verarbeitung der Telemetriedaten.

// Sensor definition to initialize telemetry
FrSkySportSensorAss ass(FrSkySportSensor::ID14); //create a sensor
FrSkySportTelemetryReader telemetry; //create a telemetry object

Die erste Zeile nach dem Kommentar erstellt ein Objekt, was einen Telemetriesensor auf dem S.Port darstellt, zu dem die Daten übertragen werden.
Die zweite Zeile erstellt ein Telemetrieobjekt zur Auswertung der Sensordaten.

// create PWM objects for LED and LED/Servo
Adafruit_PWMServoDriver pwmModule1 = Adafruit_PWMServoDriver(0x40, Wire);
Adafruit_PWMServoDriver pwmModule2 = Adafruit_PWMServoDriver(0x41, Wire);
Adafruit_PWMServoDriver pwmModule3 = Adafruit_PWMServoDriver(0x44, Wire);

Diese drei Zeilen erstellen für jedes der PWM-Module je ein Objekt. Mitgegeben werden den Objekten die i²C-Adressen der Module.

Zuletzt werden noch drei Objekte für die Datenhaltung, Webserver und AccessPoint erstellt.

setup()

Weitere Objekte werden in dieser Funktion initialisiert oder z.B. die Kommunikation über den I²C-Bus gestartet.
In Zeile 135 wird initialisiert, auf welchem Pin das Telemetriesignal vom Empfänger herein kommt.
Damit alle Ausgänge in einem definierten Zustand sind, werden einmal mit einer Schleife in den Zeilen 159-163 der Minimalwert der einzelnen Kanäle auf die Module herausgeschrieben.

Nun folgen mehrere Funktionen, die zur Aufgabe haben, die Werte für die PWM Ausgabe zu setzen.

Fahrt() schaltet die normale Navigationsbeleuchtung ein (Topplicht, Hecklicht, Backbord, Steuerbord), Schleppfahrt() schaltet zusätzlich das Topp- und Heckschlepplicht ein.
Bluelight() ist für den Fall da, dass das Blaulicht ausgeschaltet wird. Mit der Funktion erlöschen dann alle blauen LED.
calculateBluelight() ist genau für den anderen Fall da, es berechnet nämlich, welche LED gerade eingeschaltet werden soll. Dafür wird das in den Defines verwendete Makro BLUELIGHT_SWITCH_TIME als Zeitkonstante verwendet. Setzt man das Makro auf eine größere Zeit, dreht sich das Blaulicht langsamer, und entsprechend anders herum mit kleineren Werten.
Radar() ist dafür zuständig den Radarmotor einzuschalten, was über eine Relaisplatine realisiert ist.
Wipers() und calculateWipers() ist ähnlich, wie für das Blaulicht. Für die Scheibenwischer kommt aber noch eine weitere Funktion dazu: stopWipers().
Damit ein wenig Zufall in die Bewegung der Wischer kommt, kann man für alle Wischer eine andere Geschwindigkeit über die Variablen in den Zeilen 102-106 einstellen. Das bedeutet aber auch, dass die nach kurzer Zeit einfach nicht mehr synchron stehen. Um beim Ausschalten einen Sprung in die Grundposition (z.B. die linke Seite der Scheibe) zu vermeiden, berechnet stopWipers() die Bewegung der Wischerservos so lange weiter, bis alle Servos in der Grundstellung angekommen sind.
Manoevrierbehindert() - ja noch eine Bezeichnung, wo ich nicht Englisch verwendet habe, aber es ist so einfacher :) - schaltet die rot-weiß-roten Mastlichter an.

Abschließend folgt loop(), die Standardfunktion, die immer in einer Schleife durchlaufen wird.
Die IF-Abfrage in Zeile 478 macht die Unterscheidung, wo die Daten her kommen, ob vom Webserver oder vom Sender.
Kommen die Daten aus dem Sender wird die Telemetrie mit telemetry.available() in der IF-Abfrage abgefragt, ob Daten verfügbar sind, und ob sich neue Daten von den alten, bereits gespeicherten Daten unterscheiden.

Sind neue Daten vorhanden, werden die in die entsprechenden Variablen geschrieben (Zeile 487-490) und ein Dirty-Flag gesetzt, was anderen Funktionen anzeigt, dass neue Daten gelesen wurden.

Zeile 499 weist den Webserver an, auf einen eventuell verbundenen Client zu reagieren, und macht in der Webserverfunktion im Prinzip das Gleiche, wie weiter oben die Telemetrie, neue Daten werden in die lokalen Variablen zur Auswertung geschrieben. Mehr dazu im Webservermodul.

Wurden Daten geändert, werden über die Zeilen 507-511 die weiter oben beschriebenen Funktionen ausgelöst, um LEDs zu schalten oder Werte zu berechnen.

Zwischen Zeile 517-553 werden die aktuellen Werte auf die PWM-Module herausgeschrieben.

Die Zeilen 554-556 starten die Berechnungen für Blaulicht und Scheibenwischer, und in Zeile 558 wird das Dirty-Flag der eingelesenen Daten zurückgesetzt, weil diese nun vollständig ausgewertet sind.

Link to post
Meinolf Höhler

Die Dateien, die nun folgen sind C++-Dateien, das heißt sie kommen immer in Paaren von Header- und Codedateien.

SK33RcvData

Dieses Modul ist die Datenhaltung, so zusagen das Model, falls jemand mit dem MVC-Programmiermodell vertraut ist.

Die Funktionen darin sind getter und setter für die privaten Variablen, die die Datenbytes darstellen.

 

FrSkySportTelemetryReader

Die FrSkyTelemetry Bibliothek (Link im dritten Post) kann die Daten über den S.Port auslesen, hatte aber nicht genau die Funktionalität, die ich brauchen konnte. Um nicht in Konflikte mit der Originalbibliothek zu kommen, und durch eventuelle Updates Funktionen zu verlieren, habe ich die Auswertung der empfangenen Daten auf der Telemetrie nach meinem Bedarf abgeändert und in die Funktion receive() verpackt, greife aber weiterhin auch auf Funktionen aus der "Pawelsky" Bibliothek zu.

Wie genau die Datenverarbeitung des S.Port Protokolls funktioniert, ist nicht besonders wichtig. Wenn ein korrekter Datensatz erkannt wurde, werden die Daten aus dem Array des Datenstroms extrahiert und in die lokalen Daten geschrieben (Zeilen 23-27).

SK33AccessPoint

Dieses Objekt hat eine Aufgabe, und das ist den WiFi-AccessPoint zu starten. Den Code habe ich aus einem Beispiel zu dem AP gefunden, und ist auch nicht so viel und schwer zu verstehen.

Link to post
Meinolf Höhler

SK33Webserver

Zuletzt bleibt noch das Webservermodul.

void SK33WebServer::init() {
	_server.on ( "/", std::bind(&SK33WebServer::handleRoot, this) );
	_server.on ( "/F", std::bind(&SK33WebServer::handleFahrt, this) );
	_server.on ( "/S", std::bind(&SK33WebServer::handleSchlepp, this) );
	_server.on ( "/B", std::bind(&SK33WebServer::handleBluelight, this) );
	_server.on ( "/M", std::bind(&SK33WebServer::handleManeuver, this) );
	_server.on ( "/R", std::bind(&SK33WebServer::handleRadar, this) );
	_server.on ( "/W", std::bind(&SK33WebServer::handleWipers, this) );
  
	_server.onNotFound ( std::bind(&SK33WebServer::handleNotFound, this) );
}

Die Initialisierung des Webservers stellt ein paar Bindings (Aufrufe auf bestimmte Funktionen) her. Diese sind die Verbindung von einem Hyperlink zu einer Funktion, die ausgeführt werden soll. Bei mir wird der Webserver über die Adresse 192.168.4.1 aufgerufen. Letztlich ist die Adresse, die der Server sieht "192.168.4.1/". Der "/" ist in sofern wichtig, weil der schon ein Binding auf eine Standardroutine darstellt. Ist die Adresse  "192.168.4.1/F" wird das Binding ausgelöst, was die Beleuchtung für die normale Fahrt ein- oder ausschaltet.

handleRoot() ist ein Binding bzw eine Funktion, was immer ausgelöst wird, auch am Ende von den anderen Bindings. Darin ist die zu übertragende Webseite hinterlegt.

Vor dem Einstieg in die Webseite ein paar Gedanken:

Der ESP8266 hat ein wenig Speicher. Trotzdem können wir nicht mit dem Speicher umgehen, wie auf einem Raspberry Pi oder einem PC. Sparsamkeit sollte gerade für den Webserver im Vordergrund stehen. Aber nur aus Sparsamkeit einfache Textausgaben mit Links zu produzieren war mit zu doof. Ich möchte wein bisschen was für das Auge haben.

Die Entwicklung von HTML und XML im Web hat interessante Blüten geschlagen, denn es gibt seit einiger Zeit den SVG-Grafikstandard. SVG steht für Scalable Vector Graphics, und ist im Kern eine Textdatei, die auf dem XML-Standard basiert. Man kann also einfache und skalierbare Grafikelemente zusammenbasteln. Jeder moderne Internetbrowser versteht SVG ohne Probleme.
Wer mehr darüber lesen möchte, dem kann ich nur wärmstens diesen Link anvertrauen:

https://www.w3schools.com/graphics/svg_intro.asp

Der Webserver wird also eine normale HTML Seite ausliefern, in dem nur mit Text eine SVG-Grafik eingebettet ist, und die enthält dann an bestimmten Elementen normale Hyperlinks, mit denen dann Funktionen geschaltet werden. Alles ganz einfach :mrgreen: Nicht? :weisnicht: Na gut, ich mache weiter den Erklärbär 8-)

Das HTML im Code wird als Zeichenkette in eine Variable übernommen und ergibt, wenn alles korrekt läuft, das folgende Bild:

1469568681_Bild25_07.20um00_25.png.6d09dcf25f61056b79f7aea5c1370934.png

Durch verschiedene Variablen werden an die Texte Links angehangen, die dann die Schaltfunktion auslösen. Tippt man also auf "Blaulicht", geht dieses an oder aus.
Als weitere verrückte Idee könnte man schauen, ob man je nach Schaltzustand die Farben anpassen. Mal sehen, es muss ja noch was zum weiterentwickeln geben.

Durch Anpassung des HTML/SVG Codes kann man die ganze Angelegenheit einfach auf andere Schiffe anpassen.

Nach handleRoot() folgen die anderen handle...() Funktionen. Diese enthalten den Code, um die verschiedenen Funktionen zu schalten. Dies wird erreicht, indem wie bei den Daten aus der Telemetrie die Werte in die internen Variablen von SK33RcvData geschrieben werden.

Link to post
Meinolf Höhler

So der Anfall von Schreibwut ist erst einmal durch. Wenn Fragen sind, immer raus damit.

In dem ersten Thema hatte Ralf (@r41065) ja noch ein anderes Script zum Schalten der Werte in OpenTX erstellt. Da ist mit Sicherheit auch noch irgendwas zu verbessern, dass man z.B. auf dem Display sieht, was man da gerade angeschaltet hat, ohne auf das Schiff zu schauen. Mal sehen, was da noch so kommt.

Link to post

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.