Implementing the MICS-VZ-89T gas sensor on Intel Edison i2c

On my current project I have the requirement to monitor indoor air quality. What is of interest are the levels of Volatile Organic Compounds (VOCs) and CO2.  There are specific thresholds that we are looking for that when exceeded should trigger an action. For VOCs it is when the concentration is greater that 0.9 ppm. For CO2 it is when the concentration is 1000 ppm above ambient out side C02 — which is generally around 400 ppm. The links above out line the dangers of these indoor pollutants. When the threshold is reached we want to start the ventilation system and optionally message a user.

When I need a sensor my first choice is to find one that implements i2c. In this case I found a good candidate for the job in the SGX Sensortech MICS-VZ-89T. The VZ89 product is a small board with a MICS SMD device integrated with an i2c controller. The board comes in both 5 volt (VZ89) and 3.3 volt (VZ89T) versions that are are easy to implement using a logic level shifter with the Edison on a mini breakout board. (For an example of using a logic level shifter you can see my article Intel Edison and I2C sensors with XDK.)

There was no driver that I could find for implementing the board with my setup so I had to roll my own. This wasn’t too hard, but I did have to break out the logic analyzer to get it right. If we examine the MICS-VZ-89T I2C Specification page  we see that the device only has two commands. These are Set ppmC02 and Get VZ89 Status. According to the MICS-VZ-89T Data Sheet the device comes calibrated from the factory so we don’t need to implement Set ppmC02. That leaves us Get VZ89 Status. The code that follows here is available in an XDK project on git hub.

To read the status we have to perform a two step process. First we write a command byte of 0x09 to register address 0x70. We follow this by writing two data bytes to the same register. I write 0x00 twice.

We then read 6 bytes immediately after writing the command byte. The bytes are decoded as follows:

Data byte 1 = CO2-equivalent value. 
Data byte 2 = VOC_SHORT value. 
Data byte 3 = VOC_LONG value 
Data byte 4 = Raw sensor 1st byte (LSB). 
Data byte 5 = Raw sensor 2nd byte 
Data byte 6 = Raw sensor 3rd byte (MSB).

To implement the functionality I created the following class in a node module:

//Import mraa 
var mraa = require('mraa');

//Constructor -- set defaults and populate tx_buf
function VZ89(bus , address){

 this.bus = new mraa.I2c(bus || 1); 
 this.bus.address(address || 0x70); 
 this.tx_buf = new Buffer(3); 
 this.tx_buf[0] = 0x09;
 this.tx_buf[1] = 0x00;
 this.tx_buf[2] = 0x00; 

//Add a function to get the device readings. 
VZ89.prototype.getReadings = function() {
//Export as a node module 
module.exports = VZ89;

The MRAA library is used to access the i2c bus, so it is imported at the top or the file. There is a constructor that optionally takes a bus number and a devices address. The VZ89 is addressed at 0x70 so we don’t really need to change that. If the Edison mini-breakout is used we have a choice of busses. I have set this to bus 6 as I am using the Edison Arduino for this example.

A class function is added to implement named getReadings to perform the measurements and return data from the device. In this case the buffer is passed back to the calling program for use.

To use the class the code in the server file would look like this:

//Import our sensor file from the file system
var Sensor = require('./VZ89.js'); 
//Create an instance of the sensor object. 
var sensor = new Sensor();
//Create a var for the receive buffer.
var rx_buf; 

//Call the readBuf function every minute. 
setInterval(readBuf , 60000); 

//A function to read the sensor data, perform data conversions and display on the console every minute.
function readBuf(){

    rx_buf = sensor.getReadings();
    console.log("Co2_equ: " + ((rx_buf[0] - 13) * (1600/229) + 400) + " ppm"); 
    console.log("VOC_short: " + (rx_buf[1])); 
    console.log("tVOC: " + (rx_buf[2] * (1000/229)) + " ppb"); 
    console.log("Resistor Value: " + 10 * (rx_buf[3] + (256 * rx_buf[4]) + (65536 * rx_buf[5])) + " ohms"); 

The details of converting the rx_buf data to usable values are in the data sheet.  The ones we are most interested in are Co2 equivalent (rx_buf[0]) and total VOC (rx_buf[2]).

As you can see, the MICS-VZ89T is a pretty easy to use device once you know how. There are only a couple of gotchas to be aware of. First, the device can only be polled once a second. I find if I try to get the readings faster than that the device will return nulls. Secondly, care must be taken when handling the device. It contains organic material that is susceptible to solvents.

Nodejs, and Intel Edison

One of the things I really like about the Intel Edison (and the Galileo) is that they can be run as little Linux computers. We can use a lot of tools that are available for Linux machines. One of those tools in particular is nodejs.

Nodejs is a very powerful server side platform. It comes preinstalled on the Edison and Galileo Yocto images so it is very easy to get started with. We can implement code on the Edison and Galileo with the Intel XDK IoT Edition. With this combination of tools it is very easy to create applications that can monitor and control a remote Edison via a web based interface.

To get started I have created a repo on Git Hub that contains a project for the XDK. This project utilizes several technologies — node js, express , and  mraa library  on the server side. Jquery and an open source library to implement a gauge are used on the client side. I will be using the MCP9808 setup that I blogged about previously but any sensor (or group of sensors) could be utilized.

Before we jump into the code lets make sure we our Edison in up to date. I am using version 146 of the stock Yocto image. To check the version of your Edison execute the following:

configure_edison --version

If you need to update your Edison you can use the following:

configure_edison --upgrade

If you upgrade your Edison you will have to set up the wifi again.

To install the code on your Edison use the Intel XDK to open the project downloaded from Git hub. If you are using the Edison Arduino board open app.js and change line 23 to use i2c bus 6. Upload the project and use the build action to install all the Node dependencies. If you had an app running on the Edison previously you will need to stop it and start this one with the start and stop buttons.

The server code is located in the root of the project in the app.js file.  Here are lines 6 thru 10:

6  var express = require('express');
7  var app = express();
8  app.use(express.static(__dirname));
9  var server = app.listen(8085);
10 var io = require('').listen(server);

The first  four lines set up Express. Express is a full featured web framework for Node, but here I am just going to use it to serve up my static files from the file system.  The static server is set up on line 8. Line 9 instructs our server to listen on port 8085. is initialized on line 10 and instructed to listen for connections on line 10.

Express may seem like overkill for this application, but I like to use if because it is easy to configure and is well supported. There are other, lighter foot print modules that can be used. You can find these other http servers by looking in the NPM Registry .

The real work in the server is done on lines 28 thru 44 :

28 io.sockets.on('connection', function (socket) {
30  setInterval(function () { 
31 socket.emit( 'temp' , JSON.stringify(getTemp())); //send temp every interval
32 }, 2000);

35 socket.on('toggle_led', function(data){
36   if(data === 'on'){
37     myOnboardLed.write(0);
38   } else {
39     myOnboardLed.write(1); 
40   }
41 });

44 });

Line 28 sets up to listen for connections. Each client connecting to this device will start the callback that sends the current temperature every 2 seconds (lines 30 – 32 ) and listen for the ‘toggle_led’ event (lines 35 – 41) to allow an led to be turned on and off.

The client side code is just as simple. The index.html page contains divs that hold the guage and a button.  The javascript code that does the work is in /js/app.js . This is all pretty standard stuff, but the first time I worked with I was stumped for awhile trying to find the source file for the client (Line 11 in index.html). This file is served automatically from our node server when we run it and is included in the server side code. All we need to do is add the line to load it in index.html.

If we look at the client side javascript in /js/app.js :

1  var socket = io.connect();
3  socket.on('temp', function (data) {
4    var status = JSON.parse(data);
5    Gauge.Collection.get('temp').setValue(status.farenheit); 
6   });
8  function toggle_led(state){
10   socket.emit('toggle_led' , state ); 
11   var button = $('#led_button'); 
12   if(state === 'on'){
13      button.attr("onclick", "toggle_led('off')"); 
14      button.html('Turn off led'); 
15    } else {
16      button.attr("onclick", "toggle_led('on')"); 
17     button.html("Turn on led");1
19    }
20 }

We see that the code is fairly simple as well. The client is initialized on line 1 with a connection to the server we loaded our script from. We then setup a listener for the “temp” event on line 3. If we get a “temp” event the callback function will parse the json string from the server and update the guage in our page to the current value of our MCP9808. (Check the guage git hub page for comprehensive details on its use.)

To toggle the onboard led we have implemented the function at line 10 to emit the “toggle_led” event to the server. We also swap out the events on the button so we can just use one button to toggle the led.

The thing to remember is that the socket.emit sends an event with the name that we provide (in this case “toggle_led”). We need to ensure that the listener for this event uses the exact string. I’ve messed this up a couple of times.

It should look something like this when we have it running:

When the page is running it will look something like this.

When the page is running it will look something like this.

So there you have it. We have implemented a simple web page on the Edison so that we can get the temperature from out MCP9808 and display it. We have implemented the ability to toggle an led on the Edison from the web page. These examples are pretty basic, but the demonstrate a good starting point for more enhanced functionality that could be implemented. Feel free to use the template as a starting point in your projects.

I2C Interfacing on Intel Edison

In a previous post I discussed how to read the temperature from a MCP9808 temperature sensor. In this post I want to back up a bit to demonstrate some techniques for figuring out how to interface with an i2c chip and some basic verification techniques. I will be using the Intel Edison on the mini-breakout board, but these techniques will apply to most Linux based embedded systems.

The MCP9808 is an i2c bus device. I2C is short for Inter-Integrated Circuit, a two wire bus used for connecting components to each other on electronic devices. I2C is just one option used for interconnecting devices. Other options  include. SPI, 1-wire, and Serial. When using devices for an embedded project we generally find SPI or I2C on devices. I tend to go for I2C devices when I can because they allow me to get away with using less wires to hook up.

When we do integrate a device it is handy to ensure the device is recognized by the system before we try to write code for it. There are tools that are already installed on the Edison called I2C tools. A part of the LM-Sensors project, I2C tools allow low level access to I2C busses and the devices connected to them.

Before we get started lets make sure our Edison is up to date. We can do this by logging in via ssh (or use the terminal) and executing the command:

configure-edison --upgrade

You need to have your Edison attached to a network.

Let’s get started. There are four commands we are interested in that are summarized in the table here:

Bus scanning i2cdetect
Device register dumping i2cdump
Device register reading i2cget
Device register setting i2cset

Bus scanning:  

We use the i2cdetect command to both find the busses available as well as the devices on the bus. If we execute

i2cdetect -l

we can get a list of the currently installed I2C busses. The output is as as follows:

The result of i2cdetect -l on Intel Edison.

The result of i2cdetect -l on Intel Edison.

We can see we have 8 I2C buses on the Edison. But according to the docs we are only able to use bus 1 and 6 on the mini-break out board. I have only used bus 1 in my projects so far. If we wanted to see the devices on bus 1, we would enter:

i2cdetect -r 1

and get something like the following output:

The result of i2cdetect -r 1. This gives us a dump listing the devices on i2c bus 1.

The result of i2cdetect -r 1. This gives us a dump listing the devices on i2c bus 1.

When executing this command we get the standard warning that executing this command may jack up our device. It’s true, we can cause errors if we run this command on some of the other busses, but it has always cleared up for me with a power cycle of the Edison. Usually the device just hangs.

Looking at the output we can see we have four devices on bus 1. At 0x18 we have a MCP9808 temperature sensor, 0x3C is a SSD1306 display, 0x48 is an ADS1819 ADC , and 0x77 is a BMP085 barometric pressure and temperature sensor.

Device register dumping:

Now that we know what devices are on the board (or more accurately all the devices we wired to the Edison are recognized by our system) we can start working with the devices. Each device has internal registers that control the device and provide output from the device. We use the i2cdump command to take a look at these registers. Let’s take a look at the MCP9808

i2cdump 1 0x18 w

The form of the command is 12cdump (bus | device address | option). The ‘w’ option give us word output which makes it easier (for me at least) to read the registers. Executing the command gives us output that looks like this:

i2cdump of our MCP9808 on i2c bus 1 in word format. There is a lot of register space for devices, but we see we only have 13 registers in our device.

i2cdump of our MCP9808 on i2c bus 1 in word format. There is a lot of register space for devices, but we see we only have 13 registers in our device.

To understand what we are seeing in the dump we need to study the data sheet for our MCP9808.

If we study the data sheet for the MCP9808 we can decipher the registers we dumped.

If we study the data sheet for the MCP9808 we can decipher the registers we dumped.

Keeping in mind that the MCP9808 is a little endian device, we see the 16 bit data registers storing their values with the least significant  byte (LSB) first and most significant byte (MSB) second. We just need to do a quick byte swap to look at register 0 to see the value is 0x001d. According to the spec sheet the MSB should always be 0x00 as well as the LSB bits 7-4. Bits 3-0 in the LSB are the pointer used to select which register we read and write to when communicating to the device. The MCP9808 is a surprisingly complex device, so we won’t look at all the registers. But a thorough explanation of the registers and their function begins on page 16 of the data sheet.

Read a specific register:

We could certainly read the registers from the output of  i2cdump but there is a specific command for reading a single register. If we wanted to read the the current temperature the data sheet tells us the register containing this value is 0x05. To read this register we would issue this command:

i2cget 1 0x18 0x05 w

The command is in the form i2cget (bus | device address | register | option) . This gives is the following output:

Use i2cget to read the current temperature from our MCP9808.

Use i2cget to read the current temperature from our MCP9808.

We get the requisite warning and then the little endian contents of the register. Swapping the bytes to 0xC152 and referencing page 25 of the data sheet we can get the current temperature in Celsius. Briefly —   Bits 15-13 are flags that are used when we set the device to monitor a high and low set point. They can tell us if a threshold was passed, and which one. We don’t have those set so the values are not significant to us in this case. Bit 12 of the word is the sign of the two’s compliment value, since it is 0 our value is positive. Stripping those four bits out we are left with 0x0152 which is the temperature value. The rest is a base conversion and a little math — the MSB is 1 decimal, the LSB is 82. So using the data sheet formula  –> 1 * 16 + 82 / 16  = 21 degrees Celsius. Thats a cool 69.8 F in the office today.

Set a register:

If we want to manually control the MCP9808 we can set its registers with the i2cset command. Before we use the command a little back ground.

The MCP9808 has two modes of operation — continuous conversion and shutdown mode. Shutdown mode puts the device into a low power state and , as you might have guessed, stops updating the temperature measurement. If we read the temperature register while the device is in shutdown we will retrieve the last measurement made. In a battery powered device it might be a good idea to shutdown the device and only power it up when we need to update the temperature. This is controlled by bit 8 of the config register located at 0x01.

We can use the following command to enter into shutdown mode:

 i2cset 1 0x18 0x01 0x0001 w

Remember that we need to swap the bytes in the word due to endianness. This can be tested by reading the temperature while heating up the sensor (I just put my finger on it). You can use i2cget or i2cdump to read register 0x05 of the device. To set the sensor to continuous conversion mode use this command:

i2cset 1 0x18 0x01 0x0000 w

This post covered the basics of verifying a device is detected on an I2C bus and determining if the device is functioning properly. We used the four I2C tools commands to detect the busses and devices installed, dump a device registers, read a specific register, and to write a register. With these four commands we can certainly see if our devices are working correctly as we implement an embedded system.

The i2c tools do have other modes of operation that I did not touch on here. These modes can be read about on the I2C tools docs page. Armed with these tools and the data sheet for a device we are ready to get coding.

Storing configuration options on the RFduino.

One of the common requirements for an IoT device is to be able to make configuration changes to the device without having to modify its source code.   Some of the changes we might make could be changing the device name, advertising data or interval, or perhaps some application specific startup options.  Any kind of change like this will require some kind of non-volatile storage to preserve the changes between power cycles.  On the RFduino we have the ability to store changes in an unused flash page (assuming our code doesn’t fill up the 128k we are allocated).

Lets get started. Here is the code. See the included comments for more information.

Based on RFduino example FlashStructue.ino
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
#include <RFduinoBLE.h>

//For flash access 
//Notice that the page is 251? User space is 124 to 251, inclusive. 
//This gives us our 128k for sketches. If you look at the RFduino 
//flash examples, you can see how to use the first unused page. 
//I like using the last page in user space, however, as I know it will not change.
//Loading the sketch will not overwrite our storage. 
#define FLASH_STORAGE 251 //Use the last page 
// double level of indirection required to get gcc
// to apply the stringizing operator correctly
#define  str(x)   xstr(x)
#define  xstr(x)  #x
//This is the data structure we will use. 
//Since the max transfer is <20 bytes over the radio, 
//the lazy way is to make all the arrays 20 bytes long. 
struct data_t
 char name[20];
 char advertisement[20];
 int interval;
 int update_delay;
 char config[20];
 char valid[6];


//A method to display the data structure. Force is used to 
//have the in_memory config display its contents. 
void dump_data(struct data_t *p, bool force = false)

 if (String(p->valid) == "valid" || force)
        Serial.print("Name = ");
        Serial.print("Advertisement  = ");
        Serial.print("Interval  = ");
        Serial.print("Update Delay  = ");
        Serial.print("Config  = ");
        if (force){
            Serial.print("Valid = "); 
    else {
        Serial.println("No valid data in flash");


//This is a pointer to the data stored in flash. This is read only.
data_t *in_flash = (data_t*)ADDRESS_OF_PAGE(FLASH_STORAGE);
//This is our config struct. We will use this to store our config options in memory for normal operations. 
//When writing, the contents of this struct will overwrite the in_flash data. 
data_t config = { { "Probe" }, { "Probe_adv" }, 500, 1, { "Sample config data" }, { "" } }; //default data

void setup()   {

    //If we have config data in memory, use it instead of default.
    //This will be false initially, or when the flash page is erased. 
    if (String(in_flash->valid) == "valid")
        //Since the struct in flash contains char arrays we need
        //to do a quick array copy to move the in_flash config to 
        //our config struct. 
        //This could be probably done better. 
        for (int i = 0; i < 20; i++)         {             
    [i] = in_flash->name[i];

        for (int i = 0; i < 20; i++)         {             
              config.advertisement[i] = in_flash->advertisement[i];

        for (int i = 0; i < 20; i++)         {             
              config.config[i] = in_flash->config[i];

        for (int i = 0; i < 6; i++)         {             
              config.valid[i] = in_flash->valid[i];

        config.update_delay = in_flash->update_delay;
        config.interval = in_flash->interval;


    //RFDuino Specific. 
    // this is the data we want to appear in the advertisement
    // (if the deviceName and advertisementData are too long to fix into the 31 byte
    // ble advertisement packet, then the advertisementData is truncated first down to
    // a single byte, then it will truncate the deviceName)
    RFduinoBLE.advertisementData = config.advertisement;
    RFduinoBLE.advertisementInterval = config.interval;
    RFduinoBLE.deviceName =;

    // start the BLE stack


void loop() {
//Nothing going on here. 

//We will use this built in event to handle our received data.
//This is a way to accept commands, but not the only way. 
//This will let anyone that connects change options. 
void RFduinoBLE_onReceive(char *data, int len)
    //The first byte is cast to int for use in our switch
    int command = data[0];

    String string = "";
    //Get the string from the rest of the array, if there is one. 
    if (len > 1){
        for (int i = 1; i < len; i++){
            string += String(data[i]);
        string.trim(); //Remove excess spaces. 

    Serial.println("Received data over BLE");
    //We will print our command and string to the console

    switch (command)
        char buf[20]; // this is used when setting string variables.
    case 0:
        //Don't do any thing. 
    case 1:
    case 2:
    case 3:
    case 10:
    case 11: 
        //rename device -- save to flash 
        string.toCharArray(buf, string.length() + 1, 0);
        RFduinoBLE.deviceName = buf;
        for (int i = 0; i < string.length(); i++){
  [i] = buf[i];
        //Set the rest of the array to null 
        for (int j = string.length(); j < 20; j++){
   [j] = 0;
    case 9:
        //change config -- save to flash 
        string.toCharArray(buf, string.length() + 1, 0);
        for (int i = 0; i < string.length(); i++){
            config.config[i] = buf[i];
        //Set the rest of the array to null 
        for (int j = string.length(); j < 20; j++){
  [j] = 0;
        //TODO -- apply config settings to running sketch
    case 32:
        //Used to clear the receive buffer. The buffer 
        //will contain the contents of the last received message
        //unless the current message overwrites it. This can add 
        //characters from the previous message to the current message 
        //and pretty much jack up what you are trying to accomplish. To 
        //prevent this I send 20 spaces after a message to clear the 
        //receive buffer and then trim them from the current message. 
    case 255:
        //Allows reset of the system.

        Serial.println("Nothing to do"); 


//This is where the work is done. 
void erase(){
    int rc;
    Serial.print("Attempting to erase flash page " str(FLASH_STORAGE) ": ");
    while (!RFduinoBLE.radioActive);
    while (RFduinoBLE.radioActive);
    rc = flashPageErase(PAGE_FROM_ADDRESS(in_flash));
    if (rc == 0)
    else if (rc == 1)
        Serial.println("Error - the flash page is reserved");
    else if (rc == 2)
        Serial.println("Error - the flash page is used by the sketch");
    in_flash = (data_t*)ADDRESS_OF_PAGE(FLASH_STORAGE);
    Serial.println("The data stored in flash page " str(FLASH_STORAGE) " contains: ");


void write(){
    //If we don't have valid in the config object - add it here. 
    //This lets us determine if the in_memory struct has valid 
    //data in it or not. 
    if (String(config.valid) != "valid"){
        char valid[6] = { "valid" };
        for (int i = 0; i < 6; i++){
            config.valid[i] = valid[i];

    int rc;
    Serial.print("Attempting to write data to flash page " str(FLASH_STORAGE) ": ");
    while (!RFduinoBLE.radioActive);
    while (RFduinoBLE.radioActive);
    rc = flashWriteBlock(in_flash, &config, sizeof(config));
    if (rc == 0)
    else if (rc == 1)
        Serial.println("Error - the flash page is reserved");
    else if (rc == 2)
        Serial.println("Error - the flash page is used by the sketch");
    in_flash = (data_t*)ADDRESS_OF_PAGE(FLASH_STORAGE);
    Serial.println("The data stored in flash page " str(FLASH_STORAGE) " contains: ");


void read(){   
    Serial.println("Data in flash");
    Serial.println("Data in memory");
    dump_data(&config, true);

As you can see, most of the work is done in the last three functions — read(), write() and erase(). The write function erases before it writes, but an erase function is needed in case we mess up our saved data and need to clean house.

To test this we can use a program similar to Punch Through’s Light Blue (for IOS and Mac ) or any BLE app of your choosing. We simply flash the code to our RFduino the normal way (using the Arduino IDE) and open up the serial monitor.


Here is Punch Through LightBlue connected to our RFduino.

Probe, in this case, is the RFduino running our sketch. The write characteristic is 2220 for our device. Be sure to send hex, it makes it easier to get the correct command. As noted in the code above, messages received are character codes that are cast to ints. So of we use ASCII 1 when trying to get command 1 – read flash – we end up sending character code 49. Sending 0x01 in hex will get us 1.

Sending 0x01 hex will trigger the read command.

Sending 0x01 hex will trigger the read command.

My experience here is that when we try to erase or write the flash with the radio on the RFDuino will reboot — likely due to interrupts from the radio not being handled while erase and write operations are under way.  So what we need to do is turn off the radio before the write or erase and turn it back on after. This causes our application to lose the connection to the device causing us to have to reconnect. This can be handled silently in any app we create, but using LightBlue we will need to reconnect. Write (command 3) will write the contents of the config array to flash.

The result of a write command. The contents of the config array is written to flash.

The result of a write command. The contents of the config array is written to flash.

If we wanted to change the device name, for example, we would use command 11 (0x0B) and provide the name  in a character codes. If we wanted to change the name to “new” it would look like this  — 0x0B4E6577.  This also causes us to reconnect.

We can change the device name by send the new name in character codes

We can change the device name by send the new name in character codes

Other options can be changed in a similar fashion.

The only gotcha to look out for is ensuring strings sent to the device are long enough to clear the receive buffer. For example: If we change the name of the device by sending the string “NewDevice” (in character codes) and then send a string to change the advertising name of “Hello”, the resulting string will be “Hellovice” .  This is because the receive buffer does not flush between messages. We can get around this by sending a string of 20 0x32 (spaces) after a string to flush text out of the buffer. Or we can add an end of line character and check for it upon receipt.

So there you have it. This is a fairly simple way to allow over the air configuration changes to persist on your RFduino device. The code supplied here can certainly be refactored and enhanced to allow more functionality. Some things I plan on adding are password access to config changes and end of line checking.

Please feel free to contact me if you have questions.

Intel Edison and I2C sensors with XDK

The Intel Edison is becoming a popular system to use for IOT devices. Despite its small form factor it is a surprisingly capable platform. This makes the Edison a good choice for interfacing with sensors.

I like the Edison mini breakout board over the Edison kit for Arduino because of the form factor. The  mini breakout board provides USB connectivity, power input and a battery charging circuit to the Edison that covers most the of my requirements for devices.

The drawback of the mini breakout is that you need to either solder in some wires or add a header to the break out board to access i/o for the Edison. Also, the Edison I/o on this board operates at 1.8 volts while most sensors operate at 3.3 volts or higher so a level converter is needed.

The Intel Edison, on the mini breakout, supports various types of i/o but the one we are interested in today is I2C  Inter-Integrated circuit is a two wire serial protocol that is used by components to transfer data between one-another. On the mini-break out board we will be using I2C bus number 1.

Here the Edison is connected to a breadboard containing the MCP9808. There are other devices on the board.

Here the Edison is connected to a breadboard containing the MCP9808. There are other devices on the board.

Here is one way to make the i/o needed for connection available.

Here is one way to make the i/o needed for connection available.

Parts list:

  1. Intel Edison mini breakout board kit.
  2. Sparkfun bi-directional level shift converter.
  3. Adafruit MCP9808 temperature sensor board.
  4. Mini bread board.
  5. Solderless bread board jumpers. 
  6. Dupont male to female cable. 
  7. 90 degree dual row header. 

Items 6 and 7 are optional – you could just solder some wire onto the Edison if you prefer, or use some other type of pin headers.


Edison                     Level Shifter                     MCP9808

J17  – 8                          LV1

NC                                 HV1                                  SDA

J18 – 6                           LV2

NC                                HV2                                   SCL

JP19 – 2                         LV — 1.8 volt

JP20 – 2                         HV — 3.3 volt                   VDD

JP19 – 3                        Both grounds                  Ground

The connection looks something like this. The level shifter and MCP9808 are in the center of the yellow board.

The connection looks something like this. The level shifter and MCP9808 are in the center of the yellow board.

With this connection we are ready to code.

I will be using the Intel  XDK IOT edition  to read values from our temp sensor. If you have not used the XDK you can learn how to get started here. Just create a blank project and paste the following code in. Running the code will display the temperature in the console every second.

function char(x) { return parseInt(x, 16)}; // helper for writing registers

var mraa = require('mraa'); //require mraa
console.log('MRAA Version: ' + mraa.getVersion()); //write the mraa version to the Intel XDK console

var x = new mraa.I2c(1); //We will use a device in I2C bus number 1
x.address(0x18); //Default for MCP9808 is 0x10

//x.writeWordReg(char('0x01'), char('0x0100')); // Controls sleep mode for the temp sensor.


function periodicActivity()

var t = x.readWordReg(char('0x05')); // 0x05 is the register for the current temp.
//The byte order of words is not the same between Edison and the MCP9808
//The edison stores the most significant byte first - big endian, where the
//MCP9808 stores the lowest byte first -- little endian.
//Here is a wikipedia article on endianness. 
var s = ((t & 0xFF) << 8) | ((t >> 8 ) & 0xFF); //swap the bytes.
var r = s & 0xFFF; // Mask of the control bits to get the temp value
r /= 16.0; // dividing by 16 will give us the temp in celcius as long as the temp is above 0.

s = r * 9 / 5 + 32; //get the farenheit value.

console.log(r + " C " + s + " F"); //log the values

setTimeout(periodicActivity,1000); //do it again in a second.

For a more thorough explanation of the MCP9808 control registers see it’s data sheet.

And there we have it. It is not a very complex thing to interface an I2C sensor with the Intel Edison. We just need to use a level shifter and connect the thing up. Using the XDK it is fairly easy to read the temp data and control the MCP9808. The complexity comes in when more complex devices are integrated. It can take some time studying the data sheet of a device to figure out how to get everything working correctly.

There are other options for interfacing with I2C and other devices, but MRAA is the easiest in this case as it is already installed. If our sensor had been in the UPM library we could have used a predefined class to operate it.

Beagle Bone Black, Relays and Bonescript.

One of the common things to do with an embedded system is to control a high voltage device with the low voltage signal from a microprocessor. The easiest way to do this is with a relay either of the electronic or mechanical type.

In my application I want to switch a 12 volt vacuum pump on and off and open a 12 volt bleed valve to cycle pressure on and off. When the system boots or there is no 5 volt power I want the pump to be off and the bleed valve to be closed. This means I will need to use the normally open connections of the relay to control the pump and  valve. This poses a couple problems for us, which I will get in to later.

First, lets hook the relay board (Sain Smart Relay Specification) to the BBB. I chose to use P8_11 and P8_12, with 12 controlling relay 1 and 11 controlling relay 2. So make these connections:

P8_11 –> IN2

P8_12 –> IN1

P9_01 –> Ground

P9_07 –> VCC

If we wire the relays directly to the BBB we have a problem.

If we wire the relays directly to the BBB we have a problem.

When we power up the system the first problem shows up. We hear the relays chatter and notice the indictor leds are dimly lit. The issue is that at power on the pin mode is not set and the P8_11 and P8_12 are floating at a value that is neither TTL high or low. This is a problem since we are controlling a pump we don’t want the relays to momentarily energize and send power to energize it.

With the pins on the BBB floating the relays are neither on or off[

With the pins on the BBB floating the relays are neither on or off.

We can exercise the relay with the following script. Just paste this in cloud9 and step through it.

var b = require('bonescript');

var relay1 = "P8_12";
var relay2 = "P8_11";
var c = 0;
//Set pinMode causes output to go to 0, which activates our relay!
//We really need the relay off when low.
b.pinMode(relay1 , b.OUTPUT);
b.pinMode(relay2 , b.OUTPUT);

//Just alternate the relays on/off every second.
b.digitalWrite(relay1 , c % 2 == 0 ? b.HIGH : b.LOW);
b.digitalWrite(relay2 , c % 2 == 0 ? b.HIGH : b.LOW);
}, 1000);

When we set the pin mode we see the relay activating again. Pin mode sets the pin to TTL low initially, which in this case activates our relay. It is clear we need to add a small circuit between the BBB and the relay board to get this to work the way we want it to. We will add a 74LS04  hex inverter and  a pair of 500 ohm pull down resistors. The circuit is shown here (GND on pin 7, VCC pin 14):

We will add pull down resistors and a 74LS04

We will add pull down resistors and a 74LS04

So go ahead and shutdown the BBB and wire in the 74LS04.  Unused inputs on the 74LS04 should be grounded as per the manufacturers recommendation.

With the inverter and pull downs installed the relays behave as they should.

With the inverter and pull downs installed the relays behave as they should.

When we power up the BBB we will see the problem of the floating TTL is solved via the pull down resistors, and the inverter is translating that low to a high for the relay input. This keeps the relay off. Now we can run the test script above again. If you step through it you will see that setting the pin mode no longer causes the relay to activate. Writing a low to P8_11 and P8_12 will cause the relays to deactivate, a high to these pins will activate the relays. This meets our design goal.

So we see with minimal circuitry we can easily interface the BBB to a relays. The resistors and 74LS04 can be found in any electronics surplus store for pennies, or purchased form Mouser, Digi-Key or your favorite supplier.

Next time I will discuss how to control this relay setup via a web ui with bonescript.

Get Wi-Fi working on Rev. C BeagleBone black.

I recently started using the BeagleBone Black (BB) for a hardware design project. I don’t have to tell you what a dynamite little board it is. An Arm pc with I/O and USB for less than $70? I’ll take 2. But I find the documentation for the Rev C board with the Debian distribution a little sparse. Most of the documents I find concern either an earlier rev of the board or the Angstrom distro, or both. In the case of  getting Wi-Fi  to work instructions (I found) on the web simply would not work. However, since we are using  Debian getting Wi-Fi working reliably is really pretty easy. This is what I will document here.

To get started, first update your BB to use the latest Debian distro. This is well documented here — update instructions.

I am using the Debian image dated 2014-09-03.

I am also using the UWN100 USB adapter –> Compact USB Wi-Fi Adapter for BeagleBone Black . Luckily, the drivers for this device are included in the current release of Debian.

Go ahead and insert the USB Wi-Fi adapter and start the BB. It would be best to use the 5 volt adapter along with the USB connection. Login to the device as root via ssh.

First — let’s find the adapter. Issue the command:

ip a

It should give an output similar to the following:

root@beaglebone:~# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet scope host lo
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: eth0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc pfifo_fast state DOWN qlen 1000
    link/ether d0:39:72:4a:83:cb brd ff:ff:ff:ff:ff:ff
3: ra0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
    link/ether 00:0c:43:00:ca:44 brd ff:ff:ff:ff:ff:ff
    inet brd scope global ra0
    inet6 2601:9:8380:71:20c:43ff:fe00:ca44/64 scope global dynamic
       valid_lft 345584sec preferred_lft 345584sec
    inet6 fe80::20c:43ff:fe00:ca44/64 scope link
       valid_lft forever preferred_lft forever
4: usb0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
    link/ether c2:2e:5d:00:a5:9b brd ff:ff:ff:ff:ff:ff
    inet brd scope global usb0
    inet6 fe80::c02e:5dff:fe00:a59b/64 scope link
       valid_lft forever preferred_lft forever

From this we can see that our Wi-Fi adapter is named ra0. This is a little different than what is described in most instructions on Wi-Fi.  Usually the name is wlan0.

Now  lets see what the status of ra0 is. Issue the command:


We should get an output something like the following:

root@beaglebone:~# iwconfig
ra0       Ralink STA  ESSID:"11n-AP"  Nickname:"MT7601STA"
          Mode:Auto  Frequency=2.412 GHz  Access Point: Not-Associated   
          Bit Rate:1 Mb/s   
          RTS thr:off   Fragment thr:off
          Encryption key:off
          Link Quality=100/100  Signal level:-47 dBm  Noise level:-47 dBm
          Rx invalid nwid:0  Rx invalid crypt:0  Rx invalid frag:0
          Tx excessive retries:0  Invalid misc:0   Missed beacon:0
lo        no wireless extensions.
eth0      no wireless extensions.
usb0      no wireless extensions.

This tells us the ra0 adapter is operational, but not connected to any thing. Now all we have to do is configure the adapter to start automatically and connect to our desired network on startup. To do this we edit the /etc/network/interfaces file. Using your favorite editor, modify this the interfaces file and add the following:

auto ra0

iface ra0 inet dhcp

     wpa-ssid “your-ssid”

     wpa-psk  “your-psk passphrase”

This assumes you are using WPA2 security on your network. You are right? right? Save this and exit the editor. Next we just need to bring the interface up. Enter the following:

ifup ra0

You should  see something like this:

root@beaglebone:~# ifup ra0
Internet Systems Consortium DHCP Client 4.2.2
Copyright 2004-2011 Internet Systems Consortium.
All rights reserved.
For info, please visit
Listening on LPF/ra0/00:0c:43:00:ca:44
 Sending on   LPF/ra0/00:0c:43:00:ca:44
 Sending on   Socket/fallback
 DHCPDISCOVER on ra0 to port 67 interval 8
 DHCPDISCOVER on ra0 to port 67 interval 19
 DHCPREQUEST on ra0 to port 67
 bound to -- renewal in 3823421 seconds.

Now we are all set. We can now browse to our BB via the Wi-Fi adapter and dispense with a hardwired connection to the device.

Since we have the network starting automatically and connecting up we can add time sync via ntp. This is as easy entering the following:

apt-get install ntp.

Now your BB will have the correct time set on boot.

So there you have it. Not all that complicated once you know how. I have had very consistent results with the UWN100 adapter, so far it has started every time and doesn’t drop out on me.

Thanks for reading my very first blog post. I’ll try to get the formatting better as I blog some more. Check back to find out what I am up to with the Beagle Bone Black and other embedded devices I am using for projects.

Next up — controlling the Sain Smart four channel relay board with BoneScript.