Jump to content
Meinolf Höhler

Multiswitch für Taranis mit Arduino

Recommended Posts

Für die Taranis Steuerungen mit OpenTX gibt es leider keine Multiswitche, wie z.B. von IMTH für die Graupner Anlagen.

Dafür kann man aber die X9E mit selbst geschriebenen Programmen füttern, und ein Befehl in der API sah verdächtig danach aus, Daten über den Telemetriekanal vom Sender zum Empfänger zu schicken: sportTelemetryPush()

Wenn FrSky-Empfänger oder Sensoren darüber Daten empfangen können, müsste es doch auch gehen, dass man einen Sensor mit einem Arduino ähnlichen Gefährt soweit zu emulieren, dass man das Signal darüber zweckentfremdet und für die eigenen Dienste nutzen kann.

Eine Internetrecherche ergab, dass ich nicht der Einzige mit dem Vorhaben war, und es schon eine Bibliothek gibt, mit der man einen „beliebigen“ Sensor darstellen kann (https://www.rcgroups.com/forums/showthread.php?2245978-FrSky-S-Port-telemetry-library-easy-to-use-and-configurable ). Von der werden also die weiteren Bemühungen ausgehen.

FrSkyMultiswitch.zip

MtSw.zip

Share this post


Link to post
Share on other sites

Die Bibliothek funktioniert entweder mit einem Arduino kompatiblen Teensy 3.0/LC, ESP8266, oder einem Board mit ATmega328P. Der Letztere ist für mich erst einmal die interessante Variante, da ich davon ein paar zu Hause rumfliegen habe.
Die Idee war dann zuerst, alles auf einen anderen Microcontroller zu portieren, nämlich den ATMEL SAMD21. Mehr Speicher, mehr Leistung? Warum nicht, kann doch nicht all zu schwer sein.
Leider dann doch, weil eine wichtige Information erst durch tieferes graben ans Licht kam.

Man braucht einen Chip, der die folgenden Eigenschaften erfüllen sollte:
- der UART muss im Halbduplex laufen können
- der Chip muss idealerweise mit einem invertieren UART-Signal sofort zurecht kommen

Laut Datenblättern kann man die UART des SAMD21 zwar in den Halbduplex-Modus umschalten, aber dazu muss man einiges in den Kernkomponenten der Arduino IDE umschreiben.
Der zweite Punkt ist mit dem SAMD21 gar nicht zu machen, ohne zusätzlich eine Schaltung zwischen Empfänger und Microcontroller zu setzen.

Pläne sind dazu da, um sie zu ändern, also zurück zum ursprünglichen Plan, zu einem Board mit dem 328P, was auf 3,3V läuft.

Ausgewählt habe ich für das Sensorreplikat von Adafruit den Pro Trinket 3V ( https://www.adafruit.com/product/2010 ). Der läuft mit 12 MHz Takt ausreichend schnell, hat allerdings den Nachteil, dass ein paar KB des Speichers von einem Bootloader belegt sind, der auch die Kommunikation über USB regelt. Dafür ist die Bauform schön kompakt.

Es soll ja was geschaltet werden, vorzugsweise LED. Da der ProTrinket nur relativ wenige Ausgänge besitzt, muss eine andere Lösung her, wie man externe Geräte schaltet. Da das meiste LEDs sein werden, nehme ich ebenfalls von Adafruit einen, bzw mehrere 16 Kanal-PWM Module mit dem PCA9685 ( https://www.adafruit.com/product/815 ). Dieser Chip wird über I2C-Bus angesteuert und behält den Befehl für einen Ausgang so lange bei, bis ein neuer Befehl kommt, oder der Strom abgeschaltet wird. Das charmante an dem Board ist die eingebaute Konstantstromquelle, die Ideal für die LED ist. Außerdem kann man einen Kanal von ganz aus bis ganz an in 4096 Stufen sagen, wie hell man die LED haben will. Damit wird nachher die Beleuchtung so eingestellt, dass sie einigermaßen realistisch aussieht, und nicht die Positionslaternen mit der Helligkeit eines Suchscheinwerfers leuchten.

Den „Sensor“ haben wir, einen Baustein, der was schaltet haben wir, der Empfänger, der den S.Port abgibt ist ein X6R, 6 Kanal PWM + 16 Kanal SBUS mit Telemetrie.

Um den ganzen Kram mit Strom zu versorgen, brauchen wir noch ein paar Dinge. Der ProTrinket kann 16V DC ab, weil der einen eigenen Spannungsregler eingebaut hat. Da der Regler aber nur 150mA extern liefern kann, werden die PWM Module mit einer eigenen 3,3V Versorgung bedacht. Dafür verwende ich einen einfachen StepDown Regler von Pololu.

Im Modell wird der Empfänger über das BEC des Fahrmotors versorgt. Da der Testaufbau eh an einem Labornetzteil hängt, will ich mir da den Aufwand erst einmal sparen und verwende ein 5V BEC, was direkt im Empfänger eingesteckt ist. Alle Spannungsquellen sind über eine gemeinsame Leitung von - und GND zusammengeschaltet.

Die Schaltung ist damit fast komplett. Weil der ProTrinket über die USB Schnittstelle keine Debugausgabe, wie die anderen Arduinos, bereitstellen kann, muss ich das anders machen. Da ich noch von Adafruit ein OLED-Display habe, was ebenfalls über I2C angesteuert wird, hänge ich das auch noch an den Bus dran und gebe mir bestimmte Daten darüber aus.

  • Like 1

Share this post


Link to post
Share on other sites

Die Schaltung sieht also so aus: X6R (S.Port) <-> ProTrinket Pin 4 /// I2C (Pin A4 und A5) <-> PWM Module 1&2 <-> OLED-Display

Dann geht es ans Programmieren, zuerst der Sender, weil der am einfachsten geht.
In die X9E wird ein Funktions-Script geschrieben. Ein Funktions-Script, weil Zugriff auf das Display erst einmal nicht nötig ist. Es soll „nur“ die Stellung der Schalter zum Empfänger geschickt werden.
 

Apropos, was wird eigentlich übertragen? Wieviel und wie?

Das Protokoll ist für uns nicht direkt interessant. Wir müssen nur wissen, wie die übertragenen Daten aussehen.
Insgesamt werden 10 Byte übertragen. Byte 1 ist eine Art Startsignal, und ist immer 0x7E. Byte 2 ist die Zieladresse, in diesem Fall 0x0D. Byte 3 und 4 sind für uns uninteressante Daten, die bleiben auf 0x10 und 0x01 stehen. In Byte 5-8 steht der übertragene Wert, und Byte 9-10 sind Prüfsummen.

Es stehen also 4 Byte/32 Bit zur Verfügung, in denen etwas sinnvolles übertragen werden kann.

32Bit = 32 Schalter bzw Schaltstellungen? Das ist mal eine Hausnummer, und die können theoretisch vollständig verwendet werden, da die X9E maximal 18 Schalter mit drei Positionen verarbeiten kann.

3 x 18 ist 54?

Mathematisch ja, schaltungstechnisch nein, denn die AUS-Stellung aller Schalter, wie auch immer man die festlegt, wird durch die 0 übertragen. Das wird nachher aus der Logik etwas klarer werden.

Meine X9E hat nur 6 3-Stellungsschalter eingebaut, was aber erst einmal völlig ausreichend ist.

Ich lege für mich selber fest, dass die Mittelstellung der Schalter AUS oder 0 bedeutet, und jeweils „nach oben“ (+) oder „nach unten“ (-) einer EIN-Stellung ist.
Jedem der Schalter wird nun eines der 32 Bit zugeordnet.
Schalter SA bekommt im niederwertigsten Byte das Bit 1 und 2, SB Bit 3 und 4, SC Bit 5 und 6 etc etc.
Ist Schalter 1 > 10 (SA+) wird Bit 1 auf 1 gesetzt, indem zu dem zu sendenden Wert 1 hinzuaddiert wird. Ist SA < 10 (SA-), wird dem Wert 2 hinzuaddiert. Bei SB sind es 4 und 8 etc etc.
Ist ein Schalter 0, also weder + noch - wird nichts unternommen. Soweit zur Theorie, im Code sieht das dann so aus:

Der zu übertragende Wert wird mit 0 überschrieben

channelValue = 0

Schalter SA wird auf + abgefragt

 

if (getValue('sa') > 10) then

Wenn SA+ ist, addiere 1 zur Variablen

channelValue = channelValue + 1 --0x01 00
end

Schalter SA wird auf - abgefragt

if (getValue('sa') < -10) then

Wenn SA- ist, addiere 2 zur Variablen

channelValue = channelValue + 2 --0x02 00
end

Gleiches dann für SB mit den Additionen von 4 und 8

if (getValue('sb') > 10) then
channelValue = channelValue + 4--0x04 00
end
if (getValue('sb') < -10) then
channelValue = channelValue + 8--0x08 00
end

usw für alle Schalter, die man übertragen will.

Im Anschluss an die Schalterauswertung wird dann mit

sportTelemetryPush(0x0D, 0x10, 0x01, channelValue)

der Wert zum Empfänger geschickt, der das auf dem S.Port weiterleitet.

Um ein paar Schalter zu übertragen, die das Licht schalten, muss das Script nicht ständig so schnell wie möglich die Daten zum Empfänger schicken. Wenn man den Schalter betätigt darf ruhig bis zu einer Sekunde beginnen.
Daher ist die Auswertung in eine Abfrage eingebettet, die prüft, ob seit dem letzten Durchlauf 500ms vergangen sind.

Was steht nun in channelValue drin?

Die Liste zeigt zur Vereinfachung nur die ersten zwei Byte der Variablen, und die zugehörigen Bits.

Wichtig ist zu verstehen, wo die Daten nachher liegen.

Byte 1

Byte2

...

Bit 8

Bit 7

Bit 6

Bit 5

Bit 4

Bit 3

Bit 2

Bit 1

Bit 8

Bit 7

Bit 6

Bit 5

Bit 4

Bit 3

Bit 2

Bit 1

...

SD-

SD+

SC-

SC+

SB-

SB+

SA-

SA+

 

 

 

 

SG-

SG+

SE-

SE+

...

Das minderwertigste Byte wird zuerst gesendet, steht also ganz links, dann folgen Byte 2-4 in aufsteigender Reihenfolge.

Die Bits innerhalb eines Byte stehen von rechts nach links.

  • Like 1

Share this post


Link to post
Share on other sites

So gehen wir auf die Arduino-Seite.

Wie eingangs geschrieben verwende ich eine Bibliothek von „Pawelsky“ (Link im ersten Post). Die verwende ich aber weitgehend dazu, die Kommunikation zum Empfänger herzustellen.

In der Bibliothek ist in der Klasse FrSkySportTelemetry eine Funktion send(), die das Senden von Daten übernimmt, aber auch den Datenempfang.
Da ich keine Daten senden möchte, sondern nur das, was nach Adresse 0x0D gesendet wird, empfangen, schreibe ich mir eine eigene Klasse, die Teile der send-Funktion verwendet.

Die Klasse habe ich FrSkySportTelemetryReader getauft, und die einzige Funktion ist receive().
Die ersten Anweisungen stammen aus der S.Port-Klasse. Sie prüfen ab, ob der der Serielle Port zum Empfänger „offen“ ist, und überhaupt Daten vorhanden sind. Ist beides der Fall wird ein Byte eingelesen.

uint8_t data= serial.port->read();

In der Datenübertragung vom Empfänger zu den Sensoren ist 0x7E das Startsignal, dass der Empfänger was zu sagen hat.

if (data == FRSKY_TELEMETRY_START_FRAME) {
dataCounter = 0;
available = false;
}

Diese Anweisungen prüfen, ob 0x7E empfangen wurde, was in der Definition FRSKY_TELEMETRY_START_FRAME hinterlegt ist. Ist das Startsignal empfangen, wird ein Zähler zurückgesetzt, und der Marker available auf false gesetzt.

Der Zähler dataCounter und der Marker available hängen mit der zu empfangenden Datenlänge ab.
Es werden insgesamt 10 Byte mit Daten erwartet, in denen dann die 4 Byte Nutzdaten liegen.
Solange ich nicht 10 Bytes in meinem Datenpuffer habe, macht es keinen Sinn, die Daten weiterzugeben.

Daher

available = false;

Die korrekte Anzahl Daten kann ich nur feststellen, wenn ich vom Startsignal aus zähle. Daher wird bei Empfang von 0x7E der Zähler zurückgesetzt.

dataArray[dataCounter] = data;
dataCounter++;

Dieser Teil schreibt die Daten in einen Puffer und zählt den Zähler um 1 hoch.

if ((dataCounter == 10) && (dataArray[1] == 0x0D)) {
	aUiRxValue[0] = dataArray[5];
	aUiRxValue[1] = dataArray[6];
	aUiRxValue[2] = dataArray[7];
	aUiRxValue[3] = dataArray[8];

	available = true;
}

Jetzt geht es an eine erste Auswertung. Wenn 10 Bytes empfangen wurden, und das zweite Byte den Wert 0x0D hat, werden die 4 Byte Nutzdaten in einen Ausgabepuffer geschrieben, und der Marker für verfügbare Daten auf true.

Share this post


Link to post
Share on other sites

Das mit dem Daten Senden wirst du wahrscheinlich doch noch erledigen müssen:

Es werden die Sensoren vom SPort regelmässig angesprochen, die auf ihre Anfrage auch Antworten:

sind also die ID's 2,5,12 Datenmässig vorhanden ist die die Abfragereihenfolge ( nach einigen Durchläufen )

1,2,5,12,2,3,5,12,2,4,5,12,2,5,6,12......

Wenn deine Adresse 14 ( 0D ) also öfter angesprochen werden soll muss diese auch regelmässig ANTWORTEN....

Ralf

 

ps. wie weit bist du mit der Impulsgeschichte für die Momo Sauerland Module

Edited by r41065

Share this post


Link to post
Share on other sites

Hab gerade einen Dauertest von über einer Stunde laufen und keine Probleme, und da sind in den S.Port ein Liposensor und der Drehzahlsensor mit eingeschleift.
Mit der Adresse muss man wahrscheinlich mehr aufpassen, dass man nicht einen realen Sensor verwendet, sondern einen, der eben nicht im System aktiv ist.

Share this post


Link to post
Share on other sites

Rückmeldung:

Habe das Script für meine X9Lite ( die hat weniger Schalter ) angepasst und beim Arduino Nano die Ausgänge direkt zum Schalten genutzt.

Funktioniert...

 

Danke für die Anregung

Ralf

Share this post


Link to post
Share on other sites
Am 31.8.2019 um 21:35 schrieb Meinolf Höhler:

channelValue = 0

if (getValue('sa') > 10) then

 

 

Hast Du das mal in größerem Rahmen probiert? Man kann (wenn ich mich richtig erinnere) nur 6 Schalter pro Lua Script abfragen. Läuft das noch wenn Du mehrere Scripte parallel fährst um mehr Schalter abzufragen?. Ich hatte den Lösungsansatz aus diesem Grund vor einiger Zeit verworfen und einen anderen Weg (über SBUS) gewählt soetwas zu realisieren.

Share this post


Link to post
Share on other sites

Leider nicht, aber ist mal eine Idee, die ich die Woche testen kann.

Warum sollte man nur 6 Schalter abfragen können? Der Schalter ist ja auch nur ein Wert von -100 - +100? Da müsste ich nochmal die Handbücher durchsuchen.

Share this post


Link to post
Share on other sites

irgendwo bin ich darüber gestolpert. Vielleicht ist die Grenze mittlerweile auch aufgehoben.

Meine, mittlerweile erprobte und vielfach eingesetzte Lösung, habe ich hier vorgestellt:

http://rc-network.de/forum/showthread.php/679660-SBUS-Switch?p=4538997#post4538997

Funktionsbeschreibung ab Beitrag #5
Die Auswertung der Kanäle kann man narürlich auch mit einem Arduino umsetzen.

Share this post


Link to post
Share on other sites

Hallo Ulf:

der SPort ist Bidirektional, ich habe schon ein 2. GPS und Spannungssensoren am SPort direkt im Sender angeschlossen.

Ich denke ein paar zusätzliche Schalter per MC einzulesen und als "Telemetriewert" zu senden  oder ein Lua Script welches auf dem Display zusätzliche Schalter simuliert sind die nächsten logischen Schritte um einen Multiswitch über diesen Weg zu realisieren....

Vorlagen für Eigenbau SPort Sensoren gibt es ja mittlerweile genug im www.

Ralf

Edited by r41065

Share this post


Link to post
Share on other sites

auf jeden Fall ein interessanter Ansatz.
Ich werde es weiter verfolgen....

PS:
Zumal ich meine Hardware auch für das SPort Protokoll adaptieren könnte. Mal schauen

Share this post


Link to post
Share on other sites

So weiter im Tex...Programm.

Das Programm im Arduino ist schwieriger zu erklären, daher versuche ich die wichtigsten Punkte abzudecken. Ich hoffe die Kommentierung des Quelltexts ist ausreichend.

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

Auch wenn das Programm funktioniert, ohne, dass der Sender in dem Arduino einen Sensor erkennt, muss trotzdem irgend einer der Sensoren instantiiert werden, damit die Telemetriebibliothek compiliert werden kann. Ich habe den Erstbesten genommen, und das ist der AirSpeedSensor ASS.

// configure telemetry serial port
telemetry.begin(FrSkySportSingleWireSerial::SOFT_SERIAL_PIN_4, &ass);

Das Telemetrieobjekt bekommt zwei Parameter übergeben. Der Erste ist der Pin, an dem der S.Port angeschlossen ist. Hier hat sich anscheinend Pin 4 als am besten geeignet (so ist es in dem Beispiel von Pawelsky beschrieben).

Der zweite Parameter ist der „Pseudo-Sensor“, der zu Anfang instantiiert wurde.

if (telemetry.available && ((uiValue[0] != telemetry.aUiRxValue[0])
                        || (uiValue[1] != telemetry.aUiRxValue[1])
                        || (uiValue[2] != telemetry.aUiRxValue[2])
                        || (uiValue[3] != telemetry.aUiRxValue[3]))) {
uiValue[0] = telemetry.aUiRxValue[0];
uiValue[1] = telemetry.aUiRxValue[1];
uiValue[2] = telemetry.aUiRxValue[2];
uiValue[3] = telemetry.aUiRxValue[3];

telemetry.available = false;
bValuesUpdated = true;
}

Weiter oben wurde ja schon das available Flag beschrieben, und das kommt hier nun zu seinem Einsatz.

Nur wenn Daten verfügbar sind, und sich eines der 4 Byte von den vorher empfangenen unterscheidet, genau dann werden die neuen Daten in den eigenen Puffer übernommen, und die Auswertung mit bValuesUpdated = true; freigegeben.

Wichtig für die nachfolgende Auswertung ist nun die Beziehung zwischen den Empfangenen Bytes und den LEDs an den PWM Modulen.

Für diese Beziehung habe ich eine Tabelle in LibreOffice gebaut, wo in den Spalten die 32 möglichen Schaltwerte stehen, und in den Zeilen die 32 Kanäle der PWM Module.

Nebenbei, die LED sind gleichmäßig auf die PWM Module verteilt, damit nicht eines zu viel Last auf einmal hat.

Wichtig ist nun, dass jedes der PWM Module einen Zwischenpuffer in Form eines Array aus 16 Booleanwerten bekommen hat, welcher den Zustand jeder LED speichert.

bool module1onOff[16];
bool module2onOff[16];

Kommt z.B. über den Empfänger das Signal für die Beleuchtung „in Fahrt“ (Byte 1 = 0x01), sollen die folgenden LED eingeschaltet sein:

- Topplicht (Modul 2, Kanal 0)
- Hecklicht (Modul 1, Kanal 0)
- Backbordlaterne (Modul 2, Kanal 2)
- Steuerbord (Modul 1, Kanal 2)

Um zu ermitteln, ob 0x01 in dem Signal des Empfängers vorhanden ist, anders ausgedrückt der gesendete Wert gerade oder ungerade ist, wird die Bitmaske 0x01 (binär 00000001) mit einem bitweisen UND auf den Puffer angewendet. Ist der Wert ungleich 0, ist der gewünschte Schalter an. Für die anderen Schaltzustände ist die Bitmaske entsprechend 0x02, 0x04, 0x08, 0x10 etc.

//fahrt
if ((uiValue[0] & 0x01) != 0) {
  module1onOff[0] = true; //heck
  module1onOff[2] = true; //navSB
  module2onOff[0] = true; //topp
  module2onOff[2] = true; //navBB
}

In den Arrays werden dann Wert 0 und 2 jeweils auf TRUE gesetzt.

Soll zusätzlich noch das Schlepplicht an sein (Byte 1 = 0x02), werden die Werte von oben, und zusätzlich jeweils Wert 1 auch auf TRUE für Topp- und Heckschlepplicht gesetzt.

Der Befehl, der die Werte in die Kanäle der PWM-Module schreibt benötigt als ersten Parameter die Nummer des Kanals. Da das Array genau so viele Werte hat, wie das Modul Kanäle hat, können die Werte über einfache for-Schleife geschrieben werden.

//write ledState to PWM modules
for (int i = 0; i < 16; i++) {
  if (module1onOff[i]) {
    pwmLed1.setPin(i, (uint16_t)pgm_read_word(&mappingModule1[i]), false);
  } else {
    pwmLed1.setPin(i, 0, false);
  }

  if (module2onOff[i]) {
    pwmLed2.setPin(i, (uint16_t)pgm_read_word(&mappingModule2[i]), false);
  } else {
    pwmLed2.setPin(i, 0, false);
  }
}

 

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

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.