Setup your Smart Home's Temperature and Humidity Monitoring Apps

1. Choose and setup hardware (sensors, receiver, Raspberry Pi) for a basic tracking tool on temperature and humidity

“How To: Smart Home setup to track indoor temperatures and humidity with sensors, Raspberry Pi, MQTT, Node.js, Vue.js and Chart.js”

Which (new) IoT hardware do we need for this setup?

  • RTL-SDR USB receiver: a cheap dongle that can be used as a computer based radio scanner for receiving live radio signals in your area (those gagdets receive signals from 500 kHz up to 1.75 GHz) (Amazon link)
  • Temperature / Humidity sensor: an even cheaper gagdet, that sends sensor data at 433 MHz into the area (e.g. as part of a “real” wheather station) (Amazon link)
  • Raspberry Pi 3 Model B: the small single-board computer servers as our heart and base station to receive and process sensor data, I have the Raspbian operating system installed. It could even serve as your web server later (if you are familiar with maintaining your own, but I will use a cloud service in this tutorial). (Amazon link)

At first, setup your sensors whereever you like in your appartment and connect your RTL-SDR receiver with the Raspberry Pi. How to receive the raw data out of the nearby area? A little open source software will help us with that: rtl_433 is a program (Github link) to decode traffic from devices that are broadcasting on 433.9 MHz. We install it on our Raspberry Pi:

# prepare os
sudo apt-get update
sudo apt-get upgrade
 
# install dependencies
sudo apt-get install libtool libusb-1.0.0-dev librtlsdr-dev rtl-sdr cmake
 
# clone rtl_433 Git Repository
git clone https://github.com/merbanan/rtl_433.git
 
# change to directory
cd rtl_433/
# make build folder
mkdir build
cd build/
# compile
cmake ../
# make
make
# install
sudo make install
# test if rtl_433 was properly installed
rtl_433 -h

After that we test if the program detects our RTL-SDR receiver with

rtl_433 -G

I had some trouble with the following error: _Kernel driver is active, or device is claimed by second instance of librtlsdr. __In the first case, please either detach or blacklist the kernel module _(dvb_usb_rtl28xxu), or enable automatic detaching at compile time.

The repository owner recommends

sudo rmmod dvb_usb_rtl28xxu rtl2832

in that case. If everything went well, the receiver is ready to get sensor data and rtl_433 helps with processing, so that after a few seconds you should get signals in the nearby area (yes, even temperature sensor data of your neighbor’s or sensor data of connected cars.)

This is image title

This is image title

Hurray, hardware is running!


2. Get and process your smart home sensor data with MQTT Broker Mosquitto and a Node.js backend application

“How To: Smart Home setup to track indoor temperatures and humidity with sensors, Raspberry Pi, MQTT, Node.js, Vue.js and Chart.js”

The main problem in this part is how to setup a stable and endlessly running task on a Raspberry Pi to continuesly receive the (correct!) sensor data. Secondly we want it to somehow process and transfer it to a service that works as our middleware to provide the frontend with JSON data to display. MQTT is the IoT protocol to go with but how can a MQTT broker like Mosquitto work with a Node.js backend properly?

Setup a web server backend with Node.js and Express.js

Multiple solutions would be possible here, but I decided for a Javascript Node.js backend which will be run with Express.js. Make sure node and npm are ready on your machine or install them first.

node -v
npm -v

Setup a project folder and a make a subfolder “server”. Create a new file “server.js” within and code a basic backend service. We will upgrade its functionality later.

const express = require('express');
const app = express();

// test
app.get('/', function (req, res) {
 res.send('hello world!');
});

app.listen(3000, () => console.log('App listening on port ', 3000));

Don’t forget to install the npm package express in your console and start the application!

npm init
npm install express --save
node server.js

Go to your browser and check if your web server works on http://localhost:3000.

Setup the MQTT broker Mosquitto

Why MQTT? The protocol is a lightweight publish/subscribe messaging transport solution which is very popular in the IoT field. We will use a MQTT broker as the control center to receive raw sensor data from our rtl_433 program, we installed in the previous chapter and forward them to our web server. Mosquitto is a common MQTT broker and is installed and tested on or Raspberry Pi with

sudo apt install -y mosquitto mosquitto-clients
mosquitto -v

The broker will be accessable to clients on mqtt://localhost:1883.

Setup a bash script that pipes received data to Mosquitto

The Raspberry Pi now gets the important task to not only start rtl_433 to decode traffic from devices that are broadcasting on 433.9 MHz manually, but to start this task on every reboot automatically. For that, we create a cronjob with the tool crontab, which should be installed on our system.

crontab -h

On your Raspberry Pi create a new file “tsh_raspberry_script.sh” (or whatever you want) in your pi home folder and make it executable from your terminal:

chmod +x tsh_raspberry_script.sh

After that, open the file in a text editor and add the following bash script. It will start rtl_433 and pipe the output in JSON format to Mosquitto, where it will be published in the topic “home/rtl_344”. Don’t forget to close and safe the file.

#!bin/bash
/usr/local/bin/rtl_433 -F json -M utc | mosquitto_pub -t home/rtl_433 -l

Now we can set up a new cronjob which will execute the shell script on every Raspberry Pi reboot. Open up a terminal:

# edit crontabs of user "pi"
crontab -e

# a text editor will open and load all existing cronjobs, add
@reboot sleep 60 && sh /home/pi/tsh_raspberry_script.sh

Build a simple database with lowdb and Node.js

In development mode a very basic database will suffice our requirements, that’s why I used lowdb (Github link) to store the sensor data on localhost first. Lowdb is based on a JSON file in our project folder.

npm install lowdb --save

In your server.js add some code. Set some defaults first, which are required if your JSON file (mine is “db.json”) is empty at first.

const low = require('lowdb');
const FileSync = require('lowdb/adapters/FileSync')
const adapter = new FileSync('db.json');
const db = low(adapter);

db.defaults({ posts: [] })
 .write()

That’s all. Now we can write into, edit and delete data within our database.

Consume MQTT sensor data and save it to the database

We go back to our Node.js application and install the MQTT client MQTT.js (Github link) to be able to consume data that is available via Mosquitto.

npm install mqtt --save

With the newly installed MQTT client we are able to receive all the messages that the MQTT broker delivers over its API on mqtt://localhost:1883. We now filter them to only process and store “correct” data sets (remember: our RTL-SDR receiver found signals from multiple IoT gagdets we are not interested in).

My setup included some buffer, temperature and date parsing, basic verifying and filtering regarding incoming messages before I stored the correct Javascript Objects into the lowdb. Continue working on your server.js:

const mqtt = require('mqtt');
const client = mqtt.connect(mqtt://localhost:1883);

fahrenheitToCelsius = (fahrenheit) => {
 var fTempVal = parseFloat(fahrenheit);
 var cTempVal = (fTempVal - 32) * (5 / 9);
 return (Math.round(cTempVal * 100) / 100);
}

client.on('message', function (topic, message) {
 // message is buffer
 var stringBuf = message && message.toString('utf-8')
 try {
   var json = JSON.parse(stringBuf);
   // console.log(json);
   if (json.model === 'inFactory sensor') {
     if (json.id === 91 || json.id === 32) {
     // catch my specific sensor model
       if (json.temperature_F && json.humidity) {
       // add data to lowdb
       const time = moment.utc(json.time).tz("Europe/Berlin");
       const formattedTime = time.format('YYYY-MM-DD HH:mm:ss');
       console.log('write post');
       db.get('posts')
       .push({ id: uuid.v1(), room: json.id, temp: fahrenheitToCelsius(json.temperature_F), humidity: json.humidity, time: formattedTime }).write()
       }
     }
   }
 } catch (e) {
   console.error(stringBuf);
   }
})

That’s it. Whenever the MQTT client receives sensor data it will store it in our database accordingly. You can check that in your “db.json” file in your project folder, which grows bigger and bigger during runtime. It won’t delete itself on backend restart!

{
 "posts": [
 {
 "id": "c107fc70-1f33-11e9-9b95-fbfea27c8046",
 "room": 32,
 "temp": 22.89,
 "humidity": 30,
 "time": "2019-01-23 18:24:34"
 },
 {
 "id": "6607f9f0-1f34-11e9-9b95-fbfea27c8046",
 "room": 32,
 "temp": 22.89,
 "humidity": 30,
 "time": "2019-01-23 18:29:11"
 },
 {
 "id": "16492190-1f35-11e9-9b95-fbfea27c8046",
 "room": 91,
 "temp": 22.72,
 "humidity": 35,
 "time": "2019-01-23 18:34:07"
 }
]
}

Provide sensor data via REST API

Now that we have clean data in our lowdb we might want to provide them via a REST API to be consumable for a frontend or multiple frontends (smartphone app, web app, …). We already deployed a local web server running on Node.js and Express.js and can very simply add an endpoint the provides the database data with the following code. Add it to your server.js!

app.get('/api', (req, res) => {
 res.send(db.get('posts'));
});

Yes, that’s it. Check if it is working on http://localhost:3000/api or with your favourite REST client (e.g. Postman).


3. Display smart home data in a Quasar, Vue.js and Chart.js powered web application

“How To: Smart Home setup to track indoor temperatures and humidity with sensors, Raspberry Pi, MQTT, Node.js, Vue.js and Chart.js”

In this part we will build a basic dashboard displaying smart home sensor data with charts. We could go with whatever implementation you would like here: from vanilla HTML/CSS/JS to every framework which is suitable to our needs – which are basically doing an API call to our backend to fetch sensor data and lift them up to work nicely with a chart visualisation.

There are also other good solutions to deal with IoT frontends like Pimatic, OpenHAB and FHEM, but let’s just build this part completely on our own. We will go with the SPA framework Vue.js with Quasar on top: It comes with UI components in the popular Material Design, axios and some other features, that help getting started with Vue.js very fast.

Install the Vue.js (Link) and Quasar CLI (Github link) first!

npm install -g vue-cli
npm install -g quasar-cli

Go to your project root and init a new sub project for your frontend code with

quasar init vue-frontend

Now start you development server on http://localhost:8080/ with

quasar dev

and you are ready to develop your frontend according to your likes. Quasar comes with a basic starter layout including a side navigation. However we don’t need to work on this heavily and focus on the the homepage, where we will build the dashboard functionality.

Go to Index.vue and update the basic dashboard layout. You can use static images like in my proposal and put them into the src/assets folder.

<template>
 <q-page padding class="docs-input row justify-center">
   <div class="row gutter-sm">
     <div class="col-6">
       <q-card inline>
         <q-card-media style="max-height: 250px">
            <img src="~assets/temperature.jpg">
             <q-card-title slot="overlay">
               Temperature
               <span slot="subtitle">hot or not</span>
             </q-card-title>
         </q-card-media>
         <q-card-main>lorem ipsum</q-card-main>
       </q-card>
 </div>
 <div class="col-6">
   <q-card inline>
     <q-card-media style="max-height: 250px">
       <img src="~assets/humidity.jpg">
       <q-card-title slot="overlay">
         Humidity
         <span slot="subtitle">some like it wet</span>
       </q-card-title>
     </q-card-media>
     <q-card-main>lorem ipsum</q-card-main>
   </q-card>
  </div>
 </div>
 </q-page>
</template>

<style>
</style>

<script>
  export default {
  name: "PageIndex"
  };
</script>

This is image title

Fetch sensor data from Node.js backend

To fetch necessary data on page load we make use of Vue.js’ created() function in the section below the template. We insert a new function this.fetchData(); there and implement this new function in the methods’ block.

methods: {
  fetchData() {
   this.loaded = false;
   this.$axios
    .get("http://localhost:3000/api")
    .then(response => {
       return response.data;
    })
    .then(response => {
      console.log(response);
      this.loaded = true;
			
    })
    .catch(e => {
      console.log(e);
      this.$q.notify({
       color: "negative",
       position: "top",
       message: "Could not fetch.",
       icon: "report_problem"
    });
  });
 }
}

We simply use the axios library (Github link) which Quasar included right from the setup, so we can use it globally without importing it explicitly. If this works properly you should be able to log the array of sensor data in your browser’s developer tools. If there is any problem, we trigger the Quasar component notification and include the error message.

Hint: If you encounter CORS problems in the communication between front- and backend (e. g. “No Access-Control-Allow-Origin header is present on the requested resource.”) edit your server.js and restart it as follows:

app.use(function (req, res, next) {
 res.header("Access-Control-Allow-Origin", "*");
 res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
 next();
});
Build a line chart with Chart.js

In this part I will focus on displaying only the temperature chart to make the tutorial more comprehensable. At first we will install the npm package vue-chartjs (Github link) which works as a practical Vue.js wrapper for Chart.js (Link).

npm install vue-chartsjs --save

Now we build a generic, reusable line chart component to use in the web application. Create a new file “LineChart.js” in the src/components folder and import the vue-chartsjs package. Furthermore, we will follow the basic tutorial for the package and specify a data collection prop and an options prop that will prettify our line chart later.

import { Line } from 'vue-chartjs';

export default {
 extends: Line,
 props: {
   datacollection: {
    type: Object,
    default: null
   },
   options: {
    type: Object,
    default: null
   }
  },
 mounted() {
   this.renderChart(this.datacollection, this.options, {responsive: true})
 }
}

Switch to the Index.vue again and include a new line chart component (html tag) in your template. We want to display it within the card component. Additionally we specify some properties: v-if=”loaded” will tell the component that it should only mount, if the according data prop is true. Also, we transfer the fetched datacollection_humidity and options_humidity as our generic datacollection and options into the line chart.

<q-card-main>
 <line-chart
   v-if="loaded"
   :datacollection="datacollection_humidity"
   :options="options_humidity"
 ></line-chart>
</q-card-main>

We also have to edit our fetchData(); function and transfer the fetched data as a processible JSON to the data collection prop.

// process the backend response and add labels and some styling for the Chart.js api  
const datacollection_humidity = {
   labels: response.map(obj => obj.time),
   datasets: [
     {
       label: "Humidity",
       backgroundColor: "#000",
       data: response.map(obj => obj.humidity)
     }
   ]
};

this.datacollection_humidity = datacollection_humidity;

// set some optional properties regarding axes and ticks
this.options_humidity = {
   scales: {
   xAxes: [
     {
       type: "time",
       distribution: "linear"
     }
   ],
   yAxes: [
     {
       scaleLabel: {
       display: true
     },
   ticks: {
     callback: function(value, index, values) {
       return value + "%";
       }
     }
    }
   ]
  }
};

Don’t forget to add the LineChart component and the necessary “loaded” data prop in your Vue.js section.

export default {
 name: "PageIndex",
 components: {
   LineChart
   },
 data() {
   return {
     loaded: false,
     };
   }
}

That’s it! On every page reload, the web application fetches available sensor data from our backend service and will display it as a line chart. You would like to add more functionality? You can find all features we built and more (filtering, reloading, deploying, saving persistently, …) in my Github repository for this project.

I hope this tutorial will surely help and you if you liked this tutorial, please consider sharing it with others. Always warm in your smart home …

Thank you for reading!

#node-js #vue-js #javascript #json #web-development

Setup your Smart Home's Temperature and Humidity Monitoring Apps
4 Likes76.35 GEEK