Überblick und Einstieg in die Programmierung mit Windows Remote Arduino

Windows Remote Arduino – Einführung und erste Schritte
Kommentare

Massimo Banzis Arduino-Prozessrechner sind mittlerweile fast universell akzeptiert. Auch Microsoft macht durch das Anbieten diverser gemeinsamer APIs einen großen Schritt in Richtung der BanziMaten: Eine Anleitung für erste Schritte mit Windows Remote Arduino.

IoT-Anwendungen werden immer wichtiger: Der Erfolg von Christianus Kaars Runtastic ist ein großartiger Beweis dafür, dass Software-Start-ups durch das Anbieten von dedizierter Hardware an Wert gewinnen. Software lässt sich nämlich ohne Weiteres klonen, während der Nachbau von physikalischen Produkten erst ab einer gewissen Größe interessant wird – Stichwort: Salae.

Dedizierte Echtzeitbetriebssysteme haben in dieser neuen Welt einen schweren Stand. Android, iOS, Windows und/oder Linux bringen Entwickler eine Vielzahl von Hilfsdiensten, die bei der Implementierung zeitgeistiger Features hilfreich sind.

Hilfe, Fernsteuerung

Desktop- und Handybetriebssysteme sind für die Realisierung von Steuerungen denkbar schlecht geeignet: Wer einen Gadgeteer unter Last testet, kann bald ein Lied vom Garbage Collector und seinen Freunden singen. Der Arduino Yun begründete das Genre des kombinatorischen Prozessrechners – er kombinierte einen unter Linux laufenden „Rechenrechner“ mit einer primitiven und für Interrupt-Verarbeitung vorgesehenen Steuer-MCU.

Windows Remote Arduino ist eine vergleichsweise konservative Implementierung dieses Konzepts. Der aus drei Ebenen bestehende Protokollstack (Abb. 1) beginnt mit der RemoteWiring-Klasse, die auf Seiten der Windows-10-Workstation Wiring-Befehle entgegennimmt und in ein als Firmata bezeichnetes Protokoll übersetzt.

Die von MIDI abgeleitete Sprache gilt im Arduino-Bereich als Quasistandard für Fernsteuerungen; neben dem direkten Lesen und Setzen von Pins gibt es hier auch die Möglichkeit zum Festlegen von Events, die automatisch an den PC gemeldet werden.

Zu guter Letzt sorgt das eigentliche physikalische Übertragungssystem dafür, dass die Daten in Richtung des Prozessrechners wandern. Auf Seiten des Arduinos erfolgt das Entgegennehmen der Informationen durch eine Instanz von Serial – sie muss nicht unbedingt aus einer USB-Verbindung bestehen, sondern kann auch in Form eines Bluetoothmoduls oder sogar einer Socket-Verbindung bereitgestellt werden.

hanna_arduino1

Abb. 1: Das Kommunikationsinterface von Windows Remote Arduino ist vergleichsweise kompliziert

Windows Remote Arduino: erste Schritte

Der in der Einleitung erwähnte Arduino Yun erwies sich im Unternehmen des Autors als ambivalent, einzig das Einrichten der WLAN-Verbindung nimmt zugegebenermaßen etwas Zeit in Anspruch. Liegt dieses lästige Prozedere hinter einem, so funktioniert der Prozessrechner ohne Probleme.

Als ersten Schritt in die Welt von Windows Remote Arduino ist die Installation der Arduino-IDE erforderlich. Auf die Installation des Produkts gehen wir nicht ein, da sie an anderer Stelle hinreichend beschrieben ist. Danach folgt das Deployment von Visual Studio 2015; die hier gezeigten Schritte erfolgten auf einer Workstation mit Windows 10, als VS-Version diente die Community Edition von VS2015.

Unter Windows 8.1 laufende Applikationen sind – laut offizieller Aussage – auf die Kommunikation per TCP/IP oder Bluetooth LE beschränkt, die Nutzung von drahtgebundenem USB setzt Windows 10 voraus. Leider lässt sich das NuGet-Paket zum Zeitpunkt der Drucklegung in für Windows 8.1 vorgesehenen Solutions nicht deployen, wodurch Remote Arduino überhaupt nicht zur Verfügung steht – ob Microsoft hier nachbessert, ist fraglich.

Erstellen Sie ein neues Projekt auf Basis der Vorlage Windows | Universal | Blank App (Universal Windows). Nach dem erfolgreichen Abarbeiten des Projekterstellungsassistenten folgt ein Klick auf Tools | NuGet Package Manager | Package Manager Console, um die NuGet-Verwaltungskonsole zu öffnen. Scheitert die Installation nach Eingabe von Install-Package Windows-Remote-Arduino, so können Sie das Paket Windows Remote Arduino über den grafischen Paketmanager installieren. Wer die Kommunikation per Netzwerk bevorzugt, muss nun noch einige Capabilities anlegen:

<Capability Name="privateNetworkClientServer"/>
<Capability Name="internetClientServer"/>

Unter Windows 10 ist für USB die Capability serialcommunication erforderlich:

<DeviceCapability Name="serialcommunication">
  <Device Id="any">
    <Function Type="name:serialPort"/>
  </Device>
</DeviceCapability>

Die hier nicht weiter besprochene Arbeit mit Bluetooth setzt unter Windows 8 und Windows 10 verschiedene Capability-Strings voraus, die in Tabelle 1 aufgelistet sind.

tab1

Tabelle 1: Capability-String für Bluetooth im Vergleich

Zu guter Letzt muss unter Windows 8.1 der <Package>-Tag der Manifestdatei um einen Verweis auf den Namespace xmlns:m2=“http://schemas.microsoft.com/appx/2013/manifest“ ergänzt werden, sofern er nicht schon vorhanden ist.

Wie schnell ist er?

Das als Beispiel verwendete Blinkenlassen einer LED taugt auch als kleiner Geschwindigkeitstest für die Kommunikation zwischen Prozessrechner und Workstation. Öffnen Sie die Arduino-IDE und ersetzen Sie den von Haus aus angezeigten Scratch durch folgenden Code.

Es handelt sich dabei um eine mehr oder weniger komplette Implementierung des Firmata-Protokolls in C – wer die Grundlagen der Programmierung von Prozessrechnern der Bauart Arduino beherrscht, kann das Protokoll anhand des Codes reimplementieren.

Das auf GitHub bereitstehende Sketch unterscheidet sich von seinem mit der Arduino-IDE ausgelieferten Kollegen insofern, als es statt Serial den Port Serial1 verwendet. Zudem wird das Hochfahren des Linux-Teils abgewartet. Sendet der AVR zu früh Daten an das Linuxsystem, scheitert der Bootprozess.

Vor der eigentlichen Programmausführung muss der Linux-Teil des Yun darüber informiert werden, dass er die serielle Konsole der AVR-MCU über das Netzwerk ansprechbar machen muss. Verbinden Sie sich per Putty mit dem Yun, und loggen sie sich mit root/arduino ein. Als nächster Akt folgt das Deployment der ser2net-Brücke, die serielle Ports per TCP/IP ansprechbar macht:

opkg update 
opkg install ser2net 
opkg install nano

Die eigentliche Konfiguration findet sich in der Datei /etc/ser2net.conf, die an die neue Situation angepasst werden muss. Wer unter akuter Allergie gegen vi(m) leidet, erledigt die Aufgabe entweder per nano oder über WinSCP. Löschen Sie den gesamten Inhalt der Datei, und ersetzen Sie ihn durch die Zeile 5055:raw:0:/dev/ttyATH0:115200.

Im Moment gibt es einen kleinen Konflikt zwischen YunBridge und der Firmata-Bibliothek: Beide nutzen denselben seriellen Link zwischen AVR und ARM-MCU. Zur Behebung dieses Problems wird die Datei /etc/inittab geöffnet und die im Folgenden gezeigte letzte Zeile mittels Voranstellen von # ausgeknockt:

::sysinit:/etc/init.d/rcS S boot
::shutdown:/etc/init.d/rcS K shutdown
#ttyATH0::askfirst:/bin/ash –login

Achten Sie darauf, dass ein derartig konfigurierter Prozessrechner nicht mehr zur Ausführung von Bridge-basierten Sketches befähigt ist. Bei gemeinsam oder selten genutzten Planaren ist es ratsam, dies durch Ankleben eines Warnzettels zu vermerken.

ser2net startet im Moment nur bei manueller Aufforderung. Öffnen Sie die Datei /etc/rc.local, und fügen Sie den Befehl ser2net vor dem Befehl exit 0 ein. Starten Sie den Prozessrechner daraufhin neu – ser2net wird den Port 5055 öffnen, was sich durch einen beliebigen Telnet-Client überprüfen lässt.

Programmatische Spiele

Ein mit der Standard-Firmata-Firmware ausgestatteter Arduino ist eine Art GPIO-Karte; er wartet auf vom Rechner eingehende Steuerbefehle und weist keine Eigenintelligenz auf. Kehren Sie zum Visual-Studio-Projekt zurück und passen Sie den Konstruktor von MainPage() wie in Listing 1 zu sehen an.

public sealed partial class MainPage : Page
{
  Microsoft.Maker.RemoteWiring.RemoteDevice myArduino;
  Microsoft.Maker.Serial.NetworkSerial mySerial;
  public MainPage()
  {
    this.InitializeComponent();
    mySerial = new Microsoft.Maker.Serial.NetworkSerial(new Windows.Networking.HostName("192.168.1.33"), 5055);
    myArduino = new Microsoft.Maker.RemoteWiring.RemoteDevice(mySerial);
    mySerial.ConnectionEstablished += MySerial_ConnectionEstablished; ;
    mySerial.ConnectionFailed += MySerial_ConnectionFailed; ;
    mySerial.begin(115200, Microsoft.Maker.Serial.SerialConfig.SERIAL_8N1);
  }

Windows Remote Arduino besteht aus zwei Klassengruppen. Die eigentliche Implementierung des Wiring-API liegt in der RemoteDevice-Klasse, die durch eine Instanz von Microsoft.Maker.Serial Kontakt mit dem fernzusteuernden Prozessrechner aufnimmt. Je nach verwendeter Schnittstelle kommt eine andere Variante der Klasse zum Einsatz. Damit beschäftigen wir uns en Detail im nächsten Abschnitt.

Im Fall des erfolgreichen Aufbaus einer Verbindung folgt ein Aufruf von ConnectionEstablished. Da wir hier einen reinen Benchmark realisieren, spawnen wir keinen neuen Thread – der Event Handler bekommt eine Endlosschleife eingeschrieben. Das ist für uns nur insofern relevant, als der Speicher irgendwann ausgeht: In Tests des Autors hat man zwischen fünf und zehn Minuten Zeit für Messungen (Listing 2).

private void MySerial_ConnectionEstablished()
{
  myArduino.pinMode(13, Microsoft.Maker.RemoteWiring.PinMode.OUTPUT);
  for (;;)
  {
    myArduino.digitalWrite(13, Microsoft.Maker.RemoteWiring.PinState.LOW);
    myArduino.digitalWrite(13, Microsoft.Maker.RemoteWiring.PinState.HIGH);
    myArduino.digitalWrite(13, Microsoft.Maker.RemoteWiring.PinState.LOW);
    myArduino.digitalWrite(13, Microsoft.Maker.RemoteWiring.PinState.HIGH);
    myArduino.digitalWrite(13, Microsoft.Maker.RemoteWiring.PinState.LOW);
    myArduino.digitalWrite(13, Microsoft.Maker.RemoteWiring.PinState.HIGH);
  }
}

Kenner des Arduino-API wundern sich hier höchstens über den „C#-Wrapper“ … es handelt sich um das normale Wiring-API. Normalerweise entsteht bei der Abarbeitung eines derartigen Programms eine charakteristische und aus drei Wellenzügen bestehende Form: Die „längere“ High-Periode erlaubt normalerweise das Ermitteln der für die Abarbeitung der for-Schleife notwendigen Zeit.

Ob der diversen anderen Unwägbarkeiten (Stichworte Latenz und Garbage Collector) in der Welt des Windows Remote Arduino sieht die am Oszillographen angezeigte Wellenform weniger klar aus (Abb. 2). Die durch den LeCroy 9354AM durchgeführten Messungen (Abb. 3) erlauben die Abschätzung von Varianz und Arbeitsfrequenz.

hanna_arduino_2

Abb. 2: Wellenform und Wellenzug im Groben …

hanna_arduino_3

Abb. 3: … und unter mathematischer Analyse

Die im Rüstungsbereich weit verbreitete Gerätegattung des Modulationsdomänenanalysators erlaubt uns, das Thema von der Rückseite aufzusatteln. Sie arbeiten wie ein Oszilloskop, das statt der Spannung den Frequenzverlauf über die Zeit diskret oder gestaffelt anzeigt.

Abbildung 4 zeigt ein Histogramm, das durch wiederholte Bewertung der ermittelten Zeitspanne entsteht. Ob der durchschnittlichen Arbeitsfrequenz von weniger als zwei Kilohertz ist klar, dass der Arduino für Echtzeitanwendungen so nicht geeignet ist.

hanna_arduino_4

Abb. 4: Dank des HP 53310A sehen wir klarer – sonderlich stabil ist das Signal nicht

Mach es per USB

Als Nächstes wollen wir den Versuch mit einem per USB verbundenen Arduino Uno wiederholen. Diesmal diente ein von GearBest zur Verfügung gestellter TECLAST X98 Pro als Basishardware – die Verbindung zum Prozessrechner erfolgte über ein MicroUSB-USB-Kabel, das der Autor bei einem slowakischen Elektronikshop für kleines Geld erwerben konnte.

I’m roaming in the Lab!

Wer seine Workstation frei von Windows 10 halten möchte, kann auf den Remote Debugger zurückgreifen. Der TECLAST liegt dabei auf dem Messwagen, während die eigentliche Programmierung auf der unter Windows 8.1 laufenden Workstation erfolgt.

Der wichtigste Unterschied zum vorigen Beispiel ist, dass wir den zu verwendenden Prozessrechner ermitteln müssen. Die USBSerial-Klasse bietet dafür eine Vielzahl verschiedener Methoden an – unser Konstruktor beschränkt sich darauf, aus den angebotenen Geräten den ersten Arduino Uno herauszupuhlen (Listing 3).

public sealed partial class MainPage : Page
{
  Microsoft.Maker.RemoteWiring.RemoteDevice myArduino;
  Microsoft.Maker.Serial.UsbSerial mySerial;

  async private void doStuff()
  {
    DeviceInformationCollection devCol = await Microsoft.Maker.Serial.UsbSerial.listAvailableDevicesAsync();
    foreach(var x in devCol)
    {
      if (x.Name.Contains("rduino"))
      {
        mySerial = new Microsoft.Maker.Serial.UsbSerial(x);
        break;
      }
    }

    myArduino = new Microsoft.Maker.RemoteWiring.RemoteDevice(mySerial);
    mySerial.ConnectionEstablished += MySerial_ConnectionEstablished; ;
    mySerial.ConnectionFailed += MySerial_ConnectionFailed; ;
    mySerial.begin(57600, Microsoft.Maker.Serial.SerialConfig.SERIAL_8N1);
  }

Da die listAvailableDevices-Methode asynchron ist, bauen wir den Konstruktor zweistufig auf. Eine weitere interessante Änderung ist die Baudrate, die nun nur 57 600 bps beträgt – der StandardFirmata-Sketch geht die Gesamtsituation etwas gemächlicher an:

public MainPage()
{
  this.InitializeComponent();
  doStuff ();
}

Microsoft erleichtert Entwicklern die Arbeit insofern, als das Connection-Event auch beim erfolgreichen Aufbau einer USB-Verbindung auftritt. Aus diesem Grund sind keine großen Änderungen im Bereich des restlichen Codes erforderlich.

Arduino Unos unterscheiden sich insofern vom Yun, als die Verbindung zwischen Computer und MCU hier direkt ist. Das bedeutet, dass der im vorigen Abschnitt verwendete Sketch hier nicht funktioniert. Öffnen Sie stattdessen die unter Datei | Beispiele | Firmata | StandardFirmata angebotene Version des Sketches, und jagen Sie sie in Richtung des Arduino Uno.

Das Resultat ist das in Abbildung 5 gezeigte Histogramm. Mag die generierte Frequenz ob der langsamen Baudrate auch geringer sein, so ist die dank der direkten Kabelverbindung höhere Stabilität erwähnenswert.

hanna_arduino_5

Abb. 5: Die Form des Histogramms zeigt höhere Stabilität

Fortgeschrittene Aufgaben

An dieser Stelle ist es Zeit für einen weiteren Standardsatz des Autors: Pingpong mag ein lustiges Spiel sein, hat in der Welt der Informatik aber nichts verloren. Wer die einzelnen Pins eines Prozessrechners aus der Visualisierungssoftware heraus anspricht, macht so gut wie immer einen Fehler: Steuerung gehört auf die dafür vorgesehene MCU.

Das in Firmata realisierte Absetzen von Kommandos an I2C-Geräte ist für uns insofern uninteressant, als es ein Spezifikum des Sendens generischer Befehle darstellt. Wir nutzen stattdessen die Sysex-Funktion, die das Übertragen von mehr oder weniger beliebigen Kommandos samt Parametern erlaubt.

StandardFirmata enthält mit der Methode sysexCallback schon einen Korpus, der das Handling von Befehlen demonstriert. Von Haus aus nutzt der Sketch das sysex-Feature vor allem zur Realisierung des I2C-Interface:

void sysexCallback(byte command, byte argc, byte *argv)
{
  …
  switch (command) {
    case I2C_REQUEST:
    …

Ärgerlicherweise gibt es in der aktuellen Implementierung des sysex-Befehls einen Microsoft-bekannten Bug – der Arduino nimmt einige Sekunden nach dem im Rahmen des Verbindens erfolgenden Gesamtresets keine Nachrichten am seriellen Port entgegen: Das Senden funktioniert witzigerweise auch einige Zeit später ohne jedes Problem.

Aufgrund dieser technischen Schwäche müssen wir uns darauf beschränken, Informationen vom Prozessrechner in Richtung der Workstation zu schicken. Wer Daten auf Firmata-Protokollebene austauschen möchte, muss den Konstruktor des Programms nach dem in Listing 4 gezeigten Schema anpassen – die myArduino-Klasse wird nun auf Basis der Firmata-Instanz errichtet, die ihrerseits mit den Daten zum zu verwendeten Datenstrom bevölkert wird.

Microsoft.Maker.RemoteWiring.RemoteDevice myArduino;
Microsoft.Maker.Serial.UsbSerial mySerial;
Microsoft.Maker.Firmata.UwpFirmata myFirmata;
async private void doStuff()
{
  …
  myFirmata = new Microsoft.Maker.Firmata.UwpFirmata();
  myArduino = new Microsoft.Maker.RemoteWiring.RemoteDevice(myFirmata);
  myFirmata.begin(mySerial);
  myFirmata.StringMessageReceived += MyFirmata_StringMessageReceived;
  mySerial.ConnectionEstablished += MySerial_ConnectionEstablished;
  mySerial.ConnectionFailed += MySerial_ConnectionFailed; ;
  mySerial.begin(57600, Microsoft.Maker.Serial.SerialConfig.SERIAL_8N1);
}

Firmata erlaubt dem Prozessrechner das Absetzen von Strings, die 1:1 in der .NET Runtime ankommen. Wir schreiben eingehende Zeichenketten direkt in die Debuggerkonsole – in komplexeren Systemen werden sie auf die eine oder andere Art weiterverarbeitet:

private void MyFirmata_StringMessageReceived(Microsoft.Maker.Firmata.UwpFirmata caller, Microsoft.Maker.Firmata.StringCallbackEventArgs argv)
{
  Debug.WriteLine(argv.getString());
}

Timer ohne Interrupt

In sehr kleinen Steuerungen realisiert man Warte- bzw Totzeiten durch kurzfristiges Einschläfern der MCU. Bei Systemen auf Basis von Protokollen wie Firmata ist diese Vorgehensweise geradezu letal – schläft der Prozessrechner während Daten ankommen, so geht die Synchronisation im Laufe der Zeit verloren.

Normalerweise lösen Entwickler dieses Problem durch Nutzung von Interrupts bzw. Timern. Unsere Firmata-Implementierung macht uns hier insofern einen Strich durch die Rechnung, als das Protokoll die gesamten Hardwareressourcen kleinerer Arduinos in Anspruch nimmt.

Im Arduino-Bereich greift man in dieser Situation gerne auf die von Marcello Romani entwickelte SimpleTimer-Bibliothek zurück. Installieren Sie die Bibliothek im ersten Schritt in Ihre Installation der Arduino-IDE und öffnen Sie sodann den Firmata-Sketch zur weiteren Bearbeitung.

SimpleTimer arbeitet nach dem indirekten Polling-Prinzip. Das bedeutet, dass die Methode Rechenzeit „abgreift“ und anhand der vom Betriebssystem automatisch inkrementierten millis()-Methode ihre diversen Callbacks abfeuert. Die Zuweisung der Ressourcen erfolgt in Form von loop():

void loop()
{
  byte pin, analogPin;
  timer.run();

Für die Vorhaltung der benötigten Informationen legen wir eine globale Instanz der SimpleTimer-Klasse an. Bei dieser Gelegenheit können wir auch die Erstellung der Callback-Methode erschlagen, die später vom Timer aufgerufen wird:

#include &amp;lt;SimpleTimer.h&amp;gt;
…

SimpleTimer timer;
void repeatMe() 
{
  Firmata.sendString("Hello World! vom Arduino!");
}

Damit fehlt die Initialisierung, die wir aus Gründen der Bequemlichkeit direkt in der Haupt-setup()-Methode des Sketches erledigen:

void setup()
{
  timer.setInterval(1000, repeatMe);

Führen Sie das Programm im nächsten Schritt auf dem Prozessrechner aus und erfreuen Sie sich an den in der Debuggerkonsole aufscheinenden Meldungen.

Fazit

Satya Nadellas erklärtes Ziel ist das Unterstützen von Entwicklern, die mit ihren Systemen die reale und die virtuelle Welt verbinden wollen. Die komfortable Einbindung von Arduinos mag isoliert gesehen keine besonders bedeutende Errungenschaft darstellen – wer die Situation als Ganzes betrachtet, sieht sie komplett anders. Kleine Steuerungsaufgaben – Stichwort Smart Home – lassen sich nun ohne aufwendiges Hardwaredesign bewerkstelligen; ein um wenige Euro erhältlicher Arduino Uno löst das Problem.

Windows Developer

Windows DeveloperDieser Artikel ist im Windows Developer erschienen. Windows Developer informiert umfassend und herstellerneutral über neue Trends und Möglichkeiten der Software- und Systementwicklung rund um Microsoft-Technologien.

Natürlich können Sie den Windows Developer über den entwickler.kiosk auch digital im Browser oder auf Ihren Android- und iOS-Devices lesen. In unserem Shop ist der Windows Developer ferner im Abonnement oder als Einzelheft erhältlich.

Unsere Redaktion empfiehlt:

Relevante Beiträge

Meinungen zu diesem Beitrag

X
- Gib Deinen Standort ein -
- or -