JavaScript: Hardware mit Cylon.js steuern

Seite 2: Grundgerüst

Inhaltsverzeichnis

Ist alles eingerichtet, lässt sich eine einfache Node.js-Anwendung schreiben, die eine an das Arduino-Board angeschlossene LED zum Blinken bringt. Dazu gilt es zunächst, das Cylon.js-Modul in die eigene Anwendung zu importieren. Das erfolgt, wie unter Node.js üblich, mit der require-Funktion:

const Cylon = require('cylon');

Danach ist ein eigener "Roboter" zu implementieren – so bezeichnet Cylon.js jede Anwendung, die Geräte steuert. Benötigt wird dafür die robot-Funktion, die eine entsprechende Instanz erstellt und an den Aufrufer liefert. Die für den Roboter gewünschten Eigenschaften sind ihr in Form eines Konfigurationsobjekts zu übergeben. Mit der start-Funktion lässt sich der Roboter im Anschluss aktivieren:

Cylon.robot({
// ...
}).start();

Innerhalb des Konfigurationsobjekts ist die work-Funktion das wichtigste Element, da sie die eigentliche Logik enthält. Dort lassen sich beispielsweise Event-Handler oder zeitgesteuerte Aktivitäten definieren.

Für Letzteres genügen prinzipiell die in JavaScript enthaltenen Funktionen setTimeout und setInterval. Alternativ stellt Cylon.js after und every zur Verfügung, die in Verbindung mit der an den Number-Konstruktor angehängten Funktionen second und seconds für eine bessere Lesbarkeit sorgen sollen. Damit lässt sich beispielsweise statt

setInterval(() => {
// ...
}, 1 * 1000);

das vermeintlich besser lesbare

every((1).second(), () => {
// ...
});

schreiben. Was auf den ersten Blick nach hilfreicher Unterstützung aussieht, wirkt auf den zweiten reichlich fragwürdig. So ist beispielsweise die Angabe der Sekunden in Klammern zu setzen, da JavaScript den näherliegenden Ausdruck

1.second();

als Dezimalzahl zu interpretieren versucht und daran scheitert. Auch das Erweitern des Number-Konstruktors und das Einführen der globalen Bezeichner after und every wirkt wenig überzeugend. Da der Ausdruck länger ist als der native Code und die für viele Entwickler gängige Reihenfolge der Parameter umkehrt, empfiehlt es sich, auf den Einsatz der Funktionen zu verzichten.

Deutlich hilfreicher, aber ebenso fragwürdig implementiert, sind die Funktionen fromScale und toScale, mit denen sich Werte einer beliebigen Skala in Werte zwischen 0 und 1 und zurück umwandeln lassen:

const value = (150).fromScale(0, 200);

console.log(value); // => 0.75

Eine work-Funktion für einen Roboter, die einen Text wiederholt auf die Konsole ausgibt, könnte wie folgt aussehen:

Cylon.robot({
work () {
setInterval(() => {
console.log('Hello from Cylon.js :-)');
}, 1 * 1000);
}
}).start();

Da das reine Ausgeben eines Texts auf der Konsole im Kontext des Internet der Dinge unspektakulär ist, gilt es als nächstes, eine Verbindung zum Arduino-Board herzustellen. Dazu dient die Eigenschaft connections des Konfigurationsobjekts. Ihr ist ein weiteres Objekt zuzuweisen, das seinerseits wiederum die Verbindungsdaten zum Board enthält. Dabei ist der firmata-Adapter und die verwendete Adresse anzugeben:

Cylon.robot({
connections: {
arduino: { adaptor: 'firmata', port: '/dev/ttyACM0' }
},

work () {
// ...
}
}).start();

Ein mit Cylon.js definierter Roboter steuert das Arduino-Board nie direkt an, sondern nur daran angeschlossene Geräte, beispielsweise eine LED. Um sie zu definieren, ist die zusätzliche Eigenschaft devices zu konfigurieren:

Cylon.robot({
connections: {
arduino: { adaptor: 'firmata', port: '/dev/ttyACM0' }
},

devices: {
led: { driver: 'led', pin: 13, connection: 'arduino' }
},

work () {
// ...
}
}).start();

Konfigurierte Geräte stehen nun innerhalb der work-Funktion an der this-Eigenschaft zur Verfügung, etwa als this.led. Die Wahl der Namen arduino und led ist übrigens frei: Ausschließlich das angehängte Objekt bestimmt die Art der Verbindung und des Geräts. So ist es auch ohne Weiteres möglich, mehrere Arduino-Boards zu verbinden und sie mit eindeutigen Namen anzusprechen.

Das Objekt, das sich über this.led steuern lässt, stellt unter anderem eine toggle-Funktion zur Verfügung. Entwickler können mit ihr den Zustand der LED ändern:

Cylon.robot({
// ...

work () {
setInterval(() => {
this.led.toggle();
}, 1 * 1000);
}
}).start();

Da die Handhabung von this vor der Verfügbarkeit von ECMAScript 2015 in Verbindung mit Callbacks fehleranfällig war, bietet Cylon.js alternativ den Parameter my für work an, der this ausgibt. Das Vorgehen sei an der Stelle lediglich der Vollständigkeit halber erwähnt, da es dank der Fat-Arrow-Funktionen nicht mehr erforderlich ist:

work (my) {
setInterval(() => {
my.led.toggle();
}, 1 * 1000);
}

Ähnlich lässt sich beispielsweise eine Parrot AR.Drone starten und landen:

Cylon.robot({
connections: {
ardrone: { adaptor: 'ardrone', port: '192.168.1.1' }
},

devices: {
drone: { driver: 'ardrone', connection: 'ardrone' }
},

work () {
this.drone.takeoff();

setTimeout(() => {
this.drone.land();
}, 10 * 1000);

setTimeout(() => {
this.drone.stop();
}, 15 * 1000);
}
}).start();