Introduction: LoRa IOT Home Environment Monitoring System

The LoRa IOT Home Environmental Monitoring System consists of an Arduino Mega based IOT-to-Internet gateway and Arduino Feather based remote stations with environmental sensors. The remote stations communicate wirelessly with the gateway using LoRa radios.

The system enables a homeowner to monitor the home environment via an internet accessible dashboard, receive periodic SMS environmental notifications, receive realtime SMS alerts when monitored environmental parameters exceed preset thresholds, and log environmental data to the cloud.

As a homeowner, other than fire, the greatest fear is water, whether it's a sudden and catastrophic pipe or fixture failure, or a slow leak that goes undetected over time. If you are away from home when a sudden and catastrophic event occurs there may not be much that can be done, however, returning home sooner rather later or calling a neighbor due to humidity readings suddenly spiking could mitigate the damage. A slow leak can be more insidious, potentially resulting in structural damage and black mold. The LoRa IOT Home Environment Monitoring System monitors household humidity levels over time and provides insight to how normal humidity levels fluctuate throughout the year with changes in outdoor humidity, and daily with normal household activities such as clothes drying, showers & etc. Armed with this knowledge, high humidity thresholds can be intelligently set for the various remote stations enabling the LoRa IOT Home Environment Monitoring System to send an alert when abnormal humidity readings are detected. SMS alerts and hourly updates appear on a smart phone Lock Screen so are immediately available without requiring an application to be launched or browsing to a dashboard.

The LoRa IOT Home Environment Monitoring System is built using readily available hardware modules and a few individual components such as switches and connectors. The parts for the system can be obtained from Adafruit, Digikey and Sparkfun; in many cases, Adafruit and Sparkfun parts are also available from Digikey. Competent soldering skills are needed to assemble the hardware, in particular to solder SMT uFL connectors to the LoRa radio breakout and Feather boards. The Arduino code is written in a procedural style and liberally commented to allow easy extension of functionality.

The objectives for this project included the following:

  • Create a home IOT gateway Minimum Viable Product (MVP)
  • Create a User Interface (UI) framework for Arduino projects
  • Assess suitability of LoRa technology for the home environment

The MVP establishes the minimum hardware configuration and minimum set of software features needed to produce a really useful IOT based home monitoring system.

The intent of the UI framework objective is to make all user based configuration parameters accessible without requiring code changes and recompilation. User configuration changes are made via a simple UI consisting of four push button switches and a 2 x 16 LCD. The LCD is also used to display environmental variables locally, six display modes are currently available. Third party service authentication information (Temboo and Google Drive) is stored in a simple text file on an SD memory card. The current versions of remote station and gateway software mostly achieve the goal of user configuration without the need for code compilation, exceptions are noted.

LoRa is a long range, low power wireless technology developed by Semtech for the Internet of Things. Semtech builds LoRa technology into its chipsets, and these chipsets are available on the breakout and Feather boards from Adafruit used in the LoRa IOT Home Environment Monitoring System. LoRa enables the realization of simple, reliable, and energy efficient wireless telemetry links in the home between remote stations and a hub/gateway. Why not use WiFi? WiFi is overkill for environmental monitoring around the home in terms of data rates, it's a power hog, and, in my experience, WiFi links based on simplified clients are not always reliable. LoRa operates at low data rates in the 915-MHz ISM band. The lower data rates combined with the propagation characteristics at 915-MHz (versus 2.4- and 5-GHz for WiFi) enable very reliable radio links to be established around the largest home environments.

The LoRa IOT Home Environment Monitoring System consists of the following subsystems:

  1. LoRa IOT Gateway - wireless sensor hub and gateway to the Internet
  2. Indoor Station - wireless temperature and humidity sensor
  3. Outdoor Station - wireless temperature, humidity, and pressure sensor

The LoRa IOT Gateway receives periodic readings from the remote stations and implements the following functions:

  • Local time keeping synchronized with NTP
  • UI Functions and local display via 2 x 16 LCD
  • Alerts for over / under threshold readings via SMS
  • Hourly temperature and humidity updates via SMS
  • Real-time dashboard (ten minute updates) via Adafruit IO
  • Long term cloud data storage on Google Drive in Google Sheets

Remote stations sleep most of the time, waking up periodically to take environmental readings and send them to the gateway.

Communications in the LoRa IOT Home Environment Monitoring System is currently one-way from the remote station to the gateway; that is, the gateway is always receiving and does not transmit, and the remote stations periodically transmit but receivers are never enabled. As the LoRa wireless links have proven to be very reliable in a home environment, the complications that come with implementing a two-way protocol in the system did not seem warranted. Remote stations send updates asynchronously at intervals of approximately ten minutes. The transmissions are very short, so the probability of transmissions from two stations colliding is very low. An SMS alert is sent if the gateway does not receive transmissions from a given remote within a configurable number of 10 minute intervals. Battery levels are monitored for battery powered stations (currently only the Outdoor Station), and an alert sent for a low battery level. A Receive Signal Strength Indicator (RSSI) for each remote is accessible via the LCD functions to check wireless link quality.

Thank you for visiting this Instructable, and please see the following steps for further information.

  1. LoRa IOT Gateway Hardware
  2. Indoor Station Hardware
  3. Outdoor Station Hardware
  4. LoRa IOT Gateway User Interface
  5. SMS Notifications and Alerts
  6. Environmental Data Cloud Storage
  7. Internet Accessible Dashboard
  8. LoRa IOT Gateway Software
  9. Indoor Station Software
  10. Outdoor Station Software
  11. IOT Gateway SD Memory Card Authentication File

Step 1: LoRa IOT Gateway Hardware

Parts List:

1 x Arduino Mega 2560 R3, Adafruit PID 191

1 x Ethernet Shield for Arduino - W5500 Chipset, Adafruit PID 2971

1 x I2C / SPI Character LCD Backpack, Adafruit PID 292

1 x Perma-Proto Half-sized Breadboard PCB, Adafruit PID 1609

1 x RFM95W LoRa Transceiver Breakout 868 or 915 MHz, Adafruit PID 3072

1 x uFL SMT Antenna Connector, Adafruit PID 1661

1 x SMA to uFL/u.FL/IPX/IPEX RF Adapter Cable, Adafruit PID 851

1 x Antenna 916 MHz 1/2 Wave SMA, Digikey PN ANT-916-CW-HWR-SMA-ND

1 X LCD 2X16 Character Blue, Digikey PN NHD-0216K1Z-NSB-FBW-L-ND

4 x Switch Push SPDT 0.4A 20V, Digikey PN CKN1633-ND

4 x CAP Pushbutton Round Black, Digikey PN CKN1210-ND

2 x Grove - Universal 4 Pin 20cm Cable, Seeed Studios, PN ACC11317O

1 x Wall Adapter Power Supply - 9VDC 650mA, Sparkfun PN TOL-00298

1 x MicroSD Card with Adapter - 8GB, Sparkfun COM-11609

1 x Enclosure, QRP Labs

1 x Custom Front Panel, Front Panel Express

1 x Custom Rear Panel, Front Panel Express

1 x Ethernet cable

Misc male and female pin headers, wire, spacers; and 2-56 nuts, bolts and washers.

.............................................................................................................................

Ethernet Shield

The Ethernet Shield is plugged into the Arduino Mega.

.............................................................................................................................

LCD

The Adafruit I2C / SPI Character LCD Backpack is used to connect the LCD to the Arduino Mega using I2C.

Refer to the Adafruit website for detailed information on the I2C / SPI Character LCD Backpack.

A set of female pin headers is soldered on the LCD, and a corresponding set of male pin headers is soldered on the LCD backpack to allow the LCD Backpack to be plugged in and out of the LCD. The Ethernet Shield has a Grove connector that brings out +5V, GND, SDA and SCL. One of the Grove universal cables is plugged into the I2C connector on the Ethernet Shield, the connector on the other end of cable was cut off and wires connected to the LCD Backpack via the terminal blocks supplied with the LCD Backpack as follows.

Arduino <--> LCD Backpack

GND (black wire) <--> GND

+5V (red wire) <--> +5V

SDA (white wire) <--> DAT

SCL (yellow wire) <--> CLK

.............................................................................................................................

RFM95W LoRa Transceiver

Refer to the Adafruit website for detailed information on the RFM95W LoRa Transceiver, including details on soldering the uFL SMT antenna connector. The uFL connector on the RF Adapter Cable is plugged into the uFl connector soldered on to the RFM95W. The SMA connector of the RF Adapter Cable is a panel mount type and installed on the rear panel of the enclosure. The Antenna is attached to the SMA connector on the rear panel.

Communication between the RFM95W and Arduino is via SPI. The RFM95W breakout board is mounted on a half-sized Adafruit Perma-Proto prototyping board. Female pin headers are soldered to the prototyping board to accept the male pin headers on the RFM95W breakout board.

The following connections are made between the RFM95W breakout board and the Arduino by soldering one end of a wire to the prototyping board and a male header to the other end of a wire for plugging into the respective pin on Arduino stacking headers.

Arduino <--> RFM95W

GND <--> GND

+5V <--> VIN

Digital Pin 2 (interrupt 0) <--> G0 (RFMW95 Interrupt)

Digital Pin 52 (SPI SCK) <--> SCK

Digital Pin 50 (SPI MISO) <--> MISO

Digital Pin 51 (SPI MOSI) <--> MOSI

Digital Pin 8 <--> CS (Chip Select)

Digital Pin 9 <--> RST (Reset)

.............................................................................................................................

Push Button Switches

The LoRa IOT Gateway has four push button switches as follows:

FUNC - Enter and exit the gateway function mode

ENTER - Accept selected parameter value

UP - Increment parameter value

DOWN - Decrement parameter value

The push button switches used have a good tactile feel, and can be pressed with having to hold down the enclosure on most surfaces. The user interface experience will be significantly degraded if cheap, hard to press push button switches are substituted.

The push button switches have common (c), normally open (no), and normally closed (nc) terminals. The common terminals of all four switches are connected to Arduino ground. The second Grove universal cable is used to make the ground connection to the Arduino at the UART grove connector on the Ethernet Shield, the connector on the other end of cable was cut off - only the ground connection is used, so the remaining wires can be cutoff at the connector plugged into the Ehternet Shield as shown in the photo. The ground connection is daisy-chained between the switch common terminals. The normally closed switch terminals are left unconnected. A wire is soldered to each of the normally open switch terminals, and a male header soldered to the other end for plugging into the respective pin on Arduino stacking headers as detailed below.

Arduino <--> Switch

Digital Pin 19 <--> FUNC

Digital Pin 18 <--> ENTER

Digital Pin 17 <--> DOWN

Digital Pin 16 <--> UP

Program the Arduino Mega with the LoRa IOT Gateway software. Refer to step: LoRa IOT Gateway User Interface for information on operation of the gateway user interface. That's it for the LoRa IOT Gateway hardware.

Step 2: Indoor Station Hardware

Parts List for one Indoor Station:

1 x Feather 32u4 RFM95 LoRa Radio - 868- or 915-MHz, Adafruit PID 3078

1 x Female Stacking Headers - 12-pin and 16-pin, Adafruit PID 2830

1 x FeatherWing prototyping board, Adafruit PID 2884

1 x SHT31-D temperature & humidity sensor breakout, Adafruit PID 2857

1 x Four pin female pin header

1 x USB cable A / MicroB, 3 ft, Adafruit PID 592

1 x 5V 1A USB port power supply, Adafruit PID 501

3 inches 22 AWG enamel wire

Misc hookup wire

..............................................................................................................................

Refer to the Adafruit website for detailed information on the following steps:

  1. Soldering the stacking headers and wire antenna to the Feather 32u4 RFM95 LoRa Radio.
  2. Soldering the included male pin headers to the SHT31-D.
  3. Soldering the included male pin headers to the FeatherWing prototyping board.

Solder the four pin female header to the FeatherWing prototyping board according to the picture above so the SHT31-D VIN and GND pins are connected to the 3.3V and GND rails, respectively, on the FeatherWing prototyping board.

Use the hookup wire to make the following connections on the underside of the prototyping board:

FeatherWing <--> SHT31-D

SDA <--> SDA
SCL <--> SCL

The SDA and SCL pins are designated on the prototyping board and are in the top right corner of the prototyping board in the picture above.

Plug the prototyping board into the stacking headers on the Feather board.

Program the Feather boards with Indoor Station software. When deployed and powered via the USB cable connected to USB power supply, the Indoor Stations should start sending reports to the LoRa IOT Gateway every ten minutes.

That's it for the Indoor Stations.

Step 3: Outdoor Station Hardware

Parts List:

1 x Feather 32u4 RFM95 LoRa Radio, Adafruit PID 3078

1 x FeatherWing Doubler prototyping board, Adafruit PID 2890

1 x uFL SMT Antenna Connector, Adafruit PID 1661

1 x SMA to uFL/u.FL/IPX/IPEX RF Adapter Cable, Adafruit PID 851

1 x Antenna 916 MHz 1/2 Wave SMA, Digikey PN ANT-916-CW-HWR-SMA-ND

1 x Perma-Proto Half-sized Breadboard PCB, Adafruit PID 1609

1 x Small plastic project enclosure, weatherproof, Adafruit PID 903

1 x Polymer Lithium Ion Battery, 2000mAh, Sparkfun SKU PRT-08483

1 x Sunny Buddy - MPPT solar charger, Sparkfun SKU PRT-12885

1 x JST 2-Pin Cable, Adafruit PID 261B

1 x Solar Panel, 2W, Sparkfun SKU PRT-13781

1 x DS18B20 Temperature Sensor, waterproof, Sparkfun SKU SEN-11050

1 x BME280 Atmospheric sensor breakout, Sparkfun SKU SEN-13676

2 x Screw terminals, 3.5mm pitch (2-pin), Sparkfun SKU PRT-08084

1 x Davis Instruments Solar Radiation Shield 7714, Amazon

1 x Lexan sheet, HomeDepot

Misc male and female pin headers, wire, spacers; 2-56 nuts, bolts and washers; and silicon sealant.

..............................................................................................................................

Overview

The Polymer Lithium Ion Battery, Sunny Buddy - MPPT solar charger, FeatherWing Doubler prototyping board, and Feather 32u4 RFM95 LoRa Radio board are mounted in the small plastic project enclosure.

The Perma-Proto Half-sized Breadboard PCB and BME280 Atmospheric sensor breakout board are mounted in the Davis Instruments Solar Radiation Shield.

The Solar Panel is mounted on top of the bracket that comes with the Davis Instruments Solar Radiation Shield. Refer to the photos above.

Various combinations of temperature/humidity sensors and pressure sensors were tried in the outdoor station before settling on the BME280 which is an integrated temperature, humidity and pressure sensor. The readings from the BME280 are always consistent with local weather reports, and operation has been 100% reliable over a period of several months, including a New Hampshire winter.

.............................................................................................................................

Enclosure Preparation & Solar Power Supply

  1. Cut a piece of Lexan to fit the inside contours of the small plastic project enclosure.
  2. Drill holes in the Lexan to match the two mounting points in the bottom of the enclosure and accommodate the mounting screws included with the enclosure.
  3. Place the Lexan cutout in the enclosure and determine the mounting location for the SMA connector on the SMA to uFL/u.FL/IPX/IPEX RF Adapter Cable. Looking at the photo above, the SMA connector and cable assembly must be above and clear of the Lexan cutout.
  4. Drill three holes in the bottom side of the enclosure to accommodate
    • The cable from the Solar Panel, note the barrel connector should be cut off the cable.
    • The four conductor cable to the BME280 in the solar radiation shield.
    • The cable on the DS18B20 Temperature Sensor.
  5. Drill holes in the Lexan to accommodate bolts to mount the Sunny Buddy.
  6. The mounting holes in one end of the FeatherWing doubler prototyping board match up with the holes in one end of the Sunny Buddy as shown in the photo above.
  7. Referring to Sparkfun Hookup Guide for the Sunny Buddy, solder the two screw terminals to the locations on the Sunny Buddy for "Solar In" and "Load."
  8. Attach the Sunny Buddy to the Lexan cutout using blots and spacers. Use long enough bolts on one end to provide two mounting points for the prototyping board, accounting for spacers between the Sunny Buddy and the prototyping board.
  9. Mount the SMA connector and cable assembly.
  10. Place the Polymer Lithium Ion Battery in the base of the enclosure.
  11. Route the antenna cable assembly between the Lexan cutout and the bottom side of the Sunny Buddy.
  12. Mount the Lexan cutout with the Sunny Buddy attached to the enclosure using the screws supplied with the enclosure. At this point the battery is held in place between the Lexan cutout and the base of the enclosure, and the antenna cable is routed from the panel mounted SMA connector to the bottom side of the enclosure.
  13. Plug the battery JST connector into the "BATT" connector on the Sunny Buddy.
  14. Strip and tin the two wires at the end of the solar panel cable, and route the cable though the hole in the bottom side of the enclosure. Connect the solar panel red and black wires to "Solar In" terminal block on the Sunny Buddy, "+" and "-" respectively.
  15. Connect the tinned wire ends of the JST 2-pin cable to the "Load" terminal block on the Sunny Buddy, red wire to "+" and black wire to "-".

At this stage the enclosure and solar power system are ready for the Feather 32u4 RFM95 LoRa Radio.

.............................................................................................................................

Prototyping Board & Feather LoRa Radio

  1. Refer to the Adafruit website for detailed information on soldering the soldering the uFL SMT antenna connector to the Feather 32u4 RFM95 LoRa Radio.
  2. Solder the female headers that comes with the FeatherWing Doubler prototyping board to one half of the prototyping board for the Feather board to plug in as shown in the photo above. The other half of the prototyping board is not used.
  3. Solder two four-way right-angle male pin headers to the prototyping board as shown in the picture above.
  4. Install wire jumpers on the prototyping board to make connections from the Feather board 3V, GND, SDA and SCL pins to one of the four-way male pin headers. This is the BME280 pin header.
  5. Pass the four conductor through the hole in the bottom side of the enclosure and solder a four-way female header. This plugs into the BME280 pin header.
  6. Solder a four-way female header to the other end of the cable and make a note of the pins connections - 3V, GND, SDA and SCL.
  7. Install wire jumpers on the prototyping board to make connections from the Feather board 3V, GND and Digital Pin 5 to the other four-way male pin header. This is the DS18B20 pin header.
  8. Pass the cable connected to the DS18B20 through the hole in the bottom side of the enclosure and solder a four-way female pin header to match the male pin header installed in step 7 as follows: red wire to 3V, black wire to GND, and white wire to Digital Pin 5. Plug the female pin header into the DS18B20 male pin header on the prototyping board.
  9. Carefully plug the uFL connector on the antenna cable into the uFL connector soldered to the Feather board in step 1.
  10. Attach one end of the prototyping board to the two blots extending from one end the Sunny Buddy using spacers to make sure the prototyping board is clear of components on the Sunny Buddy.
  11. Program the Feather board with Outdoor Station software.
  12. Plug the Feather board into the female headers soldered to the prototyping board in step 2.
  13. At this stage, do not connect the JST connector from the cable connected to the Sunny Buddy "Load" terminal block to the battery connector on the Feather board.
  14. Apply silicon sealant around the three cable entry holes in the bottom side of the enclosure.

.............................................................................................................................

Solar Radiation Shield

  1. Follow the instructions that come with the Davis Instruments Solar Radiation Shield 7714 for assembly and mounting options. Install the solar radiation shield up to the stage shown in the lower middle photograph above.
  2. Reference Adafruit assembly instructions for soldering male pin headers to the BME280 breakout board.
  3. Using the photo above as a guide, solder a set of four-way right-angle male pin headers to the Adafruit Perma-Proto Half-sized Breadboard PCB.
  4. Using the photo above as a guide, solder the BME280 breakout board to the prototyping board.
  5. Using wire jumpers, make connections between the BME280 breakout board VIN, GND, SCK and SDI pins to the four-way right-angled male pin header installed in step 3. These pins connections must match connections on the BME280 pin header on the FeatherWing Doubler board as follows: 3V = VIN, GND = GND, SDA = SDI and SCL = SCK.
  6. It just so happens that the mounting holes on the Adafruit Perma-Proto Half-sized Breadboard PCB match exactly the sensor mounting holes on the solar radiation shield. Mount the prototyping board to the solar radiation shield using the screw hardware supplied.
  7. Plug the four-conductor cable for the BME280 from the Feather prototyping board into the male pin headers on the prototyping board in the solar radiation shield.
  8. There is a row of unused female pin headers shown in the photo above. These were used during early experiments to select sensors for the outdoor station, and are not required for normal operation.
  9. Complete assembly of the solar radiation shield according to the instructions referenced in step 1 above.
  10. Using the photo above as a guide, cut a piece of Lexan to mount the solar panel. The Lexan should be cut to the width of the solar panel, but longer than the length of the solar panel to allow bolting of the assembly to the top of the radiation shield. The length of the assembly results in a slight tilting up of the solar panel when mounted as shown above, which is beneficial in terms of sun angle.
  11. Drill holes for mounting the solar panel to the Lexan, and holes for bolting the assembly to the solar radiation shield. Mount the solar panel to the Lexan using the nuts supplied with the solar panel. Bolt the Lexan with the solar panel attached to the solar radiation shield mounting bracket per the above photo.

At this stage the JST connector from the cable connected to the Sunny Buddy "Load" terminal block can be plugged in to the battery connector on the Feather board. The Outdoor Station should start sending reports to the LoRa IOT Gateway every ten minutes.

The Outdoor Station pictured above has been in uninterrupted operation since October 2016, surviving the New Hampshire winter.

Step 4: LoRa IOT Gateway User Interface

The LoRa IOT Gateway LCD User Interface (UI) consists of an LCD Main Menu that cycles through 12 menu items listed below. The gateway software is structured to make it straightforward to add or remove menu items.

Navigation through the LCD Main Menu items using the IOT Gateway FUNC, ENTER, UP and DOWN push button switches is detailed in the PDF documents attached below.

1. Set LCD Back Light Off

Used to turn off the LCD back light, effectively turning off the LCD display. Once the LCD back light is turned off, pressing FUNC immediately turns it back on.

2. Set Display Mode

Set the Display Mode for station data display on Line 2 of the LCD. Three display modes are currently supported, and each mode can be set for temperature or humidity display, making a total of six display mode options.

Last Station Mode: Displays either temperature or humidity of the last station report received. Updates are displayed immediately when a station report is received.

Fixed Station Mode: Displays either temperature or humidity for a designated station. Useful for close monitoring of a given station. Display updates once per minute.

Cycle Station Mode: Cycles through either temperature or humidity for all active stations, one minute per station.

Stored in EEPROM for persistence.

The IOT Gateway software is structured to allow straightforward addition or removal of Display Modes. For example, the next software release will include a Display Mode that displays both temperature and humidity.

3. Display RSSI

Display station Received Signal Strength Indicator (RSSI) in dBm as reported by LoRa Radio in each station. Cycle through stations by pressing UP and DOWN buttons.

4. Display Battery

Display battery voltage to Feather 32u4 RFM95 LoRa Radio boards in each station. Cycle through stations by pressing UP and DOWN buttons.

5. Set Alerts

Enable and disable alert reporting for selected station. In current IOT Gateway software, alerts are supported for High Humidity, Battery Level, and Station Lost Contact.

Stored in EEPROM for persistence.

6. Set Humidity Threshold

Set humidity threshold on a per station basis to trigger an alert.

Stored in EEPROM for persistence.

7. Set Number of Stations

Set number of active stations in the system. The default setting is one active station, and the maximum number of stations is currently ten. The number of active stations must be set before assoiciated station names are accessible for editing in the Set Station Name Sub Menu.

Stored in EEPROM for persistence.

8. Set Station Names

Allows editing of active station names. Station names have a maximum of nine characters.

Stored in EEPROM for persistence.

9. Set Day Light Savings

Turn Day Light Savings time ON and OFF.

Stored in EEPROM for persistence.

10. Set Time Zone

Set local Time Zone. Twenty-four Time Zones supported, identified by major city and offset from UTC.

Stored in EEPROM for persistence.

11. Set Pressure Calibration Offset

Calibrate pressure sensors to account for altitude of the station location. Adjust calibration factor so local station pressure matches pressure reported by NOAA, Weather.com, or other weather authority for station city or zip code.

Stored in EEPROM for persistence.

12. Set Temperature Units

Select either Fahrenheit or Centigrade units for all temperature related functions.

Stored in EEPROM for persistence.

Step 5: SMS Notifications and Alerts

The photo above shows the SMS (text) Notification and Alerts.

A Notification is an hourly update sent via SMS. The LoRa IOT Gateway sends a notification hourly at 30 minutes past the hour. Given the limitation of 160 characters in an SMS message, temperature and humidity for up to four stations can typically be included in the notification. The first two stations will be displayed on the smartphone Lock Screen as shown in the left hand photo above, and the remaining two stations can be viewed by going to the Messages app as shown in the middle photo. The content of the notification message is defined in the sendStatus() function in the Temboo Routines section of the LoRa IOT Gateway software.

An Alert is sent if a parameter exceeds a preset threshold as shown in the right hand photo above. Alerts are currently supported for High Humidity, Station Lost Contact, and Station Battery Low. The high humidity alert threshold is set on a per station basis, and alerts can be enabled and disabled on a per station basis. The content of the alert messages is defined in the sendAlert() function in the Temboo Routines section of the LoRa IOT Gateway software.

SMS notifications and alerts are sent by sending an email to a Mobile Network Operator's email-to-SMS Gateway. For example, the SMS Gateway addresses for the four major US MNOs are as follows:

AT&T: [insert 10-digit number]@txt.att.net

Sprint: [insert 10-digit number]@messaging.sprintpcs.com

T-Mobile: [insert 10-digit number]@tmomail.net

Verizon: [insert 10-digit number]@vtext.com

Sending an email to the above addresses results in an SMS message being sent to the 10-digit number. All MNOs have SMS Gateways, and gateway addresses can be found by Googling "email to SMS gateway." The 10-digit mobile number and SMS gateway address are stored on the SD memory card.

A Temboo account and a Gmail address are required to send email from the LoRa IOT Gateway. Visit Google to set up an account for everything Google, including Gmail and Google Drive. Visit Temboo to set up an account and get the necessary authentication information by creating a Google Gmail SendEmail Choreo. Temboo offers a 30 day free trial and has an affordable Maker Plan. See Step: IOT Gateway SD Memory Card Authentication File for details of the Temboo authentication and Google authentication information required and how to store it.

Step 6: Environmental Data Cloud Storage

The photo above shows the hourly environmental data storage in the cloud on Google Drive in Google Sheets.

Data is sent to the cloud hourly at 2 minutes past the hour. Column A contains the hour of the day the update was received; in the case of the 12AM update, the hour is replaced with the date to indicate the day. Subsequent columns contain the station data. The data stored in each column of the Google Sheets is defined in the spreadsheetUpdate() function in the Temboo Routines section of the LoRa IOT Gateway software. Additional stations and station data can be stored by editing the spreadsheetUpdate() function and adding the corresponding column headers to the Google Sheets document.

A Temboo account and Google account are required to send data to Google Drive from the LoRa IOT Gateway. Visit Google to set up an account for everything Google, including Gmail and Google Drive. Visit Temboo to set up an account and get the necessary authentication information by creating a Google Sheets AppendValues Choreo. Temboo offers a 30 day free trial and has an affordable Maker Plan. See Step: IOT Gateway SD Memory Card Authentication File for details of the Temboo authentication and Google authentication information required and how to store it.

Step 7: Internet Accessible Dashboard

The photo above shows the AdafruitIO real time station dashboard display. The dashboard is updated every ten minutes.

The LoRa IOT Gateway software includes an MQTT (Message Queue Telemetry Transport) client from Adafruit that publishes data streams to AdafruitIO which is essentially an MQTT Broker. Each data stream is referred to as a "feed", for example, Outside Temperature is one feed. Adafruit provides tools for creating a dashboard that can be used for graphical display of the feeds.

Adafruit has a very good tutorial that explains Adafruit account set up, creation of AdafruitIO keys, creating feeds, and creating a dashboard. As AdafruitIO is in Beta testing, Adafruit currently limits the number of feeds per account to ten.

In the current gateway software, the AdafruitIO (AIO) Username and Key are saved in a header file AIO_Account.h as follows:

#define AIO_USERNAME    "AIO_Username_Here_Between_Quotes"

#define AIO_KEY         "AIO_Key_Here_Between_Quotes"

The gateway software must be recompiled with your AIO username and key in the AIO_Account.h file.

In a future software release and if AdafruitIO is used for the realtime dashboard (see discussion below), the AIO username and key will be stored on the SD Card with Temboo and Google authentication data, eliminating the need to recompile the software for a new user.

As of the date this Instructable was published, I have not been able to achieve long term stability of the MQTT connection to AdafruitIO. The connection is stable for 24 - 48 hours from booting the gateway, but eventually disconnects with a -1 return code. Attempts by the software to reestablish the connection fail. Other operations of the gateway are not impacted by this.

Under investigation for a future software release is implemention of a webserver on the gateway to serve the gauges directly for the realtime dashboard without requiring an MQTT connection or AdafruitIO. Defining gauge components in JavaScript using the HTML5 canvas, serving the HTML code from the SD Card, and using Ajax to update the gauges may be achievable within the memory constraints of the Arduino Mega.

Step 8: LoRa IOT Gateway Software

The LoRa IOT Gateway software is written in a procedural style and liberally commented to encourage enhancements and reuse. The software is published here on Github and is also attached below.

In the current software version, the only change needed to adapt the gateway software for a new user is to update the AIO_Account.h file as described in Step: Internet Accessible Dashboard. All other changes to configure the gateway for a new user or location are made via the LCD user interface and the authentication file on the SD Memory Card as described in Step: IOT Gateway SD Memory Card Authentication File.

An overview of the software structure is provided in this step, however, a detailed understanding of the software implementation is best accomplished by reviewing the code and comments.

.............................................................................................................................

Setup

The setup code initializes the various Arduino pins used in the gateway, initializes the SD Card and LCD, and then calls a sequence of initialization routines.

eepromRead()

The first time gateway software runs on an Arduino Mega, default settings are wrtiien to, and then read from the EEPROM as follows:

  • Time Zone = East Coast US
  • Day Light Savings = On
  • Pressure Calibration Offset = 0.36
  • All station names set to "Station "
  • Temperature units = Fahrenheit
  • LCD Display Mode = Last Station Temperature
  • Number of Active Stations = 1

If the software has been previously run on the Arduino Mega, the settings previously saved to EEPROM are restored. During start up the LCD will indicate if settings are being restored from EEPROM or if no data was found and settings are being initialized.

RFM95Initialize()

Performs a hard reset of the LoRa radio. Initializes the radio frequency to 915-MHz and sets Tx power to +23dBm (although Tx is not currently used by the gateway). In a future software release the frequency and Tx power could be provisioned via the LCD user interface.

ethernetInitialize()

Initializes the Ethernet interface. The gateway expects the IP address to be assigned via DHCP, and displays the IP address assigned.

readGatewayData()

Reads Temboo and Google authentication data from the SD Card.

sendStatus()

Sends an SMS notification with all zero data to verify Temboo and Google Gmail authentication is working.

spreadsheetUpdate()

Sends a spreadsheet update row with all zero data to the Google Sheets on Google Drive to verify Temboo and Google Drive authentication is working.

AIOconnect()

Connect to Adafruit IO MQTT broker to verify Adafruit IO authentication is working.

.............................................................................................................................

Main Program Loop

The main program loop() performs the following functions each time through the loop:

-- Check if the FUNC push button switch has been pressed - if yes, call Function() to execute the LCD menu routines.

-- Check systemTimeUpdate() to see if one second has passed since last time through Loop - if yes, do the following:

-- Execute the outer time-based event loop that executes specific functions at a given second of each minute. Two functions are executed every minute:

---- At zero second of each minute, ping the AdafruitIO MQTT broker.

---- At 50 second mark of each minute, update line 2 of the LCD if in Fixed Station or Cycle Station display mode.

-- Execute the inner time-based event loop that executes specific functions at a given minute of each hour. The following functions are executed:

---- Every 10 minutes:

-------- Publish data feeds to AdafruitIO.

-------- Check to see if an Alert needs to be sent.

---- At 2 minutes past the hour, send hourly data update to Google Drive.

---- At 3 minutes past the hour, update system time using Network Timing Protocol (NTP).

---- At 4 minutes past the hour, check the AdafruitIO MQTT connection; if down, try to reconnect.

---- At 30 minutes past the hour, send an SMS Notification.

---- At 40 minutes past the hour, reinitialize the LoRa Radio. Over a time span of days the LoRa Radio would freeze, reinitializing the radio hourly seems to solve the problem and doesn't otherwise impact performance of the gateway.

-- Check if the LoRa Radio has received a packet from a remote station - if yes, get the packet and process it.

-- Update line 2 of the LCD if in Last Station display mode and new station data has been received.

.............................................................................................................................

Interrupt Service Routine (ISR)

LoRa IOT Gateway software uses one ISR attached to the FUNC push button switch. The purpose of the interrupt is to interrupt execution of the main program loop when FUNC is pressed in order to enter the LCD Main Menu function. Interrupts are disabled while executing the LCD Main Menu function. Upon exiting the LCD Main Menu function and returning to the main program loop, an NTP update is called to correctly restore system time as time keeping is not running while the gateway is executing the LCD Main Menu function.

.............................................................................................................................

RFM95W LoRa Radio Routines

getRadioPacket()

Called once a wireless packet has been received by the LoRa Radio. Reads the packet into the remote station data array, and adds RSSI and contact time to the array.

checkAlerts()

Called from the main program loop every ten minutes to check if Lost Contact, High Humidity, or Low Battery alert needs to be sent.

.............................................................................................................................

Temboo Routines

sendStatus()

Builds the notification message to be emailed to the MNO email-to-SMS gateway. By default the software sends the temperature and humidity for the first four active stations. Modify this function to change the content of the Notification messages.

sendAlert()

Builds the alert message for Lost Contact, High Humidity, or Low Battery to be emailed to the MNO email to SMS gateway. Modify this function to change the content of the Alert messages.

sendEmail()

Code generated by the Temboo web site for sending email via the Temboo Gmail Choreo.

spreadsheetUpdate()

Builds the spreadsheet update to be sent via the Temboo Append Row Choreo to add one row of data to the Google Sheet on Google Drive. Modify this function to change the stations and data sent to Google Sheets.

sendSpreadsheetUpdate()

Code generated by the Temboo website for appending a row of data to a Google Sheets on Google Drive.

.............................................................................................................................

Time Keeping Routines

systemTimeUpdate()

Counts seconds for local time keeping and maintains a localized epoch. Updates LCD line 1 HH:MM:SS 24-hour format time display.

ntpTimeUpdate()

Called hourly from the main program loop and after LCD Menu function to synchronize gateway local time. Calls getNTPtime() to get current epoch, localizes the epoch with time zone and day light savings offsets, and updates gateway local time. Displays local date and time upon a successful NTP update.

.............................................................................................................................

EEPROM Routines

The EEPROM routines initialize, read, and write the EEPROM data structure below. The EEPROMex.h readBlock() and updateBlock() functions are used to read and write the EEPROM data structure. To preserve EEPROM life, the updateBlock() function checks if an EEPROM byte has changed before writing that byte.

In future, additional parameters requiring persistence can easily be added to the data structure.

typedef struct { 
int Version; 
long TimeZoneOffset;                                                          
long DayLightSavings;                                                         
float PressCal;                                                               
char Station[MAX_STATIONS][10];                                              
int StationHumiThreshold[MAX_STATIONS];                                       
byte StationAlert[MAX_STATIONS];                                             
byte setBackLight;                                                           
byte Fahrenheit;                                                            
byte StationDisplayMode;                                                   
byte StationDisplayNum;                                                      
byte ActiveStations;                   
} eepromData;

.............................................................................................................................

LCD / Push Button Menu Routines

An enumeration (enum) is used to index the LCD Main Menu items. The first entry in the enumeration is "START_FUNCTIONS" and the last entry is "END_FUNCTIONS." These entries bookend the menu items, allowing menu items to be added without having to explicitly keep track of the number of menu items. Testing for START_FUNCTIONS and END_FUNCTIONS identifies the beginning and end, respectively, of the list of menu items regardless of the number of menu items in between.

enum {
  START_FUNCTIONS,
  SET_BACK_LIGHT,
  SET_DISPLAY_MODE,
  DISPLAY_RSSI,
  DISPLAY_BATT, 
  SET_ALERTS,
  SET_HUMI_THRES,
  SET_NUMBER_STATIONS,
  SET_STATION_NAMES,
  SET_DAYLIGHT_SAVINGS,
  SET_TIME_ZONE,
  SET_PRESS_SENSOR_CAL,
  SET_DEGREES_F_C,
  END_FUNCTIONS
};

When FUNC is pressed, execution passes to a while(1) loop in Function(). While in this loop the four push button switches are monitored.

FUNC - causes execution to exit Function() and return to the main loop

ENTER - causes the displayed menu item to be executed

UP - causes the displayed menu item to increment up to the next menu item

DOWN - causes the displayed menu item to decrement down to the next menu item

Upon entering Function() and each time UP or DOWN is pressed, functionDisplay(), a switch / case based on the menu items from the above enumeration, is called to display the name of the menu item. Similarly, when ENTER is pressed, functionExecute(), a switch / case also based on the menu items from the above enumeration, is called to direct program execution to the corresponding menu item function.

With this structure it is a simple matter to add or remove main menu items. A similar structure is used to implement menu selection options within main menu options, for example, look at how the Display Mode menu is implemented using a display mode enumeration and the doDisplayMode() function.

A detailed description of the operation of each menu item is included in Step: LoRa IOT Gateway User Interface.

In certain cases, the enumeration is also used to index into a character array for the corresponding string to be displayed on the LCD. For example, below is the Display Mode enumeration and corresponding character array with Display Mode strings.

enum {
  START_DISPLAY_MODES,
  LAST_STATION_TEMP,
  LAST_STATION_HUMI,
  FIXED_STATION_TEMP,
  FIXED_STATION_HUMI,
  CYCLE_STATION_TEMP,
  CYCLE_STATION_HUMI,
  END_DISPLAY_MODES
};
const char* DISPLAY_MODE[] = {
  " ",
  "Last Stn Temp",
  "Last Stn Humi",
  "Fixed Stn Temp",
  "Fixed Stn Humi",
  "Cycle Stn Temp",
  "Cycle Stn Humi",
  " "
};

This technique was not used as extensively as it could have been in order to conserve RAM. Referring to: https://www.baldengineer.com/arduino-f-macro.html

...the const keyword will tell a compiler that a variable is a constant and can’t change. Depending on the optimizations used, the avr-gcc compiler will avoid putting that value into RAM since it knows it’ll never change. But that technique won’t work with c-style strings or arrays. Since arrays are based around pointers, the compiler needs to put the array into RAM so that pointers work correctly. Which means, all strings need to be put into RAM before they can be used.

So even though the constant declaration is used, string constants are written into RAM. As the gateway software uses many string constants to implement the user interface, RAM usage was getting well above 50% in the Arduino Mega. The F() function is used to alleviate this situation. So what does the F() function do?

The F() macro tells the compiler to leave this particular array in PROGMEM. Then when it is time to access it, one byte of the data is copied to RAM at a time. There’s a small performance overhead for this extra work. However, printing strings over Serial or to a LCD is a really slow process, so a few extra clock cycles really won’t matter.

Note that the the F() function only works on a single string, it won't work on strings that change, or with any other variable type. Below is a code snippet showing how the F() is used.

void functionDisplay(byte functionToDisplay) {
  clearDisplayLine1(F("Select Function:"));
  switch (functionToDisplay)
  {
    case 0:
      {
        break;
      }
    case SET_BACK_LIGHT:
      {
        updateDisplayLine2(F("Set Back Light"));
        break;
      }
    case SET_NUMBER_STATIONS:
      {
        updateDisplayLine2(F("Set Number Stns"));
        break;
      }

.............................................................................................................................
SD Card File Routines

The function readGatewayData() reads Temboo and Goole authentication from a file named GW_Data.txt on the SD card during set up. Refer to Step:IOT Gateway SD Memory Card Authentication File for information on the content of GW_Data.txt and how to create it.

GW_Data.txt contains strings that are delimited by "[" and "]". This assumes the authentication strings do not contain these characters; if that is ever not the case, new delimiting characters will have to be used. The readGatewayData() function reads the strings into the string array gatewayData[12]. The following enumeration is used to index the authentication strings in the gatewayData array.

enum {
  TEMBOO_ACCOUNT,
  TEMBOO_APP_KEY_NAME,
  TEMBOO_APP_KEY,
  SS_TITLE,
  SS_REFRESH_TOKEN,
  SS_CLIENT_SECRET,
  SS_CLIENT_ID,
  EM_SUBJECT,
  EM_PASSWORD,
  EM_USERNAME,
  EM_TO_ADDRESS
};

Additional authenication or other strings can be added by increasing the size of the gatewayData array and adding corresponding entries to the enumeration.

.............................................................................................................................
LCD Routines

The updateStationDisplay() functions creates the string lastStationDisplay which contains the station data displayed on line 2 of the LCD according to the Display Mode selected. Whenever another function uses line 2 of the LCD to display status or etc., lastStationDisplay is used to restore the station data when the function has finished.

The following functions are used to manipulate the lines on the LCD and simplify the code.

updateDisplayLine12(String Line_1, String Line_2) - updates Line 1 & 2

updateDisplayLine1(String Line_1) - updates Line 1 leaving Line 2 unchaged

updateDisplayLine2(String Line_2) - updates Line 2 leaving Line 1 unchaged

clearDisplayLine1(String Line_1) - clears Lines 1 & 2, and updates Line 1

clearDisplayLine2(String Line_2) - clears Lines 1 & 2, and updates Line 2

That's it for the LoRa IOT Gateway software.

Step 9: Indoor Station Software

The LoRa Indoor Station software is written in a procedural style and liberally commented to encourage enhancements and reuse. The software is published here on Github and is also attached below. Highlights of the software are provided in this step, however, a detailed understanding of the software implementation is best accomplished by reviewing the code and comments.

The indoor stations and outdoor station are collectively referred to as remote stations.

Each remote station requires a unique ID number to be correctly identified by the LoRa IOT Gateway. The ID number is hard coded in the current software, therefore, the remote station software must be recompiled with a unique remote station ID for each remote station. The software currently limits the maximum number of stations to ten, so the station IDs should begin at 0x00 and end with 0x09. In a future software version this will be handled automatically between the gateway and the remote stations, allowing the system to be plug and play.

Adafruit libraries are used for initializing and reading temperature and humidity values from the SHT31 sensor.

Remote stations send up to five floating point values to the gateway as shown in the code snippet below.

typedef union {
  float floatingPoint;
  byte binary[sizeof(floatingPoint)];
} binaryFloat;

binaryFloat airTemp;
binaryFloat gndTemp;
binaryFloat humidity;
binaryFloat pressure;
binaryFloat batVolt;

The union maps floating point values to bytes allowing the floating point values to be sent across the wireless link as bytes. Other variables such as int and long can be sent in the same way. The number of variables sent over the wireless link can be increased by defining new variables as above, and adding the variables to the wireless packet described below.

The indoor stations send air temperature (airTemp), humidity, and battery voltage (batVolt). The battery voltage in the case of indoor stations is the power supply voltage to the Feather board. The indoor stations set the ground temperature (gndTemp) and pressure values to zero.

The size of packet sent over the wireless link is determined as 5 floats x 4 bytes per float + 1 byte for remote station ID = 21 bytes. The wireless packet is built up in the buildWirelesspacket() function using the binary mapping of the floating point variables as follows.

for(int n=0; n<4; n++)  {
    wirelessPacket[n] = airTemp.binary[n];
    wirelessPacket[n+4] = gndTemp.binary[n];
    wirelessPacket[n+8] = humidity.binary[n];
    wirelessPacket[n+12] = pressure.binary[n];
    wirelessPacket[n+16] = batVolt.binary[n];
  }
  wirelessPacket[20] = stationID;

This process is reversed in the gateway software to extract the bytes from the wireless packet and map them back into the corresponding floating point value.

The following code is used to enable Watch Dog Timer (WDT) sleep mode and set the prescaler to its maximum value which causes the WDT to wake up the processor every ~8 seconds.

set_sleep_mode(SLEEP_MODE_PWR_DOWN);<br>MCUSR &= ~(1 << WDRF);
WDTCSR |= (1 << WDCE) | (1 << WDE);
WDTCSR = (1<< WDP0) | (1 << WDP3);
WDTCSR |= (1 << WDIE);
sleep_enable();

When the WDT wakes up the processor, the Interrup Service Routine (ISR) increments a variable stateTime to count 8 second increments of time, and the loop() function is executed once, finally putting the processor back in sleep mode. The loop() function is a time-based event loop that causes sensor readings to be taken and sent to the gateway every ten minutes.

The LoRa Radio is put in sleep mode in between sending wireless packets to gateway.

The rest of the indoor station code should be self explanatory. That's it for the Indoor Station software.

Step 10: Outdoor Station Software

The LoRa Outdoor Station software is written in a procedural style and liberally commented to encourage enhancements and reuse. The software is published here on Github and is also attached below. The outdoor station software is based on the indoor station software, so only the differences are highlighted in this step. A detailed understanding of the software implementation is best accomplished by reviewing the code and comments.

The outdoor station sends air temperature (airTemp), ground temperature (gndTemp), humidity, pressure and battery voltage (batVolt). The battery voltage in the case of the outdoor station is the voltage being delivered by the Lithium Ion battery to the Feather board..

The outdoor station uses a BME280 temperature, humidity, and pressure sensor. Adafruit libraries are used for initializing and reading temperature, humidity, and pressure values from the BME280 sensor.

A waterproof DS18B20 temperature sensor is used for measuring the ground temperature. Adafruit one wire libraries are used for initializing and reading temperature from the DS18B20.

That's it for the Outdoor Station software.

Step 11: IOT Gateway SD Memory Card Authentication File

The Arduino Mega SD Card slot takes a micro SD card, refer to step: LoRa IOT Gateway Hardware for details of the specific card used. Memory capacity of the SD Card is not critical. The SD Card used is 8GB, leaving plenty of memory for gateway data logging to be added in future.

Authentication data is stored on the SD card in a file called GW_Data.txt. In this way the gateway software can be deployed with out recompilation as the user specific authentication data is stored on the SD card. Think of the SD card as a SIM card in a mobile phone.

Gateway software reads the authentication data from GW_Data.txt on the SD Card during gateway software set up. The readGatewayData() function in the gateway software searches for the delimiters "[" and "]", and copies the data between the delimiters into an string array gatewayData. Characters outside the square brackets are ignored and only included in the file to label the authentication data strings.

TEMBOO_ACCOUNT: user specified name of the Temboo account

TEMBOO_APP_KEY_NAME: user specified name of the application

TEMBOO_APP_KEY: generated by the Temboo site

SS_Title: user specified title/name of the Google Sheets document for cloud data storage

SS_RefreshToken: generated by the Temboo site during Google Sheets choreo set up

SS_ClientSecret: generated by the Temboo site during Google Sheets choreo set up

SS_ClientID: generated by the Temboo site during Google Sheets choreo set up

EM_Subject: user defined subject for email / SMS message

EM_Password: users Gmail password

EM_Username: users Gmail email address

EM_ToAddress: email "to" address. For sending SMS, ten digit mobile phone number @ operator email-to-SMS gateway address.

The GW_Data.txt file should be created and saved to the SD card using a text editor that doesn't insert non displayable formatting characters. Sublime Text was used by the author to create the file.

TEMBOO_ACCOUNT      [temboo account name goes here]
TEMBOO_APP_KEY_NAME [application name goes here]
TEMBOO_APP_KEY      [application key goes here]
SS_Title            [spreadsheet title/name goes here]
SS_RefreshToken     [refresh token goes here]
SS_ClientSecret     [client secret goes here]
SS_ClientID         [client ID goes here.apps.googleusercontent.com]
EM_Subject          [email subject goes here]
EM_Password         [email password goes here]
EM_Username         [email address goes here]
EM_ToAddress        [email to address goes here]
Microcontroller Contest 2017

Runner Up in the
Microcontroller Contest 2017