Do you want to measure the quality of your property’s environment? There are lots of commercial products out there that’ll do it for you, [such as Netatmo or Sen.se Peanuts] but if you’re not afraid of a little bit of DIY, then this is the guide for you. Using the power of the Internet of Things, we can easily monitor the conditions in each room of your house.
My aim is to show you that monitoring your environment doesn’t need to be expensive. In fact, the sensors can cost as little as £10-£25, depending on what you want to measure.
That said, this example is currently limited to a single node to gateway system due to ThingSpeak’s limitations, however, by removing the delay in the gateway, and changing to a different logging software (Such as MQTT), you can easily make it a multiple node to single gateway system.
What you need
The Hardware
Links to all the hardware mentioned in this post are available at the bottom of this article.
The Gateway
- ESP8266 module (this guide uses the NodeMCU v1.0, but the AdaFruit Huzzah ESP8266 Feather also works)
- NRF24L01+ radio Transceiver
That’s all. Really. The ESP8266 itself has a MCU with flash memory, and is programmable with the Arduino IDE, meaning that there is no need for a middleman Arduino between the two.
The Node(s)
- Arduino Nano Rev 3
- NRF24L01+ radio Transceiver
- DHT11*
- MH-Z19** (optional)
- RGB LED (optional)
* According to the tutorial linked below, there is an issue with the DHT22 and deep sleep, which may be present in the DHT11. An alternative is the DS18B20, but I haven’t had the chance to try this sensor out.
** If you don’t want to detect CO2, just delete the “getCO2()” method, as well as those variables marked as CO2.
The Software
This example uses ThingSpeak as a way of logging the data. ThingSpeak has a library within the Arduino IDE and is very easy to use.
Simply sign up, and create a “New Channel” with the below settings:
Now get your API key from the “API Keys” tab and insert the key into your gateway code:
ThingSpeak also has a mobile friendly web application, allowing for portable sensor tracking. There has also been a number of Mobile Apps developed for Android and iOS.
The Wiring
The Gateway
NRF24L01+ | ESP8266 Digital Pin | ESP8266 GPIO Pin |
GND | GND | GND |
VCC | 3V3 | 3V3 |
CE | D4 | 2 |
CSN | D2 | 4 |
SCK | D5 | 14 |
MOSI | D7 | 13 |
MISO | D6 | 12 |
IRQ | NC | NC |
Your module may have its pins marked as “D#”, which means you can use the Digital Pin layout, or you may have to look for the GPIO pinout for your module and, therefore, follow the GPIO table.
The Node(s)
DHT11 Wiring
DHT11 | Arduino |
VCC | 3V3 |
GND | GND |
Data | D2 |
NRF24L01+ Wiring
NRF24L01+ | Arduino |
GND | GND |
VCC | 3V3 |
CE | D8 |
CSN | D10 |
SCK | D13 |
MOSI | D11 |
MISO | D12 |
IRQ | NC |
MH-Z19
MH-Z19 | Arduino |
Vin | 5V |
GND | GND |
AOT | NC |
PWM | D5 |
Status LED
RGB LED | Arduino |
GND | GND |
Blue Pin | D3 |
Red Pin | D4 |
Green Pin | 3V3 |
The Code
The below code is a basic starting point if you were developing software for a home environment sensor system. There are a number of additions that would greatly improve the flexibility of the program, including: local web server to set up SSID/Passwords, deep sleep implementation for use with battery powered nodes, and moving to a MQTT server to allow for a smoother one to many relationship between nodes and a gateway.
To give each node and gateway it’s own unique address, I use a program to store a value in the EEPROM, which has a lifespan of around 100,000 erase/writes.
#include <EEPROM.h> int deviceID = 0, curDeviceID; void setup() { Serial.begin(9600); EEPROM.write(0, deviceID); curDeviceID = EEPROM.read(0); if (curDeviceID == deviceID) { Serial.print("Arduino ID successfully changed to: "); Serial.println(curDeviceID); } } void loop() { // put your main code here, to run repeatedly: }
Run this code on your gateway and node, assigning a unique ID to each of them.
The Gateway Software
A lot of people, myself included, got stuck in a reboot loop, citing a Watchdog timeout error, or a “WDT”, which gives an error similar to:
ets Jan 8 2013,rst cause:4, boot mode:(1,6)
As far as I could tell, it was due to the wiring of the NRF24 using GPIO 15 /D8. GPIO 15 on the NodeMCU v1
To fix this, it requires a change in the initialisation of the radio object within the arduino program.
RH_NRF24 nrf24(2, 4);
This changes the CS and CE pins for the radio to unused GPIO pins 2 and 4, allowing the NRF24 to work correctly.
If you are not planning on using the CO2 sensor, delete the highlighted lines of code.
#include <SPI.h> #include <RH_NRF24.h> #include <Wire.h> #include <EEPROM.h> #include <ESP8266WiFi.h> String apiKey = "APIKEY"; const char* ssid = "SSID"; const char* password = "PASSWORD"; WiFiClient client; int gatewayID = EEPROM.read(0); const char* server = "api.thingspeak.com"; // Singleton instance of the radio driver //RH_NRF24 nrf24; RH_NRF24 nrf24(2, 4); // use this for NodeMCU Amica/AdaFruit Huzzah ESP8266 Feather // RH_NRF24 nrf24(8, 7); // use this to be electrically compatible with Mirf // RH_NRF24 nrf24(8, 10);// For Leonardo, need explicit SS pin // RH_NRF24 nrf24(8, 7); // For RFM73 on Anarduino Mini int LED = 5; void setup() { Serial.begin(9600); Serial.print("Receiver Started, ID: "); Serial.print("Connecting to "); Serial.println(ssid); Serial.print(gatewayID); Serial.println(); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(""); Serial.println("WiFi connected"); nrf24.init(); nrf24.setChannel(3); nrf24.setRF(RH_NRF24::DataRate2Mbps, RH_NRF24::TransmitPower0dBm); pinMode(LED, OUTPUT); } void loop() { if (nrf24.available()) { // Should be a message for us now uint8_t buf[RH_NRF24_MAX_MESSAGE_LEN]; uint8_t len = sizeof(buf); if (nrf24.recv(buf, &len)) { // new message, turn on LED digitalWrite(LED, HIGH); // Send a reply uint8_t sdata[] = "Data Received."; nrf24.send(sdata, sizeof(sdata)); nrf24.waitPacketSent(); int humidity = buf[0]; int temperature = buf[1]; int CO2 = (buf[3]) * 10; int deviceID = buf[2]; Serial.println("--- Data retrieved from device ---"); if (client.connect(server, 80)) { // "184.106.153.149" or api.thingspeak.com String postStr = apiKey; postStr += "&field1="; postStr += String(temperature); postStr += "&field2="; postStr += String(humidity); postStr += "&field3="; postStr += String(gatewayID); postStr += "&field4="; postStr += String(deviceID); postStr += "&field5="; postStr += String(CO2); postStr += "\r\n\r\n"; client.print("POST /update HTTP/1.1\n"); client.print("Host: api.thingspeak.com\n"); client.print("Connection: close\n"); client.print("X-THINGSPEAKAPIKEY: " + apiKey + "\n"); client.print("Content-Type: application/x-www-form-urlencoded\n"); client.print("Content-Length: "); client.print(postStr.length()); client.print("\n\n"); client.print(postStr); Serial.println("---- Data sent to Thingspeak ----"); Serial.print("Device ID: "); Serial.print(deviceID); Serial.print(", Temperature:"); Serial.print(temperature); Serial.print(", Humidity:"); Serial.print(humidity); Serial.print(", CO2:"); Serial.println(CO2); } client.stop(); } } else { // no new message, turn off LED digitalWrite(LED, LOW); } // delay is due to ThingSpeak's limitations delay(20000); }
The Node(s) Software
If you are not planning on using the CO2 sensor, again, delete the highlighted lines of code.
// Node code #include <Arduino.h> #include <SPI.h> #include <RH_NRF24.h> #include <idDHT11.h> #include <EEPROM.h> #define pwmPin 5 // Singleton instance of the radio driver RH_NRF24 nrf24; // RH_NRF24 nrf24(2, 4); // use this for NodeMCU Amica/AdaFruit Huzzah ESP8266 Feather // RH_NRF24 nrf24(8, 7); // use this to be electrically compatible with Mirf // RH_NRF24 nrf24(8, 10);// For Leonardo, need explicit SS pin // RH_NRF24 nrf24(8, 7); // For RFM73 on Anarduino Mini int TransmitLED = 3; int ErrorLED = 4; int idDHT11pin = 2; //Digital pin for comunications int idDHT11intNumber = 0; //interrupt number (must be the one that use the previus defined pin (see table above) int deviceID = EEPROM.read(0); // CO2 measurement values int prevVal = LOW; long th, tl, h, l, ppm; //declaration void dht11_wrapper(); // must be declared before the lib initialization // Lib instantiate idDHT11 DHT11(idDHT11pin, idDHT11intNumber, dht11_wrapper); void setup() { Serial.begin(9600); pinMode(TransmitLED, OUTPUT); pinMode(ErrorLED, OUTPUT); while (!Serial) ; // wait for serial port to connect. Needed for Leonardo only if (!nrf24.init()) { Serial.println("init failed"); digitalWrite(ErrorLED, HIGH); } // Defaults after init are 2.402 GHz (channel 2), 2Mbps, 0dBm if (!nrf24.setChannel(3)) { Serial.println("setChannel failed"); digitalWrite(ErrorLED, HIGH); } if (!nrf24.setRF(RH_NRF24::DataRate2Mbps, RH_NRF24::TransmitPower0dBm)) { Serial.println("setRF failed"); digitalWrite(ErrorLED, HIGH); } pinMode(pwmPin, INPUT); Serial.println("Transmitter started"); } // This wrapper is in charge of calling // mus be defined like this for the lib work void dht11_wrapper() { DHT11.isrCallback(); } void getCO2() { long tt = millis(); int myVal = digitalRead(pwmPin); if (myVal == HIGH) { if (myVal != prevVal) { h = tt; tl = h - l; prevVal = myVal; } } else { if (myVal != prevVal) { l = tt; th = l - h; prevVal = myVal; ppm = 5000 * (th - 2) / (th + tl - 4); } } } void loop() { getCO2(); Serial.println("Sending to gateway"); uint8_t data[4]; int result = DHT11.acquireAndWait(); switch (result) { case IDDHTLIB_OK: Serial.println("DHT11 Measurement OK"); break; case IDDHTLIB_ERROR_CHECKSUM: Serial.println("Error\n\r\tChecksum error"); break; case IDDHTLIB_ERROR_ISR_TIMEOUT: Serial.println("Error\n\r\tISR time out error"); break; case IDDHTLIB_ERROR_RESPONSE_TIMEOUT: Serial.println("Error\n\r\tResponse time out error"); break; case IDDHTLIB_ERROR_DATA_TIMEOUT: Serial.println("Error\n\r\tData time out error"); break; case IDDHTLIB_ERROR_ACQUIRING: Serial.println("Error\n\r\tAcquiring"); break; case IDDHTLIB_ERROR_DELTA: Serial.println("Error\n\r\tDelta time to small"); break; case IDDHTLIB_ERROR_NOTSTARTED: Serial.println("Error\n\r\tNot started"); break; default: Serial.println("Unknown error"); break; } data[0] = DHT11.getHumidity(); data[1] = DHT11.getCelsius(); data[2] = deviceID; data[3] = ppm / 100; Serial.println("------------- Measurements -------------"); Serial.print("Humidity: "); Serial.print(data[0]); Serial.print(", Temperature: "); Serial.print(data[1]); Serial.print(", PPM: "); Serial.print((data[3]) * 100); Serial.print(", ID: "); Serial.print(data[2]); Serial.println(); digitalWrite(TransmitLED, HIGH); digitalWrite(ErrorLED, LOW); nrf24.send(data, sizeof(data)); nrf24.waitPacketSent(); // Now wait for a reply uint8_t buf[RH_NRF24_MAX_MESSAGE_LEN]; uint8_t len = sizeof(buf); if (nrf24.waitAvailableTimeout(1000)) { // Should be a reply message for us now if (nrf24.recv(buf, &len)) { digitalWrite(TransmitLED, LOW); digitalWrite(ErrorLED, LOW); Serial.print("got reply: "); Serial.println((char*)buf); } else { digitalWrite(ErrorLED, HIGH); Serial.println("recv failed"); } } else { Serial.println("No reply."); digitalWrite(ErrorLED, HIGH); } delay(2000); }
Summary
To conclude, you will be surprised how cheap and easy it is to monitor your property using existing and easily accessible hardware out there.
Another option is to use the well developed library MySensors, which the wiring for the gateway will be compatible with.
Happy Coding!
Links
https://forum.mysensors.org/topic/3285/solved-esp8266-fails-to-boot-with-nrf24l01-connected/6
https://www.mysensors.org/build/esp8266_gateway
https://github.com/esp8266/Arduino/issues/1013
https://forum.mysensors.org/topic/3496/esp8266-gateway-where-are-spi-pins-defined/3
https://learn.sparkfun.com/tutorials/esp8266-thing-hookup-guide/example-sketch-goodnight-thing-sleep-mode
Examples used
Examples of using the NRF24 as a client/server
Radiohead’s NRF24_server and NRF24_client
This guide on the MH-Z19 (it’s in Russian, but easy enough to follow)
http://www.2150692.ru/faq/87-co2-mhz19-arduino
Arduino ThingSpeak guide
http://www.arduinesp.com/thingspeak
Hardware Links
Hardware Links
AliExpress links (expect 20-40 day delivery)
NodeMCU v1 (£3.13)
https://www.aliexpress.com/item/4M-4FLASH-NodeMcu-Lua-WIFI-Networking-development-board-Based-ESP8266/32551536028.html
MH-Z19* (£17.93)
https://www.aliexpress.com/item/1PCS-module-MH-Z19-infrared-co2-sensor-for-co2-monitor-Free-shipping-new-stock-best-quality/32643995676.html
NRF24L01+ x 2 (£0.61)
https://www.aliexpress.com/item/Free-Shipping-10PCS-NRF24L01-wireless-data-transmission-module-2-4G-the-NRF24L01-upgrade-version-We-are/32469366102.html
Arduino Nano 3 Clone (£1.80)
https://www.aliexpress.com/item/Nano-CH340-ATmega328P-MicroUSB-Compatible-for-Arduino-Nano-V3-0/32740641316.html
For more advice and expert consultation as well as development of bespoke applications across mobile, web, enterprise and cloud get in touch using the form below.