Friday 5 December 2014

Raspberry PI, sensors and DIoTY MQTT cloud

In a previous post I showed how to connect your Arduino to DIoTY's MQTT cloud infrastructure and make your sensor data instantaneously available to any MQTT client.

This time round, I'm going to work with a Raspberry PI and a DHT22 Humidity Temperature Sensor Module.  I will use MQTT to publish the values received from the sensor and use the new and improved DIoTY website to view the how warm and humid it actually is in my office!

We use following hardware for our example:

  • Raspberry PI B+ (but any model should do)
  • micro SD card (or the one matching your Raspberry PI model)
  • USB wifi nano
  • Type-T GPIO Expansion Board Accessory for Raspberry Pi B+
  • DHT22 Humidity Temperature Sensor Module
  • breadboard and some wires

I assume most readers already have their Raspberry PI up and running, but for those who are new to Raspberry PI, here's very brief how you get that far:
  1. Download the OS image from http://www.raspberrypi.org/downloads/ 
    • I used the Raspbian Debian Wheezy
  2. Copy the image to your SD card
    • check the volume of your SD card using diskutil list
    • unmount the volume (eg if the volume found is called disk1 then  execute diskutil unmountDisk /dev/disk1)
    • copy image with sudo dd bs=1m if=2014-09-09-wheezy-raspbian.img of=/dev/disk1 (adapt image name and volume name to match yours)
  3. Configure your Rasberry PI
    • startup your Raspberry PI with ethernet plugged in and log in with user pi and password raspberry
    • start configuration with sudo raspi-config
      • expand filesystem
      • change pi password to your own
      • change timezone to yours under internationalisation
      • finish with reboot and log in with your new password
  4. Update the OS
    • execute sudo apt-get update to get the latest (security) updates
  5. Configure the wifi (I'm using a wpa with  a wep key on my router)
    • execute sudo vi /etc/wpa_supplicant/wpa_supplicant.conf and add the following network section (filling in your values for ssid and hex web_key0):
      network={
         ssid="MyNetworkName"
         wep_key0=abcdef0123456789
         key_mgmt=NONE
        }
    • install wicd-curses
      • execute sudo apt-get install wicd-curses
    • plug in the wifi stick and configure by running sudo wicd-curses
      • configure network --> connect automatically using WEP key
Unlike with Arduino, with a Raspberry PI we can choose what programming language we'd like to use.  As I've been working a lot with Node.js lately and started liking it I'll do this exercise with Node.  

Node is not the ideal language when it comes to real time programming.  In fact, you won't find any pure Node modules that interact with the GPIO.  You do find libraries for this in C however which you can wrap in your Node module.  Even better, as Node.js is so well adopted in the open source community you'll often find those wrapper modules readily available for your sensor... and the DHT22 is no exception (see Node Package Manager website for node-dht-sensor).

So let's get started!

First we need Node and NPM installed.  There are several ways of doing this, but I prefer to do it the safe (or long, whichever you prefer) way.  Installing Node.js is done using the following set of commands:
     sudo apt-get install python g++ wget libssl-dev
     mkdir /tmp/nodejs && cd /tmp/nodejs
     wget http://nodejs.org/dist/node-latest.tar.gz
     tar xzvf node-latest.tar.gz && cd node-v*
     ./configure
     make
     sudo make install
Note that those make commands will take time!

Next install the Node Package Manager:
     curl https://www.npmjs.org/install.sh | sudo sh

The link above on the NPM node-dht-sensor package shows us how to install the module and gives also some sample code on how to use it.  All we have to do is adapting the sample program to start talking to our MQTT cloud service.  And yes, there is also an MQTT package available which does all the hard work on the MQTT side for us.  So lets create a new directory for our project in our home drive and install both packages required:
     cd 
     mkdir dht22 && cd dht22

The node-dht-sensor package requires the bcm2835, so let install that first:

     wget http://www.airspayce.com/mikem/bcm2835/bcm2835-1.38.tar.gz
     tar zxvf bcm2835-1.38.tar.gz
     cd bcm2835-1.38
     ./configure
     make
     sudo make check
     sudo make install

Now we can install both node packages:
     cd ~/dht22
     sudo npm install node-dht-sensor
     sudo npm install mqtt

The node-dht-sensor module comes with a test program located under ~/dht22/node_modules/node-dht-sensor.  Copy test.js to your root project folder ~/dht22 and make the changes as indicated below:

// Module node-dht-sensor demo
// Reads relative air humidity from DHT sensor

var fs = require('fs');

var mqtt = require('mqtt')
  , host = 'mqtt.dioty.co'
  , myUserId = 'xxxxxx@gmail.com'  // your DIoTY userId
  , myPwd = 'xxxxxx'               // your DIoTY password
  , client = mqtt.createClient(1883, host, {username: myUserId, password: myPwd});

var sensorLib = require('./node_modules/node-dht-sensor/build/Release/node-dht-sensor');

var sensor = {
  initialize: function() {
    this.totalReads = 0;
    return sensorLib.initialize(22, 4);
  },

  read: function() {
    var readout = sensorLib.read();
    this.totalReads++;
    console.log('Temperature: '+readout.temperature.toFixed(1)+'C, humidity: '+readout.humidity.toFixed(1)+'%'+
                ', valid: '+readout.isValid+
                ', errors: '+readout.errors);
    fs.appendFile('log.csv',
      new Date().getTime()+','+readout.temperature+','+readout.humidity+',"'+(readout.checksum ? 'Ok' : 'Failed')+'",'+
readout.errors+'\n',
      function (err) { });

    if (readout.isValid && readout.checksum) {
      client.publish('/'+myUserId+'/office/temp', readout.temperature.toFixed(1).toString(), {retain: true});
      client.publish('/'+myUserId+'/office/humidity', readout.humidity.toFixed(1).toString(), {retaintrue});
    }

    if (this.totalReads < 300) {
      setTimeout(function() {
        sensor.read();
      }, 500);
    }
  }
};

if (sensor.initialize()) {
  sensor.read();
} else {
  console.warn('Failed to initialize sensor');
}

We will run this with the command sudo node test,js but let's first look at the hardware side.  This as well is pretty easy.  The dht22 sensor module I have comes with a 4.7kOhm resistor build in, but if that's not the case for you then you should put one in between the VCC and Data line of your sensor.  Apart from that we have just 3 connections to make:

  • Connect the VCC (+) of your sensor to the 3.3V (pin 1) or your Raspberry PI
  • Connect the GND (-) of your sensor to the GND (pin 39) of your Raspberry PI
  • Connect the Data (S) of your sensor to the GPIO4 (pin 7) of your Raspberry PI


So now that we are all set up and as specified below, we will run the test script with the command sudo node test.js
This start writing temperature and humidity levels to:
  • the console
  • a log file
  • our DIoTY MQTT Cloud infrastructure
This last we can see on www.dioty.co by following the "My DIoTY" link you will see after signing in.

7 comments:

  1. after running node test.js output says - createClient is deprecated, use connect instead and no data is parsed to webpage

    ReplyDelete
  2. the "createClient is deprecated, use connect instead" is a warning and not the reason why it is not working
    check your javascript code, your connection details (user-id/password) and make sure your topic starts with /yourEmailAddress/

    ReplyDelete
  3. Thanks for fast response, I checked everything twice - I have working iphone app with webpage - I can post message in my topic and I see it in phone - from pc looks like it can't send message to topic - from my box i see that there is connection to dioty.co host and port - but messages I can't see, also csv file is filling messages localy

    ReplyDelete
    Replies
    1. Hi Raivis, there was indeed a problem with the code:

      readout.temperature.toFixed(1) is not a string (same for humidity) and so the publications didn't work. I've updated the code above to use the toString() function: readout.temperature.toFixed(1).toString()

      This successfully publishes to the DIoTY MQTT broker.
      Thanks for pointing this out!

      Delete
  4. This comment has been removed by the author.

    ReplyDelete
  5. Thanks to Ward and a few others, I've got this working in my RasPi setup. Its taken some doing though, as the DHT22 or, AM2302 (in my case) is exceedingly sensitive and tends to lock up if you so much as look at it the wrong way… There's loads of postings about this sensor on the if you want to understand more about that, I'll save it for another time.

    To try and get my version of Ward's code working on my set up reliably, I ended up getting another sensor just to check it wasn't a problem with my first AM2302. I then reinstalled the Pi with the latest version of Raspian. After that, finally I rebuilt all the connections on my breadboard and that seemed to stabilise the sensor and I got consistent readings again.

    FWIW - what also worked for me particulary well, was to run a Cron job to read the values instead of using the setTimeout in the script (which kept borking the sensor when it was run in the background). Cron isn't as sexy, but it made things simpler.

    I'm going to move on to another sensor after all the agro with this. Here's my code, don't forget to substitute USERNAME, PASSWORD and ROOTTOPIC with your own setup. Oh, and this code gets rid of the 'createClient is deprecated' message, which was annoying me!

    // Module node-dht-sensor demo
    // Reads relative air humidity from DHT sensor

    var mqtt = require('mqtt');

    var options = {
    port: 1883,
    host: 'mqtt.dioty.co',
    clientId: 'guest',
    username: 'USERNAME',
    password: 'PASSWORD',
    keepalive: 60,
    reconnectPeriod: 1000,
    protocolId: 'MQIsdp',
    protocolVersion: 3,
    clean: true,
    encoding: 'utf8'
    };

    var client = mqtt.connect(options);

    // Include required modules
    var sensorLib = require('node-dht-sensor');

    // Read from sensor & send to MQTT
    var sensor = {
    initialize: function () {
    return sensorLib.initialize(22, 4);
    },
    read: function () {

    // Read from sensor
    var readout = sensorLib.read();
    var temperature = readout.temperature.toFixed(2);
    var humidity = readout.humidity.toFixed(2);
    console.log('Temperature: ' + temperature + ' °C, ' +
    'Humidity: ' + humidity + ' %');

    // Send to MQTT Host
    client.publish('/'+options.username+'/ROOTTOPIC/temp', readout.temperature.toFixed(1).toString(), {retain: true});
    client.publish('/'+options.username+'/ROOTTOPIC/humidity', readout.humidity.toFixed(1).toString(), {retain: true});
    console.log("MQTT Message is published");
    // Disable recurrence, will also run from Cron job to avoid errors
    client.end();

    // setTimeout(function () {
    // sensor.read();
    // }, 3000);
    }
    };

    // Initialize DHT22 sensor
    if (sensor.initialize()) {
    sensor.read();
    } else {
    console.warn('Failed to initialize sensor');
    }


    Good luck everyone!

    ReplyDelete
    Replies
    1. Glad you got it to work and thanks for posting this!

      Delete