Comparing Node.js and Python on the Raspberry PI

Picture of my simple led circuit connected to the Pi.
Picture of my simple led circuit connected to the Pi.

Programming on the Raspberry Pi

Python seems to be the more popular language for writing programs on the Raspberry Pi.  However, it is far from the only language: Python, C, C++, Java, and Ruby are some that are automatically supported out of the box.  As it turns out Node.js is also supported.  Perhaps, just based on my preconceptions, I didn’t originally consider JavaScript when interfacing directly with hardware.  The goal of this article is to compare functionally equivalent sample programs written both in Python and JavaScript.

 

The Goal

I decided to make the scope of the problem created for this article as small as possible.  Given that the goal will be to create simple circuit diagram consisting of a LED and then to write a program that makes the LED flash for a specified period of time.
 

Simple LED circuit diagram
Simple LED circuit diagram

Hardware

With the Pi, a circuit that will make a LED flash is relatively simple: Consisting of just a LED and resistor.  I arbitrarily selected pin 7 on the Pi for this build.  I’ve included a diagram of it on this page.

 

Software

As the goal states, the following two implementations do nothing except toggle an LED at half the specified duration (0.5s) and automatically shutoff after 60 seconds.
 

Python

Since Python is the more traditional programming language for the Raspberry Pi, let us start with it.  The Rpi.GPIO module I used can be installed via pip.  Before you read the code sample, let me point out some things about the implementation.  Could it have been done simpler given that the problem was just to make the LED flash for a certain amount of time?  Yes, but I wanted to create something of an asynchronous process that could be controlled from outside its execution context.

import RPi.GPIO as GPIO
from threading import Event, Thread, Timer
class ImprovedThread(Thread):
    def __init__(self, *args, **kwargs):
        super(ImprovedThread, self).__init__(*args, **kwargs)
        self._stopEvent = Event()
    def stop(self):
        self._stopEvent.set()
    def is_stopped(self):
        return self._stopEvent.isSet()
    def wait(self, duration):
        self._stopEvent.wait(duration);
class LEDFlasher(ImprovedThread):
    def __init__(self, pin, duration):
        super(LEDFlasher, self).__init__(target=self._flashLED)
        self.setDaemon(True)
        self._pin = pin
        self._duration = duration
        self._state = False
        GPIO.setmode(GPIO.BOARD)
        GPIO.setup(self._pin, GPIO.OUT)
        self.start()
    def _flashLED(self):
        while not self.is_stopped():
            GPIO.output(self._pin, self._state)
            self._state = not self._state
            self.wait(self._duration/2)
flasher = LEDFlasher(7, 0.5)
Timer(60, flasher.stop).start()
flasher.join()
GPIO.cleanup()

 

JavaScript

I used pi-gpio to interface with the GPIO on the Pi in JavaScript.  It can be installed via npm. It makes use of gpio-admin so that your script doesn’t have to run as sudo.  Follow the installation instructions provided here and it should get you setup.  Note that due to differences int the languages, it was not necessary to thread (web workers) the solution.

var gpio = require("pi-gpio");
function flashLED(pin, duration) {
    return setInterval(function() {
        gpio.open(pin, "output", function(err) {
            gpio.write(pin, 1, function() {
                setTimeout(function() {
                    gpio.write(pin, 0, function(err) {
                        gpio.close(pin);
                    });
                }, duration/2);
            });
        });
    }, duration);
}
var intervalID = flashLED(7, 500);
setTimeout(function() {
    clearInterval(intervalID);
}, 60000);

 

The Findings – I was surprised

When I started writing the JavaScript side of this article, I was mainly doing it to gain experience working with JavaScript on the Pi.  I did not expect to come out of it vastly preferring it over my python implementation.  I find the Python implementation above to be to wordy and overcomplicated.  It just seems that the amount of code needed to achieve the same results is excessive.  Perhaps this is because of the problem scope and implementation.  The problem posed here was a simple one, where a functional solution is superior to an object-oriented one.  Could the Python code above be rewritten into something a bit more functional? Sure!  Are there problems that an object-oriented Python or JavaScript implementation would be the better solution for?  Definitely.  The lesson to take away: be open to working outside your comfort zone and pick solutions to problems based on their fit for the problem not your comfort level with them.
I’ve created a copy of the code as a Gist that is available here: https://gist.github.com/chaddotson/570501a3e3dcfe8928c8

8 Comments

  1. Interesting read, thought that your preference for the JavaScript version was interesting as well. Although it is tighter and might perform better the Python version seems much more readable.

  2. Agreed, I’m a fan of the saying “Python is executable pseudocode.” I like it, its very readable and it doesn’t have the quarks that javascript does. Run it with pypy and it will beat out node performance-wise. I think the feel of the javascript appealed for the event-driven nature of the code sample.

  3. Despite of the amount of codes needed for achieving the same work, did you compare the runtime of these two languages on RaspberryPi? Somehow I heard a saying, that Node.js runtime could consume most of the 512MB RAM and cause the Pi to crash after running for a long time, yet this seemed not happen to Python.
    “The new Garbage Collector (GC) algorithm in node.js v0.10.x behaves differently than v0.8.x – in that it doesn’t enforce a clean until it is near a memory limit larger than the 512MB in the Pi – this can cause the Pi to crash if left running for a long time”, excerpted from nodered website.

  4. I didn’t compare their footprint at all. I’ve not left anything of substance running for a prolonged amount if time in Node on the PI. I’d not seen the post on node-red before you mentioned it since I compiled from source. I did see where they mention (below the quote you used) the use of the “max-old-space-size” argument that would fix the issue.

  5. If you can install nodejs, then you can upgrade python to 3.5. In that case, you can do an elegant functional implementation:
    import asyncio
    import RPi.GPIO as GPIO
    async def flash_led(pin, duration):
    while stop.run:
    GPIO.output(pin, 0)
    await asyncio.sleep(duration / 2)
    GPIO.output(pin, 1)
    await asyncio.sleep(duration / 2)
    def stop(coro):
    stop.run = False
    GPIO.cleanup()
    loop = asyncio.get_event_loop()
    stop.run = True
    coro = flash_led(7, 0.5)
    loop.call_later(5, stop, coro)
    loop.run_until_complete(coro)
    Which is not only shorter than the JS version, but easier to read (no callback hell) and way easier to debug or refactor.

  6. Michael

    Seems to me this would work (ES6):
    var gpio = require(“pi-gpio”);
    function flashLED(_gpio, pin, duration) {
    let toggle = 0;
    return setInterval(()=> {
    toggle = !toggle;
    _gpio.open(pin, “output”, ()=> _gpio.write(pin, toggle, ()=> _gpio.close(pin)));
    }, duration);
    }
    const intervalID = flashLED(gpio, 7, 250);
    setTimeout(()=> {
    clearInterval(intervalID);
    }, 60000);

Leave a Reply