

The "Why?" and the "How?"
Since it first appeared decades ago, Javascript has expanded far beyond the horizons of it's creator's initial intended use. Most notable of these was NodeJS, which enabled the execution of Javascript code without the need for a web browser. Although primarily used in conjunction with the Express framework for web server purposes, this does also enable the creation of desktop applications and, with the help of some platform specific libraries and packages, embedded app development.
Now, although embedded platforms include everything from tiny 8-bit chips to full blown single board computers running Linux, this blog post will focus around a middle-ground that has been available to most people in some form for years.
The first generation of the Raspberry Pi jump-started the embedded hobbyist community by offering a cheap Linux computer with great IO capabilities and support. Even though there are newer and more powerful versions of the Raspberry Pi, we'll be using the first generation as it is still cheaper and available in several different forms. Another factor is the single-threaded limitation of NodeJS and the unfair advantage a multi-core CPU might give to other languages if their libraries were to utilize multi-threading in the background.
So all you need to start tinkering with electronics using Javascript is a computer with a solid set of IO that's capable of running Linux and has enough storage for the Node installation. Once this was available, it was only a matter of time before someone did it just to see if they could. Few people however stopped to wonder if they should.
Turn that light on and off... really fast
The very first snippet of code taught in every embedded platform's tutorial is blink. It is the first step in physically manifesting the will of the code, a simple change of a digital output. How fast those changes can go from the code to the output is usually important and often a good metric of the general system performance. The faster a system is, the faster it will be able to access and change its IO. All you need to measure this is a square signal generated by toggling a pin in a loop with no inserted delays, and if you're lucky enough to have one, an oscilloscope.
Blinking with Javascript
To control a digital output in Javascript, a GPIO package is needed. Two frequently used ones are rpio which is a platform specific package written for the Raspberry Pi, and onoff which uses the Linux sysfs GPIO interface and can theoretically be run on any Linux board (BeagleBone, Banana Pi,...).
With the "generic" onoff package, a simple blink program would look something like this
1const Gpio = require('onoff').Gpio;2const pin = new Gpio(17, 'out');34process.on('SIGINT', _ => {5 pin.unexport();6 });78while (true) {9 pin.writeSync(1);10 pin.writeSync(0);11}
This generates a waveform as seen on the oscilloscope

Three things can be noticed:
- The measured output frequency is around 12,5 kHz, which, while not terrible, isn't great for a 700 MHz CPU
- The waveform is somewhat irregular regarding the period, which could be attributed to Linux executing other tasks as it appears in faster languages as well
- The voltage spikes on each impulse. This is normal since the pin wasn't loaded or filtered and you can see it actually "ringing" before settling.
The example above was only writing and performing the entire square wave in a single loop pass. Let's see what happens if we just change the loop to read and invert the pin value.
1while (true) {2 pin.writeSync(pin.readSync() ^ 1);3}

We can see that the waveform is now highly deformed, having a 30% difference in high/low periods, and the frequency dropped to a measly 5 kHz. Hopefully, a platform specific package like rpio can save the day.
1const rpio = require('rpio');23rpio.init({mapping: 'gpio'})45rpio.open(17, rpio.OUTPUT, rpio.LOW)67while (true) {8 rpio.write(17, 1);9 rpio.write(17, 0);10}
Again, first we do both high and low in a single loop pass.


Indeed, the performance increase is drastic, achieving over five times the frequency. Still, the waveform is not symmetrical, and also, the chunk of time Linux takes for its own housekeeping is far more noticeable when you have more impulses to fit in a timeframe.
Replacing the loop with a read and invert again drops the performance, though not as drastically as with onoff
1while (true) {2 rpio.write(17, rpio.read(17) ^ 1);3 }
The output frequency only drops to about 31 kHz, though still not optimal.
Offerings from some fan favorites
GO is a language with Google funding and a solid community behind so it's no surprise that it has proper Raspberry Pi support, including its own version of the rpio module.
1package main23import "github.com/stianeikeland/go-rpio/v4"45func main() {6 rpio.Open()7 defer rpio.Close()89 pin := rpio.Pin(17)10 pin.Output()1112 for {13 pin.Write(rpio.High)14 pin.Write(rpio.Low)15 }16}
This little snippet generates a very clean looking, simetric, waveform

and the frequency bump is significant at 1.28 MHz. Switching to a read-and-invert loop again
1for {2 pin.Write((pin.Read() + 1)%2)3}
results in a 1 MHz output frequency which is hardly a drop compared to the Javascript implementation.
Now that we got modern contenders out of the way, it's time for the granddaddy of them all, C. By far the most popular embedded language, it has been a staple in programming since the early days of computing and is still a mighty tool for those that have the patience to manually do what most languages don't even mention to you.
There are clean and simple ways to handle IO in C for the Raspberry Pi, such as the WiringPi library. But we are going for speed, and for speed you want direct access to the registers controlling the IO. This way of access isn't wrapped in a neat library and makes the code snippet for this rather large for a blog post so I will point you to a nicely documented snippet on eLinux which you can cut down for your needs. The resulting output is impressive.

The frequency of the output is 13.9 MHz and is in fact so fast the signal doesn't have time to settle into a square form and is still in the phase of ringing when it switches states. Reading then inverting does drop the output frequency to 3.7 MHz but it's still faster than any other tested language by far.
Out of pure curiosity, C was also tested using sysfs access, just like the onoff npm package. It resulted in output frequencies of 130 kHz for normal and 59 kHz for inverting blink loops. Horrible when compared to the direct access way, but still ten times faster than Javascript using the same approach. Please note that the sysfs snippet provided in the eLinux code samples opens and closes the IO file for each digital write which is detrimental for performance and should be replaced with the standard open file on program init and close on program close.
To summarize, this is how the final performance table looks:
Blink - write 0, write 1
| Language | Library | Output Frequency |
|---|---|---|
| Javascript | onoff | 12,5 kHz |
| Javascript | rpio | 69.4 kHz |
| GO | rpio | 1,28 Mhz |
| c | register access | 13,9 Mhz |
| C | sysfs | 130 kHz |
Blink - read and invert
| Language | Library | Output Frequency |
|---|---|---|
| Javascript | onoff | 5 kHz |
| Javascript | rpio | 31,6 kHz |
| GO | rpio | 1 Mhz |
| C | register access | 3,7 MHz |
| C | sysfs | 59 kHz |
Final word
The old saying states that if all you have is a hammer, everything starts looking like a nail. This is often the case in programming as developers are reluctant to learn a new language along with its quirks and issues if the same task can be performed with a language they already know. The boom of web development in recent years means that for a lot of people, this language is Javascript. So, should they start hammering screws? Maybe.
Although the performance gap demonstrated above seems devastating, it needs to be put into perspective. If you're developing an ABS system for a car or an optical scanner for factory quality control, I probably lost you at the title. C works, it has the libraries, the support, documentation and is only outperformed by assembly (if at all). And you already know the quirks. But if you've been doing web work and want to automate your home or finally make that drink carrying robot and all you know is Javascript, go for it. The code is far more readable, and mindbogglingly shorter in modern languages. Most libraries and packages go through sysfs anyway, even in C if they want to maintain some form of multi-platform support. In that case it's only ten times slower, which honestly isn't that bad. Plus, you can always buy a newer Raspberry/Beaglebone/OLinuxino and get a boost.
Go for it, write embedded code in Javascript, experiment with electronics and if after all of that you still need the speed, there's still a few modern contenders fast enough to satisfy before you reach the Classics.

