Skip to content

owenhardy00/LoRa-Environmental-Sensor-Base-Station

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 

Repository files navigation

Miami University ECE387 Embedded Systems: Project 2 Report - Spring 2020

LoRa Environmental Sensor & Base Station

Created by: Brian Egolf (‘21) & Owen Hardy (‘22)

Table of Contents

Introduction
Design
Implementation
Results/Demo
Conclusion
Works Cited

Introduction

Being passionate about DIY home automation solutions, we wanted to create some sort of a room environmental sensor that could trigger different actions within a home automation sensor. A temperature sensor could signify the heating system to activate if one room becomes colder than the heating system thinks. A light sensor could signify that a light should turn on. A motion sensor could signify that a room is occupied and a light should turn on.

Although these sorts of "multi-sensor" solutions already exist, we couldn't find one that would suit our needs. We wanted a solution that was battery powered but also cost-effective. The cost-effective part ruled out many pre-existing solutions since many of those operate on a proprietary communication protocol called Z-Wave. Z-Wave is a commonly used protocol in battery-powered home automation devices since it is low power draw, however, it's expensive to prototype and license these devices. We thought that we could create a solution that worked with existing home automation systems while keeping the device low cost while also battery powered.

We were already familiar with the growing LoRa wireless communication protocol and we thought it'd be a perfect platform to design our project around. LoRa is a low power, low cost, long-range wireless communication protocol. There already exist many LoRa breakout modules from manufacturers like Sparkfun and Adafruit. We decided to pick the Adafruit Feather M0 RFM69HCW Packet Radio for our microprocessor. It has a built-in ARM Cortex M0 processor along with the LoRa radio. It also supports hardware interrupts, deep sleep modes, analog inputs, I2C, and has provisions for a LiPo battery. Everything we need in one package for $25, awesome.

Sensor Module

Now that we had our MCU picked out, we were able to pick the sensors that we would use. We didn't want to pick our sensors first because we thought that it'd be important to pick our MCU hardware first since this was the most constraining part of our design. We didn't want to settle on a temperature sensor, for example, that supported I2C but our MCU didn't. There are many more sensor options to pick from compared to MCUs. Luckily we were able to pick all of the exact hardware we wanted. We selected a BME280 Temperature & Humidity sensor which supports I2C. We selected a "dumb" analog photocell for detecting ambient lighting conditions. The third sensor we selected was a PIR sensor for motion detection. There was no particular reasoning behind picking the BME280 or photocell, they are popular, cheap, widely available, and well documented. We knew that whatever motion sensor we were going to use had to be powered on all the time to trigger an interrupt on our MCU to come out of deep sleep if motion was detected. We initially wanted to go with a microwave sensor since the sensor is much more granular compared to a PIR. This would allow us to detect a stationary human body compared to just motion with a PIR sensor. Just because there is no motion in a room doesn't mean it's not occupied. The reason we decided to go with the PIR sensor is that it has a much lower power consumption compared to a microwave sensor. We also decided to include a pushbutton which will bring the MCU out of deep sleep and force publish new sensor data. The button could also be used elsewhere in the home automation system to maybe manually trigger a function in the room such as toggling a light. We knew it would also be important to publish the battery capacity of the device so that the home automation service could monitor this and send the user a notification if a charge was required.

LoRa to WiFi Base Station

The second component to our design is a LoRa base station which will be located at some nearby location. The purpose of this device is to communicate any data on the LoRa network to the home automation system. This would be accomplished through a pre-established communication system within the home automation network. Owen already had a home automation system set up called OpenHAB. It runs on a Raspberry Pi on his local Internet network. OpenHAB supports a lightweight communication protocol called MQTT. An instance of a Mosquitto MQTT broker runs on the same Raspberry Pi. OpenHAB interfaces with the MQTT broker and monitors so-called inbound channels for messages which can contain different sensor data and information. OpenHAB can respond to the information within the messages to execute different functions on other components of the home automation. This is also controlled through MQTT by publishing messages on different outbound channels. Each device on the home automation network has its own unique channel that it listens and responds to. An example flowchart of how a lamp would be turned on is below.

Interfacing the LoRa network with MQTT would require passing LoRa data packets. We already had a LoRa radio, and we decided to use an ESP8266 NodeMCU for our WiFi interface. The reasons we went with this option is they're cheap, widely available, Arduino-compatible, and we have experience using them. The LoRa radio would simply listen for new data to be pushed on to the network and the NodeMCU would wait for incoming LoRa data packets and push them out on MQTT over WiFi.


Design

Sensor Module

Connecting all of our sensors together was relatively straight forward. Our BME280 temperature and humidity sensor communications via I2C. Our PIR sensor simply had a digital output that was active HIGH. Our pushbutton was wired to a digital input that was connected to a pullup resistor on the ARM MCU through software. Our photocell was connected to an analog input and a 10k voltage divider. Lastly, our battery monitor was connected to the Feather's "BATT" pin which exposed the raw battery voltage. This was connected to an analog input via a dual 100k voltage divider as detailed on the Adafruit hookup guide for the Feather. A voltage divider is required here because the LiPo's battery voltage can reach a maximum of 4.2V which is too high for the Feather which can only handle a maximum of 3.3V. The voltage divider will half the apparent voltage of the battery so the highest analog voltage the Feather will see is 2.2V.

As for the software, we wanted the device to operate in a very specific way to conserve as much power as possible. We wanted the MCU to be in a deep sleep mode most of the time. The MCU would come out of sleep only under the following conditions. (1) The pushbutton has been pressed. (2) Motion has been detected for the first time in over 10 minutes. (3) 10 minutes have passed without a wake cycle from either the PIR sensor or pushbutton. We mustn't come out of sleep every time the PIR sensor outputs HIGH because then the MCU may be on for minutes at a time when there are many guests in the room or when someone is up and moving around. We assume that the room is unoccupied if there has been no motion for 10 minutes. When the device does come out of sleep, we turn on our sensors and LoRa radio, take data readings, transmit them over LoRa, and then return to deep sleep.

LoRa to WiFi Base Station

Our LoRa to WiFi base station was the most simple part of our solution. It simply had to relay data from the LoRa radio to the NodeMCU so that it can be transmitted to the proper MQTT channel. This will be communicated across a Serial line for simplicity.









Implementation

Sensor Integration

The first part of our software consisted of integrating all of our sensors. We specifically started with the BME280 temperature and humidity sensor since our instructor wanted us to create this code from scratch without using a library. We started by jumping right into the information in the datasheet. You should have been able to read from the "id" register to see if you had the chip connected properly. We initially couldn't get a reading from this and thought that we weren't able to read and write on our I2C bus correctly. Eventually, we downloaded the Adafruit BME280 just to test the part and sure enough, we had no luck with that library. We figured the part was DoA and ordered a replacement. Unfortunately, this happened right at the start of the COVID-19 pandemic so we had to wait a while on shipping. We ordered extra this time just in case. We were able to read back the correct 0x60 chip ID with our new part. Next, we moved on to setting the configuration registers. These set things like oversampling correction, filters, and standby times. This was relatively easy, all we had to do was implement the instructions from the datasheet into Arduino code which involved reading, writing, and shifting various bits and registers. The way that we tested we set our configuration parameters correctly was by using a part of a pre-existing library for the device to read the temperature. We knew that if we got a temperature reading back we had successfully implemented configuring the device's parameters. Once we got that working, we moved on the creating the code for reading the temperature and humidity. This involved reading from various registers and shifting the bits. Again, this was detailed in the datasheet and was identical for both the temperature and humidity except for the register addresses. This part of the software was by far the most challenging part of the project which is why we wanted to tackle it first that way we could come up with a backup plan if we had unresolvable issues.

The software for the remaining sensors we had to integrate was quite simple. All of our sensor readings were analog/digital readings implemented with a simple analogRead or digitalRead. For debugging purposes, we printed these values out to the Serial Monitor. Once we could see all of our sensor data printing to the Serial Monitor, we decided it would be best to compile all of the data into a data packet that is fixed in length and has data points in the same recurring positions. This would make it easy to parse and segregate the data before we published each value to its own MQTT topic. We define a unique ID to each LoRa client on the network so that we can identify and publish to unique MQTT topics. We constrained all of our data values to a fixed length of 1, 2, or 4 characters depending on the data point so their position indices are fixed in space. Below is how the data packet is structured.

Data packet structure: "xx,xxxx,xxxx,xx,x,x,xx"

Index position:
id       = [0,1]
temp     = [3,4,5,6]
humidity = [8,9,10,11]
lux      = [13,14]
motion   = [16]
button   = [18]
battery  = [20,21]

LoRa Communication

We wanted to get our LoRa network working independently before sending any sensor data to make it as easy as possible to debug. We first used the example sketches provided in the Adafruit Radiohead library. One LoRa radio was set up as the base station and the other was set up as a client. We simply sent "Hello" as our data packet every 2 seconds. We had our client connected to power sending this data packet and blinking its onboard LED upon packet transmission so we could have a visual indicator. Our base station was connected to our Arduino Serial Monitor so we could see the incoming data transmissions. As expected, we received a "Hello" every 2 seconds.

Once we had our LoRa radios communicating properly, we started transmitting our constructed data packet which worked as expected. This signified the near completion of the sensor module. The only thing left to do is implement the deep sleep functionality which will be done after the entire system is working as a whole.

LoRa to WiFi Translation

Now that we had our base station LoRa receiving the sensor module's data, we needed to get that information sent to our NodeMCU so that we can publish it over MQTT. When our LoRa module receives a data packet, we also receive a value called RSSI which represents our signal quality. It's reported as a negative value and the closer it is to 0, the stronger our signal. Adafruit says you can expect to receive a signal between -15 (highest) and -80 (lowest). Before we added any sort of antenna, we had a signal quality of -96 when the two radios were only a few feet from each other. We added a 3" quarter-whip antenna to both radios as suggested by Adafruit. This increased our signal strength dramatically The modules could be separated the distance of the home with an acceptable signal quality of -50. We thought this might be an important metric to publish to the home automation server so we added an additional 3 characters at the end of our constructed data packet to fit it in. Our new data packet structure is as follows.

Data packet structure: "xx,xxxx,xxxx,xx,x,x,xx,xxx"

Index position:
id       = [0,1]
temp     = [3,4,5,6]
humidity = [8,9,10,11]
lux      = [13,14]
motion   = [16]
button   = [18]
battery  = [20,21]
rssi     = [22,23,24]

This 24-bit long packet is sent over Serial from the LoRa MCU to the NodeMCU. Once the data packet has reached the NodeMCU, we can decompose our data packet into 8 data points. We need to do this because we need to publish each value on its own separate MQTT topic so that we have separate metrics for our home automation server to react to. Here is an example of how an incoming data packet would be decomposed and published to MQTT by the NodeMCU.

Data packet: "02,70.1,43,07,1,0,99,-32"
Publish on topic: "/home/LoRa/node02/temperature" "70.1"
Publish on topic: "/home/LoRa/node02/humidity" "43"
Publish on topic: "/home/LoRa/node02/lux" "07"
Publish on topic: "/home/LoRa/node02/motion" "1"
Publish on topic: "/home/LoRa/node02/button" "0"
Publish on topic: "/home/LoRa/node02/battery" "99"
Publish on topic: "/home/LoRa/node02/rssi" "-32"

Once the sensor data has been published to MQTT, the home automation server can react to these updates and trigger various automations depending upon how it's configured to do so. The beauty is that OpenHAB can pool information from hundreds of different devices and services together into a cohesive system. Take the following scenario as an example. We have a sensor module located in the kitchen of a 2-story home. The sensor module awakes from sleep due to new motion. OpenHAB recognizes this new update to the MQTT topic. It also sees that the newest light level was 00 which is absolute darkness. OpenHAB recognizes that the time is 2 AM. This must mean that someone has come into the kitchen during the night. At this point, OpenHAB could trigger the kitchen lights to come on for better visibility. OpenHAB can also track data over time. For example, we see that the humidity inside a room is above 80 for an extended period of time. We might want to alert the homeowner or activate a dehumidifier. The user can configure OpenHAB to respond with whatever action they desire.

At this point, we can see our sensor readings on the OpenHAB interface.

Deep Sleep

We wanted to implement the sleep functionality of the sensor module last because we wanted to have all of the other parts working correctly. After lots of research and testing, we determined that we would be unable to implement the deep sleep functionality. We needed to be able to wake the MCU with two external interrupts. One is the pushbutton and the other is the PIR sensor. The pushbutton will always be connected as an interrupt, however, we needed to be able to attach and detach the PIR interrupt. This is because we want to ignore the state of this sensor if we already detected motion in the past 10 minutes. It was our intention to set a flag for this in software and detach the interrupt until the next wake cycle from either 10 minutes passing or a press of the pushbutton. Although it is possible to attach and detach interrupt pins on the M0, it is not possible to do so while also having these interrupts wake the device from deep sleep. For this reason, we were unable to implement the deep sleep functionality as originally planned. We will talk about alternative options for if we ever decide to revisit this project.


Results/Demo

Demo video


Conclusion

Overall I would say the product is almost a complete success. The device turned out compact, requiring minimal hardware and could be turned in to a singular PCB so that the device could be miniaturized into some sort of plastic case/enclosure. The device keeps track of all of the metrics we wanted and does so at a minimal cost. The cost to build a sensor node from scratch would be about $30 with $23 of that being the Adafruit Feather MCU. You could theoretically reduce this cost further if you developed your own PCB with the ARM processor and LoRa module on it. One note to mention if you were going to recreate this project for yourself. For the base station, we opted to use a Feather and ESP, however, it would be more cost-effective and simpler to use just a bare LoRa radio module without an MCU like how the Feather features. The reason we did this was that we were restricted to ordering parts from suppliers due to COVID-19.

Challenges/issues

The one unresolved issue that we did encounter was that of the deep sleep mode. This could be resolved if we ever decided to revisit the project by incorporating an ATtiny chip to control the sleep mode. Although this may slightly increase power draw, the device could still last many weeks on a singular charge. The reason we did not implement this solution was due to time restrictions as well as limited access to electronics parts due to COVID-19.


Works Cited

Adafruit Feather M0 RFM69HCW Hookup Guide
Bosch BME280 Datasheet (pages 23-31)
Adafruit Radiohead LoRa Arduino Library
Arduino MQTT Library
Arduino ESP8266 Core Library
Visual aids and diagrams made using Creately

Releases

No releases published

Packages

No packages published

Languages