Contributors

Events & Callbacks

If you are used to programming hardware in an Arduino environment, this section might seem a bit foreign to you. Unless you are using interrupts, code execution happens in logical order. When dealing with events & callbacks, specific chunks of code gets called as events happen. If you are used to developing with Javascript, then you should feel right at home.

Callbacks

We've already discussed events earlier in the guide, but what's a callback? A callback is a function that is passed as a parameter to another function so it can be used at any point in the future.

What on earth does this have to do with events & event listeners? When dealing with asynchronous events, your callback function defines the actions that happen in response to events. If you want a LED to light up when a button press happens, then you would write the code that turns on the LED inside of a callback. Let's take a look at an example using the Adafruit T-Cobbler to connect the button and LED.

We will show you how to download and run the example code in the last section of the guide, but if you would like to follow along and create the example files yourself, you will need to install the onoff npm package.

Let's break down what is happening in the example code. button.watch(callback) is a function that you can call if you would like to watch a button for changes in state. When a change event happens, the watch() function calls the callback function that was passed to it as its only parameter. In this case, the watch function will call the light() function when the button changes state.

When watch() notices that the button state changed, the light() function is called with two parameters. The first parameter has any errors that may have occurred, and the second parameter gives the current button state. When I wrote the function definition for the light method, I called the first parameter err, and the second parameter state, but you could name them whatever you would like. It's best to name them after what they represent if possible.

Callbacks don't have to be defined separately, and then passed to the functions that will call them. You can define the callback and pass it to the watch function all in the same step. The only drawback to this method is that your callback can't be used by any other buttons. Here's an example of defining your callback & passing it to watch() in the same step.

// button is attached to pin 17, LED to 18
var GPIO = require('onoff').Gpio,
led = new GPIO(18, 'out'),
button = new GPIO(17, 'in', 'both');
// pass the callback function to the
// as the first argument to watch() and define
// it all in one step
button.watch(function(err, state) {
// check the state of the button
// 1 == pressed, 0 == not pressed
if(state == 1) {
// turn LED on
led.writeSync(1);
} else {
// turn LED off
led.writeSync(0);
}
});

// button is attached to pin 17, LED to 18
var GPIO = require('onoff').Gpio,
led = new GPIO(18, 'out'),
button = new GPIO(17, 'in', 'both');
// pass the callback function to the
// as the first argument to watch() and define
// it all in one step
button.watch(function(err, state) {
// check the state of the button
// 1 == pressed, 0 == not pressed
if(state == 1) {
// turn LED on
led.writeSync(1);
} else {
// turn LED off
led.writeSync(0);
}
});

Now that you have a handle on events & callbacks, let's compare how you would approach things in node.js vs how you would approach things in Arduino.

Arduino & node.js Examples

Let's take a look at how you would approach a couple simple tasks in Arduino, and then compare that with how you would tackle the same tasks using node.js.

Let's start with a classic example. Blinking a LED! First up, Arduino.

// LED pin
int led = 13;
void setup() {
// initialize the LED pin as an output
pinMode(led, OUTPUT);
}
void loop() {
// turn the LED on
digitalWrite(led, HIGH);
// delay for one second
delay(1000);
// turn the LED off
digitalWrite(led, LOW);
// delay for one second
delay(1000);
}

// LED pin
int led = 13;
void setup() {
// initialize the LED pin as an output
pinMode(led, OUTPUT);
}
void loop() {
// turn the LED on
digitalWrite(led, HIGH);
// delay for one second
delay(1000);
// turn the LED off
digitalWrite(led, LOW);
// delay for one second
delay(1000);
}

What's going on here? As the sketch runs the main loop, it flips the LED on and off separated by delays of one second. Now, let's look at the same example using node.js on a Raspberry PI.

// export GPIO 18 as an output.
var GPIO = require('onoff').Gpio,
led = new GPIO(18, 'out');
// start a timer that runs the callback
// function every second (1000 ms)
setInterval(function() {
// get the current state of the LED
var state = led.readSync();
// write the opposite of the current state to the LED pin
led.writeSync(Number(!state));
}, 1000);

// export GPIO 18 as an output.
var GPIO = require('onoff').Gpio,
led = new GPIO(18, 'out');
// start a timer that runs the callback
// function every second (1000 ms)
setInterval(function() {
// get the current state of the LED
var state = led.readSync();
// write the opposite of the current state to the LED pin
led.writeSync(Number(!state));
}, 1000);

What's going on here? The same thing, but the difference is that the Arduino delay function blocks all other code from executing for one second for each call to delay(1000), and the node.js setInterval timer does not.

Why does this matter? Let's say you wanted to blink a green LED on and off every second, and you wanted to control the state of a red LED with a momentary button. Let's take a look at another Arduino example sketch.

Since we are using Arduino's delay function, the button presses could be missed because the loop has to make it through both calls to delay(1000) before checking the button state.

Note:I know some of the more experienced Arduino users can think of a ways to avoid using delay(), and solve this problem with interrupts or by checking the current millis() against previous millis() + 1000. This is just a simple example designed to highlight how things can easily get a lot more complicated when you don't have a way to call code asynchronously.

// export GPIO 17 as the red LED output, GPIO 18 as
// the button input, and GPIO green as the button input.
var GPIO = require('onoff').Gpio,
green = new GPIO(21, 'out'),
red = new GPIO(17, 'out'),
button = new GPIO(18, 'in', 'both');
// watch the button for changes, and pass
// the button state (1 or 0) to the red LED
button.watch(function(err, state) {
red.writeSync(state);
});
// start a timer that runs the callback every second
setInterval(function() {
// get the current state of the LED
var state = green.readSync();
// write the opposite of the current
// green LED state to the green LED pin
green.writeSync(Number(!state));
}, 1000);

// export GPIO 17 as the red LED output, GPIO 18 as
// the button input, and GPIO green as the button input.
var GPIO = require('onoff').Gpio,
green = new GPIO(21, 'out'),
red = new GPIO(17, 'out'),
button = new GPIO(18, 'in', 'both');
// watch the button for changes, and pass
// the button state (1 or 0) to the red LED
button.watch(function(err, state) {
red.writeSync(state);
});
// start a timer that runs the callback every second
setInterval(function() {
// get the current state of the LED
var state = green.readSync();
// write the opposite of the current
// green LED state to the green LED pin
green.writeSync(Number(!state));
}, 1000);

Next, let's look at how to make things even simpler with node.js streams!

OUT OF STOCK NOTIFICATION

YOUR NAME

YOUR EMAIL

You have been successfully subscribed to the Notification List for this product and will therefore receive an e-mail from us when it is back in stock!

For security reasons, an e-mail has been sent to you acknowledging your subscription. Please remember that this subscription will not result in you receiving any e-mail from us about anything other than the restocking of this item.

If, for any reason, you would like to unsubscribe from the Notification List for this product you will find details of how to do so in the e-mail that has just been sent to you!