Say “Hello World” with Johnny-Five on Tessel 2

Back in April I wrote about Bocoup’s excitement for the recently announced Tessel 2 in “Pushing NodeBots Into The Future with Tessel 2”. Since then, we’ve worked side-by-side with the Tessel Project to help them realize their platform vision for the next stage in the evolution of the Tessel. With shipping underway, we’re excited to start publishing a series of “getting started” guides that will provide the basic building blocks to create Johnny-Five applications for the Tessel 2.

In this first article, we’ll get familiar with writing Johnny-Five programs for Tessel 2 by writing a simple “Hello World,” then explore a couple variations on that theme. If you have any trouble along the way, with either Johnny-Five, Tessel-IO, or the Tessel 2, please ask for help in the Johnny-Five Gitter channel.

Tessel 2 Setup

This article assumes that you are familiar with running programs from a terminal, and have some experience with JavaScript, Node.js, and npm (also, that these are already installed in your system).

We need to make sure your Tessel 2 is running the latest OS images and firmware before you continue. Complete all steps in the t2-start guide to guarantee you have the latest Tessel 2 system. If you haven’t already done so, you must complete the t2-start activity before continuing with this guide. Once your Tessel 2 is updated, come back to this article and continue to the next section.

A New Project

From within your terminal, create a new directory named tessel-projects as a sandbox for all projects you’ll create in this post. We’ll be executing all commands from within that directory. From within the new project directory, use npm to install the johnny-five and tessel-io packages:

npm install johnny-five tessel-io

To make this a valid Tessel 2 project directory, it must contain a package.json file. We only need to do this once, so go ahead and run the following, responding in the affirmative as many times as necessary to finalize the package.json setup:

npm init

Once this is complete, you’ll have the necessary local environment to build your project!

Hello World

Let’s make an LED blink—no more, no less.

Parts list:

  • (1) LED of any color
  • (1) Tessel 2

Once these parts are inventoried and accounted for, create a file called blink.js and open it in your preferred editor. Type, or copy and paste, the contents of the following program:

// 1
var five = require("johnny-five");
// 2
var Tessel = require("tessel-io");
// 3
var board = new five.Board({
  // 4
  io: new Tessel()
});
// 5
board.on("ready", function() {
  // 6
  var led = new five.Led("a0");
  // 7
  led.blink(500);
});

Let’s break that program down:

  1. Require the Johnny-Five module.
  2. Require the Tessel-IO module. This class is used by Johnny-Five to communicate with the Tessel 2’s SAMD21 coprocessor.
  3. Initialize a new instance of the Board class, specifying that io will be provided by a new instance of the Tessel class. The Tessel class is an IO-Plugin, meaning it will produce objects that are API-compatible with Firmata.js. (This makes it the fifteenth module in the growing family of IO plugins!)
  4. Create a “ready” event handler, which is called once Johnny-Five and Tessel-IO have completed any initialization that must be done.
  5. Once the board has signaled that it is “ready,” create a new instance from the Led class 0, specifying that the LED itself is attached to PORTA, pin 0 using the shorthand string notation "a0" (Tessel-IO Pin Naming Guide).
  6. Call the blink() method of the led instance object, specifying 500ms on-off periods.

Next, attach an LED of your choice to PORT A‘s GND and 0 pins:

tessel-led-blink

Return to your terminal, in the tessel-projects directory and type the following:

t2 run blink.js

You will see some output that explains what is happening, it will look something like this:

$ t2 run blink.js
INFO Looking for your Tessel...
INFO Connected to bishop over LAN
INFO Generating slim build.
INFO Writing project to RAM on bishop (xxx kB)...
INFO Deployed.
INFO Running bundled project...

And of course your LED will be blinking on-off every 500 milliseconds. Whenever you’re ready to move on, shut down the program by sending a signal interrupt (CTRL+C).

A Rainbow Connection

So far you’ve made a single LED blink, which is very exciting the first time, but trivial none-the-less. Let’s see what it takes to make many LEDs blink.

Parts list:

  • (6) LEDs of any color or size (I’ll use one LED each for red, yellow, green, blue, violet & white)
  • (7) Jumper wires (one for each LED and one for GND)
  • (1) Solderless breadboard
  • (1) Tessel 2

Start by creating a new file called rainbow.js and open it in your preferred editor. Type, or copy and paste, the contents of the following program:

var five = require("johnny-five");
var Tessel = require("tessel-io");
var board = new five.Board({
  io: new Tessel()
});
board.on("ready", function() {
  // 1
  var leds = new five.Leds(["a0", "a1", "a2", "a3", "a4", "a5"]);
  // 2
  leds.blink(500);
});

Since nothing has changed about steps 1 through 5, I’ve omitted the annotation comments from the code and I’m going to skip them now (starting back at 1) and focus only on the part of the code that’s changed:

  1. Create a new instance of the Leds collection class, specifying that LEDs will be attached to PORTA, pins 0, 1, 2, 3, 4 and 5, using the shorthand string notation "a0", "a1", "a2", "a3", "a4", "a5".
  2. Call the blink() method of the leds instance object, specifying 500ms on-off periods.

Next, connect your LEDs to the Tessel 2 via the solderless breadboard.

tessel-leds-blink

Now you can run the program from your terminal with:

t2 run rainbow.js

And it should look like this:

… Which is not very interesting at all—in fact, the whole effect could be achieved by connecting all of the LEDs in series and controlling them with a single pin! For something more interesting, take a look at the next example; first create a new file called larson.js and open it in your preferred editor. Type, or copy and paste, the contents of the following program:

var five = require("johnny-five");
var Tessel = require("tessel-io");
var board = new five.Board({
  io: new Tessel()
});
board.on("ready", function() {
  var index = 0;
  var step = 1;
  var leds = new five.Leds(["a0", "a1", "a2", "a3", "a4", "a5"]);

  // 1
  this.loop(20, function() {
    // 1.1
    leds.off();
    // 1.2
    leds[index].on();
    // 1.3
    index += step;
    if (index === 0 || index === leds.length - 1) {
      step *= -1;
    }
  });
});

I’m only going to focus on the parts that are relevant to my previous point:

  1. In an async loop:
    1. Call the off() method of the leds object. This will shut off all LEDs under the control of the collection instance.
    2. Call the on() method of the LED located at index of the collection.
    3. Increment or decrement the index.

Express Yourself

For this next project, I’m going to scale back to a single LED—however, the BlinkM is no ordinary LED. This component is an RGB LED controlled via an I2C interface, which means that it requires four jumpers to the board: 3V3, GND, SCL and SDA (SCL is pin 0 and SDA is pin 1, on either PORT).

Parts list:

  • (1) BlinkM (Available from SparkFun)
  • (4) Jumper wires (one each for GND, 3V3, SCL and SDA)
  • (1) Solderless breadboard
  • (1) Tessel 2

Create a new file called blinkm.js and open it in your preferred editor. Type, or copy and paste, the contents of the following program:

var five = require("johnny-five");
var Tessel = require("tessel-io");
var board = new five.Board({
  io: new Tessel()
});
board.on("ready", function() {
  // 1
  var rgb = new five.Led.RGB({
    address: 0x09,
    controller: "BLINKM",
    port: "A",
  });
  // 2
  rgb.color("#ff0000");
});

Again, nothing has changed about steps 1 through 5 (note that there are no additional libraries to load), but I’m now using a different component class to control the BlinkM.

  1. Create a new instance of the Led.RGB component class, setting the address property’s value to 0x09, the controller property’s value to "BLINKM", and the port property’s value to "a". I could have omitted the address property from the options, which would signal to the internal controller to use the default address, which also happens to be 0x09. Then the instantiation would look like this:
    var rgb = new five.Led.RGB({
      controller: "BLINKM",
      port: "a", 
    });
    
    Furthermore, Tessel-IO allows me to omit the port property, and will default to using “a”. That further reduces the code I need to write to this:
    var rgb = new five.Led.RGB({
      controller: "BLINKM"
    });
    
    Note that controller and port values are case insensitive. The address property is optional for all built-in controller objects in Johnny-Five, but the port property is specific to the Tessel IO Plugin. I2C components have specialized internal operations and therefore require a little more information than can be implied by the pin name mapping used in the previous examples. Later we’ll look at an I2C component that has its own pins!
  2. Call the color(...) method of the rgb instance object, specifying the hexadecimal color code "#ff0000".

Next, connect your BlinkM to the Tessel 2 via the solderless breadboard.

tessel-led-blinkm-basic

Back in your terminal, run with:

t2 run blinkm.js

And it should look like this:

blinkm-1

This is fine, but not expressive at all. Let’s try controlling the intensity of the color:

var five = require("johnny-five");
var Tessel = require("tessel-io");
var board = new five.Board({
  io: new Tessel()
});
board.on("ready", function() {
  // 1
  var rgb = new five.Led.RGB({
    controller: "BLINKM"
  });
  // 2
  var level = 1;
  var interval = 10;
  var step = 10;
  // 3
  rgb.color("#ff0000").intensity(0);
  // 4
  this.loop(interval, function() {
    // 4.1
    if (level >= 100 || level <= 0) {
      step *= -1;
    }
    // 4.2
    rgb.intensity(level += step);
  });
});

Now we’re getting a little more interesting, right? Let’s pick this apart:

  1. Create a new instance of the Led.RGB component class—you’re already an expert in this, so no more discussion is needed.
  2. Declare and assign values to three variables: level to 1, interval to 10 (10ms = 100Hz) and step to 10. level will track the value of the intensity level from 0-100%, interval is the time in milliseconds between each loop iteration, and step is the number to increase or decrease on each iteration.
  3. Call the color(...) method of the rgb object with hexadecimal color code "#ff0000", then chain a call to the intensity(...) method with value 0. This will set the color to red, but the intensity level will be 0, so it will initially appear to be off (not that your slow human eyes will ever notice).
  4. Inside the board object’s "ready" event handler, the this object is that of the Board instance itself, ie. (this === board) === true. As such, call the board object’s loop(...) method with interval and a callback to call on each iteration. In a loop:
    1. If the level has gone over 100 or under 0, invert the step value.
    2. Call the intensity(...) method of the rgb object with the result of adding the step value to the present level value.

Ok, now let’s play with color and intensity:

var five = require("johnny-five");
var Tessel = require("tessel-io");
var board = new five.Board({
  io: new Tessel()
});
board.on("ready", function() {
  var rgb = new five.Led.RGB({
    controller: "BLINKM"
  });
  // 1
  var level = 100;
  var interval = 5;
  var step = 1;
  // 2
  var nulls = [null, null, null];
  // 3
  rgb.intensity(level);
  // 4
  this.loop(interval, function() {
    if (level >= 100 || level <= 0) {
      // 4.1
      if (level <= 0) {
        rgb.color(
          // 4.1.1
          nulls.map(randomUint8)
        );
      }
      step *= -1;
    }
    rgb.intensity(level += step);
  });
  // 5
  function randomUint8() {
    return (Math.random() * 0xFF) >>> 0;
  }
});

Ok, let’s get cozy with this version of the program:

  1. The initial values of level, interval and step have changed to 100, 5 (5ms = 200Hz) and 1, respectively.
  2. In addition to level, interval, and step, nulls is declared and assigned to [null, null, null], which will be used to generate new arrays of unsigned 8-bit integer (uint8) values that are passed to color(...).
  3. Call the intensity(...) method of the rgb object with level.
  4. The call to this.loop(...) now accepts the interval variable as its first argument, instead of a magic number. In a loop:
    1. If level is less than or equal to 0:
      1. Call the color(...) method of the rgb object with a new array of uint8 values.
  5. Define a function called randomUint8(), which will return a random uint8 number value. It’s important to call out that the invocation of color(array) is not a mistake: Johnny-Five does its best to be as accommodating as possible. In this case that means color can be defined several different ways that are all equivalent:
    // A hexadecimal String, with or without the leading `#`:
    rgb.color("#FF0000");
    rgb.color("FF0000");
    
    // An Array of bytes that are automatically UInt8 clamped:
    rgb.color([0xFF, 0x00, 0x00]);
    
    // An Object with red, green, and blue properties, 
    // whose values are automatically UInt8 clamped:
    rgb.color({
      red: 0xFF, 
      green: 0x00, 
      blue: 0x00
    ]);
    

Let’s see that program in action:

I’m going to swap out the BlinkM with two diffused RGB LEDs, which will be driven by an Adafruit 16-Channel 12-bit PWM/Servo Driver (PCA9685). The driver is required here because the Tessel 2’s firmware does not yet support direct PWM output. The “pulsing” color change pattern will remain, but it will be used for both LEDs.

Parts list:

  • (2) 5mm Diffused RGB LED
  • (1) Adafruit 16-Channel 12-bit PWM/Servo Driver (PCA9685)
  • (13) Jumper wires
  • (1) Solderless breadboard
  • (1) Tessel 2

Create a new file called dueling-rgbs.js and open it in your preferred editor. Type, or copy and paste, the contents of the following program:

var five = require("johnny-five");
var Tessel = require("tessel-io");
var board = new five.Board({
  io: new Tessel()
});
board.on("ready", function() {
  // 1
  var left = new five.Led.RGB({
    isAnode: true,
    controller: "PCA9685",
    pins: [ 0, 1, 2 ],
  });
  var right = new five.Led.RGB({
    isAnode: true,
    controller: "PCA9685",
    pins: [ 4, 5, 6 ],
  });
  var level = 100;
  var step = 5;
  var interval = 5;
  // 2
  left.intensity(level);
  right.intensity(level);
  // 3
  this.loop(interval, function() {
    if (level >= 100 || level <= 0) {
      // 4
      if (level <= 0) {
        // 5
        left.color(randomRGB());
        right.color(randomRGB());
      }
      step *= -1;
    }
    // 6
    level += step;
    left.intensity(level);
    right.intensity(level);
  });

  // 7
  function randomRGB() {
    return [null, null, null].map(function() {
      return (Math.random() * 0xFF) >>> 0;
    });
  }
});
  1. Initialize two Led.RGB instances, both with isAnode: true and appropriate pins. The order of pins in the pins array corresponds to [ red , green, blue ].
  2. Call the intensity(...) method of both the left and right objects, with the initial value of level.
  3. In a loop:
    1. If level is less than or equal to 0:
      1. Call the color(...) method of both the left and right objects, with an array of uint8 values returned by randomRGB().
    2. Call the intensity() method of both the left and right objects, with the updated value of level.
  4. Define a function called randomRGB(), which will return an array containing 3 random uint8 values.

The here’s the circuit to assemble:

tessel-led-pca9685

Go ahead and deploy this program to your Tessel:

t2 run dueling-rgbs.js

Assuming everything was connected correctly, the result should look something like this:

Say “Hello World”

So far I’ve limited the “voice” of my Tessel 2 to LED demonstrations, which are fun but also relatively trivial. The title of this article is Say “Hello World” with Johnny-Five on Tessel 2 and my goal is to deliver exactly that. In this section, I will show how to “say” the words with a digital display and a Tessel 2.

Parts list:

Create a new file called hola.js and open it in your preferred editor. Type, or copy and paste, the contents of the following program:

var five = require("johnny-five");
var Tessel = require("tessel-io");
var board = new five.Board({
  io: new Tessel()
});
board.on("ready", function() {
  // 1
  var display = new five.Led.Digits({
    controller: "HT16K33",
  });
  // 2
  display.print("Hola");
});

Short and sweet, but let’s look at the annotated parts:

  1. Create a new instance of the Led.Digits component class, setting the controller property’s value to "HT16K33".
  2. Call the print(...) method of the display object, with the string “Hola”.

Assemble the parts:

tessel-HT16K33

Return to the terminal and run the following:

t2 run hola.js

Now let’s update the program to display “Hello World” and scroll the message from right to left infinitely. No changes to the hardware are necessary, just create a new file called hello-world.js and open it in your preferred editor. Type, or copy and paste, the contents of the following program:

var five = require("johnny-five");
var Tessel = require("tessel-io");
var board = new five.Board({
  io: new Tessel()
});
board.on("ready", function() {
  var display = new five.Led.Digits({
    controller: "HT16K33",
  });
  // 1
  var message = "Hello World";
  var index = 0;

  this.loop(200, function() {
    // 2
    var chunk = message.slice(index, index + 4);
    // 3
    if (chunk.length < 4) {
      // Coming soon in ES7: String.prototype.padLeft!
      chunk += " " + message.slice(0, 3 - chunk.length);
    }
    // 4
    display.print(chunk);
    // 5
    if (++index > message.length) {
      index = 0;
    }
  });
});

This is straight forward, but let’s look at each step to fully understand the semantics of this program:

  1. Declare and assign values to two variables: message to "Hello World" and index to 0. message is obviously the string characters that will be shown on the display, and index will be used to track that starting index for each iteration.
  2. Inside the board instance’s loop(...) method callback, declare the variable chunk and assign it the value of a new string created by slicing four characters from the message string, starting from the index. In a loop:
    1. If the length of chunk is less than the 4 (the number of characters that can be displayed at once), then concatenate an empty space (" "), followed by a slice of the message string, from the beginning to whatever the difference of 3 (the remaining number of characters after the empty space) and the present chunk.length. This is how the display will appear to “cycle” the contents of message in from the right edge to the left edge.
    2. Call the print(...) method of the display object, with the string contents of the chunk variable.
    3. If the index is greater than the length of message, set index back to 0. This is how the display will appear to “cycle” the contents of message infinitely.

And finally, it will say “Hello World,” just like this:

Next Steps

This article has shown several different ways to convey some kind of information from a program running on a Tessel 2 to the outside world, but the capabilities don’t end here. Think about how a program might convey information that is relevant to the environment surrounding the device itself. Consider displays you might take for granted: microwave, stove, digital clock, remote control, and game pad indicator lights. What about the difference between text based “notifications” versus simple indicator lights that have some meaning assigned to them. What are the pros and cons of each?


  1. Led API:

Comments

Contact Us

We'd love to hear from you. Get in touch!

Boston

201 South Street, Boston, MA 02111

New York

315 Church St, New York, NY 10013

Phone & Email

(617)379-2752 hello@bocoup.com