Although I've seen several weather station projects around the web for a while, I can't say that I've seen one completely wireless that can be spread in different rooms in a house or in different offices inside a company, without dealing with network wires or AC cables.
After seeing for the first time the Texas Instruments CC2650STK sensor, the idea of building this project went from a thought to a perfect reality. I will tell you from the beginning that there are no cables involved, nor soldering, it's more like a project built by a software developer (this is what I am :-)).
Due to the fact that I'm a .net software developer, my first thought was to build this project using the Windows 10 Core IoT OS and Universal Windows 10 SDK but after doing a bit of a research I stumbled upon two things that I didn't liked:
- the current Windows 10 Core IoT insider preview version doesn't support the on-board wireless and Bluetooth adapters that come with Raspberry PI 3;
- in order to connect to a Bluetooth device using the Universal Windows 10 SDK you should manually pair it first, otherwise, though the device is discoverable from code, the app cannot connect to it (there are many issues open on Stack Overflow and other specific sites) - and my intention was from the beginning to offer a solution that can add multiple sensors without any interaction.
So, after evaluating what options do I have, I took the decision of installing Raspbian OS and write the app in node.js, because I've seen that I have all that I need from the support libraries point of view.
In order to be easy for everyone who reads this project tutorial to implement it, I will take it step by step but if you think that any of this step doesn't apply to you, you can skip it (I will put a special mark near the ones that are mandatory*).
Step 1. Install Raspbian and Update ItTake the easy track and use Noobs to install Raspbian on your Raspberry PI 3 - you can download it from here: https://www.raspberrypi.org/downloads/noobs/ where you can also find the necessary instructions.
After you finish with the installation and you are able to connect to your Raspberry using any SSH client (I'm using putty), it's a good idea to start updating the OS to the latest available libraries:
pi@raspberrypi3:~ $ sudo apt-get update
pi@raspberrypi3:~ $ sudo apt-get upgrade
Step 2. Update node.js to 4.X VersionMost probably, if you use the default Noobs installation for Raspbian, you will see that the node.js installed version is pretty old (something like 0.10.XXX) and we need something newer than this.
First, in order to check the installed node.js version use the following command:
pi@raspberrypi3:~ $ node -v
If the displayed version is less than 4.X, then is time to update it - there are many ways to do this but from all of them, this is what I found to be the easiest one:
pi@raspberrypi3:~ $ curl -sL https://deb.nodesource.com/setup_4.x | sudo -E bash -
pi@raspberrypi3:~ $ sudo apt-get install -y nodejs
(all the instructions were taken from here: http://askubuntu.com/questions/672994/how-to-install-nodejs-4-on-ubuntu-15-04-64-bit-edition)
Sometimes, in order to be sure that everything is set correctly, is a good idea to restart the raspberry:
pi@raspberrypi3:~ $ sudo shutdown -r now
After this, check the installed version one more time with the node -v command to be sure that everything was set correctly.
Step 3. Install node.js Package Manager (npm)In order to be able to install the required node.js dependencies (libraries) we need to have the node.js package manager (the so called npm) installed, so you have to run the following command:
pi@raspberrypi3:~ $ sudo apt-get install npm
Step 4. Install DependenciesThe main dependency that we need to implement this project is called sensortag (you can find info here https://www.npmjs.com/package/sensortag) and it has been implemented by Sandeep Mistry (thank you - you did a great job!).
Accordingly to the above specified page, first we need to install some other libraries, so we have to run the following commands:
pi@raspberrypi3:~ $ sudo npm install -g node-gyp
pi@raspberrypi3:~ $ sudo apt-get install bluetooth bluez libbluetooth-dev libudev-dev
pi@raspberrypi3:~ $ npm install noble
pi@raspberrypi3:~ $ npm install sensortag
Step 5. First Look at the Sensor and Connectivity CheckThough a lot can be said about the Texas Instruments CC2650 sensor that I choose for this project, I can only tell you the following things: a sensor that have a battery that can last for almost an year, connects to Raspberry PI 3 through BLE (Blueetooth Low Energy) protocol to up to 50 meters, easy to setup by only pressing a button and a very nice looking piece of hardware.
What is important to notice for now, is that this device has 2 buttons - one on the right side and one on the left side - in order to differentiate them easily, the one that we care for now is the smallest one (it has the power-on icon on it).
That button has multiple purposes but we use it just to start the device and then to make it "advertise" its presence to our "base" (Raspberry PI 3). Remember that anytime you need to connect to this sensor you have to press on this button and as a response the sensor will have a green led that will start blinking.
Now, that we have to sensor up and running, let's see if the base machine (RPI 3) can connect to it (discover it). First of all, please be sure that the green led is blinking and then start the following command:
pi@raspberrypi3:~ $ sudo hcitool lescan
At this moment the Raspberry will display all the Bluetooth devices that are in range and that have the "discover mode on". Among these devices you should see our CC2650 Sensortag.
Step 6. Microsoft Azure Required InfrastructureIn order to be able to collect the data produced by our Raspberry PI project, we have to choose where this data will go. From my personal point of view, I think that for this project, the best choice is the Microsoft Azure cloud infrastructure, and to be more specific, the Microsoft Azure IoT Hub. Please, remember that this is a just a Hub to which you send the data; in order to actually do something with this data, you have to plug-in other features offered by Microsoft Azure (Streaming Analytics Job - which gets the incoming data and sends it to a storage resource; Azure DocumentDB to store the data; Microsoft PowerBI to interpret the data; etc).
Because I don't want to deviate from the main purpose of this project, I will just mention the fact that you have to create a new Azure IoT Hub instance (it's free for less then 8000 messages per day) and take the connection string (you will need it a bit later).
Due to security reasons (and not only security reasons), you have to register each device that sends data to Azure IoT hub; the easy way to do this is to use the Azure IoT Hub Device Explorer (you can find all the instructions here).
First, run the Device Explorer and paste the connection string obtained earlier into the Connection string field and then press on the "Update" button.
After that, switch to the "Management tab", and press on the "Create" button (this will give you the possibility to register a new device to the given hub).
The primary and secondary keys are already populated with the values taken from the connection string, so you don't have to do anything there, but enter a human-readable value for the Device ID field (I entered home-meteo-device).
Now that we have the device registered, we have to take the device connection string (not to be confused with the Hub IoT Connection string).
In order to do this, once you pressed on "Create" button in the previous dialog, the devices list will be update with the newly registered one; in this list you have to identify your device and do a right click on that row and just click on "Copy connection string for selected device". Keep this value for the next step.
Step 7. Let's Write Some Code, I Say :-)First of all, let's create a folder where we will put our code, logs or whatever else we need for this project - let's call it "weather".
pi@raspberrypi3:~ $ mkdir weather
pi@raspberrypi3:~ $ cd weather
Also, as a personal preference, I choose to write the code using Visual Studio Code (which is a free editor offered by Microsoft) because it offers some very nice plugins - one of these plugins gives me the possibility to sync the code that I'm writing on my PC to a given folder on a remote machine (in our case, the Raspberry PI), so anytime I'm saving the edited file on my local PC, that file is getting uploaded automatically through SSH to the Raspberry PI (if there is anyone who wants some details about this procedure, please let me know).
After we have everything setup from the source code editor point of view, we create our files file - the list of dependencies that our project requires - this file should be called package.json (you can find it attached).
Though initially we installed some required dependencies for our project, which were directly related to the BLE communication, now we have to install also the libraries required to communicate with the Microsoft Azure cloud infrastructure (we will speak about this a bit later).
Based on the file that we already created (package.json), if we call the following command:
pi@raspberrypi3:~ $ sudo npm install
the node.js package manager will know what we need for our project and install the required libraries (be sure that you call this command after you entered the project's folder with "cd weather").
Because the entire application source code is attached to this tutorial, I will take it piece by piece to explain it but before that, I will tell you a bit about the way the data can be collected from this sensor.
The interaction with this sensor is made totally asynchronously, so once you call a method, you have to wait to get the response back in the given callback method. All the features of this sensor (temperature, pressure, humidity, luminosity, etc) works independently, so you have to decide what data you will collect and also how you send this data to the cloud; you have two possibilities:
- collect each sensor data individually and send it once you have it to the cloud (e.g.: subscribe to each sensor notification and send the result);
- read the sensor one by one and once you have all the data, package it and send it to the cloud.
Just for fun and without getting into any discussion if it's OK or not, I choose the second option; but at this point I'm having the first challenge - because everything is async, how do I collect all the data from all the sensors and at the end, send the data? This comes in another great node.js package called "async". This package offers a construction called "series" - executes a series of method and when these are done, do something (in our case, sends the data).
Now, let's speak about the source code.
First we have to instantiate the sensortag and async libraries:
var SensorTag = require('sensortag');
var Async = require('async');
Than we have to instantiate the microsoft azure libraries:
var Protocol = require('azure-iot-device-amqp').Amqp;
var Client = require('azure-iot-device').Client;
var ConnectionString = require('azure-iot-device').ConnectionString;
var Message = require('azure-iot-device').Message;
Now it's time to use the device connection string obtained in the previous step (again, please, use the device connection string - the one obtained from the right click action, not the initial hub connection string).
var connectionString = 'HostName=XXX.azure-devices.net;DeviceId=home-meteo-device;SharedAccessKey=YYY';
var deviceId = ConnectionString.parse(connectionString).DeviceId;
var client = Client.fromConnectionString(connectionString, Protocol);
Please, use the above mentioned connection string and change the value of the connectionString variable (the one that starts with 'HostName=...').
Now is time to open a connection to the Azure IoT Hub and once the connection is open, we start collecting data:
client.open(function (error, result) {
if (error)
{
console.log("Connectivity error: %s...", error);
return;
}
// .... this means that here our connection is open and we can start do something ...
});
So we have our connection open and now we can start the sensors' discovering process and connect to and setup each of them:
SensorTag.discoverAll(function (sensorTag) {
console.log("Connecting to %s...", sensorTag.id);
sensorTag.on('disconnect', function() {
console.log("Disconnected from %s!", sensorTag.id);
process.exit(0);
});
sensorTag.connectAndSetUp(function (error) {
console.log("Connected to %s...", sensorTag.id);
// ... we have our sensor ready to interact with ...
});
});
});
As I've said earlier, being into an async environment and dealing with independent readings from different sensor features, we have to wait until all features has been initialized and then start read values on an interval of 1000 ms (1 second).
Async.series([
function (callback)
{
console.log("Starting IR temperatures sensor for %s...", sensorTag.id);
sensorTag.enableIrTemperature(callback);
},
function (callback)
{
console.log("Starting humidity sensor for %s...", sensorTag.id);
sensorTag.enableHumidity(callback);
},
function (callback)
{
console.log("Starting pressure sensor for %s...", sensorTag.id);
sensorTag.enableBarometricPressure(callback);
},
function (callback)
{
console.log("Starting light intensity sensor for %s...", sensorTag.id);
sensorTag.enableLuxometer(callback);
}
], function () {
setInterval(function () {
var readings = { sensorId: sensorTag.id };
Async.series([
function (callback)
{
sensorTag.readHumidity(function (error, temperature, humidity)
{
readings.humidity = humidity;
readings.temperatureFromHumidity = temperature;
callback();
});
},
function (callback)
{
sensorTag.readIrTemperature(function (error, objectTemperature, ambientTemperature) {
readings.objectTemperature = objectTemperature;
readings.temperatureFromIr = ambientTemperature;
callback();
});
},
function (callback)
{
sensorTag.readBarometricPressure(function (error, pressure)
{
readings.pressure = pressure;
callback();
});
},
function (callback)
{
sensorTag.readLuxometer(function (error, lux){
readings.lux = lux;
callback();
});
}
], function()
{
readings.currentTime = new Date();
var message = new Message(JSON.stringify(readings));
client.sendEvent(message, function (error) {
if (error)
{
console.log(error.toString());
}
else
{
console.log("Data sent on %s...", readings.currentTime);
}
});
});
}, 1000);
});
Step 8. Run the CodeThe code is there, everything is prepared, so run the code and let's see some results. In order to do this, log on to the Raspberry PI console and run the following command:
pi@raspberrypi3:~ $ cd ~/weather
pi@raspberrypi3:~ $ sudo node meteo.js
Just as a side note - if you are not prepared yet to send the data to the cloud, make a small change to the last anonymous function (the one that starts with the readings.currentTime...) and just display the read values:
readings.currentTime = new Date();
var message = new Message(JSON.stringify(readings));
console.log(message);
If you don't see anything displayed on the screen after running the app, don't forget to press on the small "power-on" button.
That's it!
Please - if you have any questions or any remarks regarding this project, feel free to post comments and I will try to learn as much as possible from your comments.
Thank you!
Comments