Kickstage Logo
Default post hero

Javascript für Embedded Computing – Gut genug?

IoT
Nikola Jerković
Nikola Jerković
,
Javascript For Embedded Computing - Good Enough?

Das „Warum?“ und das „Wie?“

Seit seiner Entstehung vor Jahrzehnten ist Javascript weit über die Horizonte der ursprünglich beabsichtigten Nutzung seines Schöpfers hinausgewachsen. Am bemerkenswertesten war hierbei NodeJS, das die Ausführung von Javascript-Code ohne Browser ermöglichte. Obwohl es primär in Verbindung mit dem Express-Framework für Webserver genutzt wird, ermöglicht es auch die Erstellung von Desktop-Anwendungen und – mit Hilfe einiger plattformspezifischer Bibliotheken – die Entwicklung von Embedded-Anwendungen.

Obwohl Embedded-Plattformen heute alles von winzigen 8-Bit-Chips bis hin zu ausgewachsenen Einplatinencomputern mit Linux umfassen, konzentriert sich dieser Blogpost auf einen Mittelweg, der den meisten Menschen seit Jahren in irgendeiner Form zugänglich ist.

Die erste Generation des Raspberry Pi gab der Embedded-Bastler-Community Starthilfe, indem sie einen günstigen Linux-Computer mit großartigen IO-Fähigkeiten und Support bot. Auch wenn es neuere und leistungsfähigere Versionen des Raspberry Pi gibt, verwenden wir die erste Generation, da sie immer noch günstiger und in verschiedenen Formen verfügbar ist. Ein weiterer Faktor ist die Single-Threaded-Limitierung von NodeJS und der unfaire Vorteil, den eine Multi-Core-CPU anderen Sprachen verschaffen könnte, wenn deren Bibliotheken im Hintergrund Multi-Threading nutzen.

Alles, was Sie also brauchen, um mit Javascript an Elektronik zu basteln, ist ein Computer mit soliden IO-Anschlüssen, der Linux ausführen kann und genügend Speicher für die Node-Installation hat. Sobald dies möglich war, war es nur eine Frage der Zeit, bis es jemand tat – einfach nur, um zu sehen, ob es geht. Wenige Leute hielten jedoch inne, um sich zu fragen, ob sie es tun sollten.

Mach das Licht an und aus... wirklich schnell

Der allererste Code-Schnipsel, der in jedem Embedded-Tutorial gelehrt wird, ist Blink. Es ist der erste Schritt zur physischen Manifestation des Willens des Codes: eine einfache Änderung eines digitalen Ausgangs. Wie schnell diese Änderungen vom Code zum Ausgang gelangen können, ist meist wichtig und oft ein guter Indikator für die allgemeine Systemleistung. Je schneller ein System ist, desto schneller kann es auf seine IO zugreifen und diese ändern. Alles, was man braucht, um dies zu messen, ist ein Rechtecksignal, das durch das Umschalten eines Pins in einer Schleife ohne Verzögerung erzeugt wird – und, falls Sie das Glück haben, eines zu besitzen, ein Oszilloskop.

Blinken mit Javascript

Um einen digitalen Ausgang in Javascript zu steuern, wird ein GPIO-Paket benötigt. Zwei häufig verwendete sind rpio, ein plattformspezifisches Paket für den Raspberry Pi, und onoff, das die Linux sysfs GPIO-Schnittstelle nutzt und theoretisch auf jedem Linux-Board (BeagleBone, Banana Pi, ...) laufen kann.

Mit dem „generischen“ onoff-Paket würde ein einfaches Blink-Programm etwa so aussehen:

1const Gpio = require('onoff').Gpio;
2const pin = new Gpio(17, 'out');
3
4process.on('SIGINT', _ => {
5 pin.unexport();
6 });
7
8while (true) {
9 pin.writeSync(1);
10 pin.writeSync(0);
11}

Dies erzeugt eine Wellenform, wie sie auf dem Oszilloskop zu sehen ist.

blink_onoff_normal

Drei Dinge fallen auf:

  1. Die gemessene Ausgangsfrequenz liegt bei etwa 12,5 kHz, was für eine 700 MHz CPU zwar nicht schrecklich, aber auch nicht großartig ist.
  2. Die Wellenform ist hinsichtlich der Periode etwas unregelmäßig, was darauf zurückzuführen sein könnte, dass Linux andere Aufgaben ausführt (dies tritt auch bei schnelleren Sprachen auf).
  3. Die Spannungsspitzen bei jedem Impuls. Das ist normal, da der Pin nicht belastet oder gefiltert war, und man sieht tatsächlich, wie das Signal „nachschwingt“ (Ringing), bevor es sich stabilisiert.

Das obige Beispiel hat in einem einzigen Schleifendurchlauf geschrieben und die gesamte Rechteckwelle ausgeführt. Sehen wir uns an, was passiert, wenn wir die Schleife so ändern, dass sie den Pin-Wert liest und invertiert.

1while (true) {
2 pin.writeSync(pin.readSync() ^ 1);
3}


look_onof_single

Wir sehen, dass die Wellenform nun stark deformiert ist, mit einem Unterschied von 30 % zwischen High- und Low-Phasen, und die Frequenz auf mickrige 5 kHz gefallen ist. Hoffentlich kann ein plattformspezifisches Paket wie rpio den Tag retten.

1const rpio = require('rpio');
2
3rpio.init({mapping: 'gpio'})
4
5rpio.open(17, rpio.OUTPUT, rpio.LOW)
6
7while (true) {
8 rpio.write(17, 1);
9 rpio.write(17, 0);
10}

Auch hier führen wir zunächst High und Low in einem einzigen Schleifendurchlauf aus.

blink_rpiojs_linux


blink_rpiojs_single_period

Tatsächlich ist der Leistungszuwachs drastisch und erreicht mehr als das Fünffache der Frequenz. Dennoch ist die Wellenform nicht symmetrisch, und auch die Zeit, die Linux für seine eigenen Verwaltungsaufgaben („Housekeeping“) benötigt, ist viel deutlicher wahrnehmbar, wenn man mehr Impulse in einem Zeitrahmen unterbringen muss.

Ersetzt man die Schleife durch Lesen und Invertieren, sinkt die Leistung erneut, wenn auch nicht so drastisch wie bei onoff.


1while (true) {
2 rpio.write(17, rpio.read(17) ^ 1);
3 }


Die Ausgangsfrequenz fällt nur auf etwa 31 kHz, was jedoch immer noch nicht optimal ist.


Angebote von einigen Publikumslieblingen

Go ist eine Sprache mit Google-Funding und einer soliden Community im Rücken, daher überrascht es nicht, dass sie ordentlichen Raspberry Pi-Support bietet, einschließlich einer eigenen Version des rpio-Moduls.

1package main
2
3import "github.com/stianeikeland/go-rpio/v4"
4
5func main() {
6 rpio.Open()
7 defer rpio.Close()
8
9 pin := rpio.Pin(17)
10 pin.Output()
11
12 for {
13 pin.Write(rpio.High)
14 pin.Write(rpio.Low)
15 }
16}

Dieser kleine Schnipsel erzeugt eine sehr sauber aussehende, symmetrische Wellenform.

Und der Frequenzsprung ist signifikant bei 1,28 MHz. Der Wechsel zu einer Lese-und-Invertier-Schleife:

1for {
2 pin.Write((pin.Read() + 1)%2)
3}

resultiert in einer Ausgangsfrequenz von 1 MHz, was im Vergleich zur Javascript-Implementierung kaum ein Einbruch ist.

Nachdem wir die modernen Herausforderer abgehandelt haben, ist es Zeit für den Großvater von allen: C. Als die bei weitem beliebteste Embedded-Sprache ist sie seit den frühen Tagen des Computings ein fester Bestandteil der Programmierung und immer noch ein mächtiges Werkzeug für diejenigen, die die Geduld haben, manuell das zu tun, was die meisten Sprachen einem gar nicht erst erwähnen.

Es gibt saubere und einfache Wege, IO in C für den Raspberry Pi zu handhaben, wie etwa die WiringPi-Bibliothek. Aber wir wollen Geschwindigkeit, und für Geschwindigkeit will man direkten Zugriff auf die Register, die das IO steuern. Diese Art des Zugriffs ist nicht in einer schönen Bibliothek verpackt und macht den Code-Schnipsel für diesen Blogpost etwas zu groß, daher verweise ich auf einen gut dokumentierten Schnipsel auf eLinux, den Sie für Ihre Bedürfnisse kürzen können. Das Ergebnis ist beeindruckend.

c blink

Die Frequenz des Ausgangs beträgt 13,9 MHz und ist tatsächlich so schnell, dass das Signal keine Zeit hat, sich in eine Rechteckform zu stabilisieren und sich beim Zustandswechsel noch in der Phase des „Ringings“ (Einschwingens) befindet. Lesen und anschließendes Invertieren lässt die Ausgangsfrequenz auf 3,7 MHz sinken, aber das ist immer noch bei weitem schneller als jede andere getestete Sprache.

Aus reiner Neugier wurde C auch mittels sysfs-Zugriff getestet, genau wie das onoff npm-Paket. Dies resultierte in Ausgangsfrequenzen von 130 kHz für normale und 59 kHz für invertierende Blink-Schleifen. Schrecklich im Vergleich zum direkten Zugriff, aber immer noch zehnmal schneller als Javascript mit demselben Ansatz. Bitte beachten Sie, dass das sysfs-Beispiel im eLinux-Code die IO-Datei für jeden digitalen Schreibvorgang öffnet und schließt, was schädlich für die Performance ist und durch das standardmäßige Öffnen der Datei beim Programmstart und Schließen beim Programmende ersetzt werden sollte.

Zusammenfassend sieht die finale Leistungstabelle so aus:

Blink – Schreibe 0, Schreibe 1

SpracheLibraryAusgangsfrequenz
Javascriptonoff12,5 kHz
Javascriptrpio69.4 kHz
GOrpio1,28 Mhz
cregister access13,9 Mhz
Csysfs130 kHz


Blink - read and invert

SpracheLibraryAusgangsfrequenz
Javascriptonoff5 kHz
Javascriptrpio31,6 kHz
GOrpio1 Mhz
Cregister access3,7 MHz
Csysfs59 kHz


Ein letztes Wort

Das alte Sprichwort sagt: Wenn man nur einen Hammer hat, sieht alles wie ein Nagel aus. Das ist in der Programmierung oft der Fall, da Entwickler zögern, eine neue Sprache mitsamt ihren Eigenheiten und Problemen zu lernen, wenn dieselbe Aufgabe mit einer Sprache erledigt werden kann, die sie bereits kennen. Der Boom der Webentwicklung in den letzten Jahren bedeutet, dass diese Sprache für viele Menschen Javascript ist. Sollten sie also anfangen, Schrauben zu hämmern? Vielleicht.

Obwohl der oben demonstrierte Leistungsunterschied verheerend wirkt, muss er relativiert werden. Wenn Sie ein ABS-System für ein Auto oder einen optischen Scanner für die Qualitätskontrolle in einer Fabrik entwickeln, habe ich Sie wahrscheinlich schon beim Titel verloren. C funktioniert, es hat die Bibliotheken, den Support, die Dokumentation und wird nur von Assembly übertroffen (wenn überhaupt). Und Sie kennen die Eigenheiten bereits.

Aber wenn Sie aus dem Web-Bereich kommen und Ihr Zuhause automatisieren oder endlich diesen getränkebringenden Roboter bauen wollen und alles, was Sie kennen, Javascript ist – tun Sie es. Der Code ist viel lesbarer und in modernen Sprachen verblüffend viel kürzer. Die meisten Bibliotheken und Pakete laufen ohnehin über sysfs, selbst in C, wenn sie irgendeine Form von Multi-Plattform-Support beibehalten wollen. In diesem Fall ist es nur zehnmal langsamer, was ehrlich gesagt nicht so schlimm ist. Außerdem können Sie immer einen neueren Raspberry/Beaglebone/OLinuxino kaufen und erhalten einen Leistungsschub.

Also los, schreiben Sie Embedded-Code in Javascript, experimentieren Sie mit Elektronik, und wenn Sie nach all dem immer noch mehr Geschwindigkeit brauchen, gibt es immer noch ein paar moderne Herausforderer, die schnell genug sind, um zufrieden zu stellen, bevor Sie zu den Klassikern greifen müssen.

Lassen Sie uns Ihr Projekt realisieren

Sie suchen technische Expertise für die Planung und Umsetzung digitaler Vorhaben, damit Sie sich auf Ihre Kernaufgaben konzentrieren können? Wir unterstützen Sie dabei. Besuchen Sie uns für ein persönliches Gespräch oder kontaktieren Sie uns direkt.

Kontakt

Büro Zagreb

Radnička cesta 47, 10000, Zagreb, Croatia

Social Media

Ljubo Radoš

Ljubo Radoš

Geschäftsführer