Tag: i2c

UPM library for MCP9808 in XDK.

If you have read my blog at all you may be beginning to think I have a fetish for the MCP9808. Well, maybe a little, but for a good reason. If we look it’s data sheet we see the MCP9808 is a surprisingly complex device. Some of the features the data sheet lists:

  • Accuracy:
    •  ±0.25 (typical) from -40°C to +125°C
    • ±0.5°C (maximum) from -20°C to 100°C
    • ±0.5°C (maximum) from -20°C to 100°C
  • User-Selectable Measurement Resolution:
    • +0.5°C, +0.25°C, +0.125°C, +0.0625°C
  • User-Programmable Temperature Limits:
    • Temperature Window Limit
    • Critical Temperature Limit
  • User-Programmable Temperature Alert Output
  • Operating Voltage Range: 2.7 V to 5.5V
  • Operating Current:  200 µA (typical)
  • Shutdown Current: 0.1 µA (typical)
  • I2C bus compatible

These features make the MCP9808 ideal for a variety of temperature monitoring applications when being precise would be an advantage.

The devices I have been using I got from Adafruit.  They have a driver library for the Arduino  that is useful, but I want to use the device with Linux based IoT platforms such as the Intel Edison or the Beaglebone Black. Both of these platforms support the Intel UPM library , so it made sense to add a driver for the MCP9808 to that. I implemented the driver and added a pull request to have it incorporated into the Intel UPM library. The MCP9808 was accepted in to version 4.0 of the library and should be available after upgrading the version of MRAA on your board. If not — I have a blog post that lists various ways to update MRAA/UPM on your boards. (UPM is very easy to compile).

I provided a C++ program to the UPM repo that exercises all of the implemented functions.  You can copy the code from that example into an Intel iotdk-ide project (Eclipse) and run it from there. But I didn’t include a very good example for Nodejs. That brings us to the purpose of this post — I want to go over how to use the MCP9808 in Nodejs with XDK on the Edison (or compatible) MRAA board.

In order to give a proper demo I created a Nodejs/Socket.io app that illustrates all the implemented features of the MCP9808 UPM driver. It is setup as an XDK project so you can download it and open it in the XDK ide to run it on your Edison. Before you run it open the server.js file and change the line

var temp = new mcp.MCP9808(1) 

to whatever i2c bus you are using. I generally use a handmade boardthat extends the Edison Mini breakout board so I use bus 1. It will be bus 6 on the Arduino or DFRobot breakout, 0 on the Galileo.

When you download and start the app on the board browse to it’s url :8085 (like edison.local:8085) and you should see something that looks like this:

mcp9808_default_celcus

This is a single page app that uses socket.io to communicate with the Edison. The Edison serves the app via Express and the UI is basic HTML with the  Bootstrap framework. The four sections of the app (from right to left) are:

Temperature:

The radial gauge will reflect the current temperature at the device. The MCP9808 reports temperatures as Celsius (C), but the driver provides a conversion to Fahrenheit (F). The F values are calculated, so when we switch between C and F there are sometimes rounding differences. All the other temperature values in the UI will convert to C or F except for resolution. Resolution is always reported as C. I have the temperature limited from  freezing to boiling range, but the MCP9808’s range is little wider than that — see the specsheet.

Temp limits: 

These reflect the state of the three temperature limit registers detailed in section 5.1.2 of the spec sheet. These registers are used to allow the setting of temperature limits the MCP9808 can monitor so that we don’t have to constantly check for out of limit temps in code. The limit registers will cause flags in the ambient temp register (section 5.1.3)  to set if a threshold has passed. The driver exposes these bits as flags that can be read (see lines 158 to 167 of the server code) via a simple function call.  The thing to remember with these flags is that they reflect the state of the bits as of the most recent getTemp() call, so you need to read the temp before reading the flags.

I use the state of the Temp limit bits to set the color of the subheadings in the Temp Limit section. Since at power on the temps are set at 0 C, so the bits for TCrit and TUpper are set. Using the sliders to set a value greater than the current temperature will allow the TCrit and TUpper values to go green:

MCP9808_alert_off

The temp limit registers are also used in the Alert functionality.

Alert Control: 

Described in section 5.2.3 of the spec sheet,  the MCP9808 has the ability to control an alert pin when certain conditions arise.  On power up alerts are disabled. Default sets the device to comparator mode (section 5.2.3.1). The alert will assert (in this case pull low — you need to use a pull up on the pin) whenever one of the monitor temps are crossed. When the temp comes back within limits the alert will de-assert. In interrupt mode the just the TUpper and TLower are monitored. It the temp goes over a threshold the alert is asserted and will not de-assert until the temp goes back with range and the interrupt is cleared by code. These calls are illustrated in the server code.

Other:

The Other section contains controls for  resolution and hysteresis and display for device info, id and rev.

Resolution will always be reported in Celsius. Startup default is 0.0625 and equates to 4 temp measurements per second. Temperature resolution is detailed in section 5.2.4 of the spec sheet.

Hysteresis will be reported in either C of F depending on the driver settings. Hysteresis is detailed in section 5.2.2 and Figure 5-10. If the alerts are not clearing as you expect, check the hysteresis settings.

About:

The about menu item is a list of helpful links concerning the MCP9808.

What’s not Illustrated:

I didn’t use the sleep and wake function in this application. It is illustrated in the test file in the UPM library.

What’s not implemented: 

There were a couple of things that I did not implement. First: The temp monitoring registers can be locked by setting bits in the config register. It takes a power cycle to unlock them so I didn’t implement this. The alert can be set to assert active high. I didn’t add this functionality either. If either of these capabilities are needed you can just make an MRAA call directly to the device to do so. If you need help with either of these just ask me in the comments.

 

Scanning i2c bus 6 on Intel Edison

In a previous post I discussed using the LMSensors project programs to scan I2C buses on the Intel Edison. As I mentioned in that post, I only had luck scanning bus 1 on the Edison (which is only available on the mini breakout board).

Generally, when using i2ctools to scan bus 6 on the Edison the scan will run very slowly and no devices will be found. I have been a little confused by this in the past because occasionally I would find that I am able to scan the bus normally with those tools. I even went so far as to create a tool using the MRAA libraries to implement similar functionality with a Nodejs script. But I have found something interesting.

What I found was that i2c bus 6 is not configured when the system is started. If I have a node application running that uses i2c (which most of mine do) I find I am able to scan the bus with i2c tools without a problem. Also my tool — m2ctool — can serve to configure the bus so that it can be accessed by i2ctools.

To see what I am talking about try this:

  1. Start up your Edison and login. Ensure no programs are running.
  2. Enter the command i2cdetect -r 6. Press enter when prompted.
  3. The bus should scan very slowly and show no devices.
  4. Download and install m2ctool. See instructions on the read me to install.
  5. Enter the command m2ctool scan 6.
  6. The bus should scan normally and show any devices that are installed.
  7. Enter the command i2cdetect -r  6.
  8. The bus should scan normally and show any devices that are installed.

So if you need to probe i2c bus 6 on Edison but don’t have a program running that utilizes i2c, just run ‘m2ctool scan 6’ and then use i2ctools as you normally would. I have only tried this on Edison, I don’t know if this is true on other MRAA based systems.

The m2ctool has other features, I had intended it to stand in for i2ctools when dealing with MRAA based i2c buses. But given that all I need to do is initialize the bus by running a program once I could have saved myself a bit of work. For more info on m2ctool see the read me in the git hub repo.

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() {
 this.bus.frequency(mraa.I2C_STD);
 this.bus.write(this.tx_buf); 
 return this.bus.read(6);
 
};
//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, Socket.io 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 , socket.io 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('socket.io').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. Socket.io 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 socket.io 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 socket.io I was stumped for awhile trying to find the source file for the socket.io 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 socket.io 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();
2
3  socket.on('temp', function (data) {
4    var status = JSON.parse(data);
5    Gauge.Collection.get('temp').setValue(status.farenheit); 
6   });
7
8  function toggle_led(state){
9  
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 socket.io 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.