Arduino LoRa Weather Sensor
2019-06-10 | By ShawnHymel
License: None Arduino Raspberry Pi
As part of my upcoming "Arduino Project to Product" series, I created a simple (but somewhat realistic) Internet of Things (IoT) project to showcase how a microcontroller might be used to send sensor data from a remote location. The series will dive into tricks to lower power consumption and prepare the widget for manufacturing.
The series does not delve into the LoRa project itself, as it is only used for demonstration purposes. If you'd like to recreate the project or use it as a starting point for your own LoRa project, you are welcome to use the code and directions found here.
Required Parts
- Arduino UNO
- 2x Adafruit RFM95W for North America or 2x Adafruit RFM95W for Europe
- LoRa Breakout
- Adafruit BME280 Breakout
- Raspberry Pi 3 Model B
- 2x Breadboards
- Jumper wires
Hardware Setup
Connect the Arduino as shown:
Connect the Raspberry Pi as shown:
Arduino Software
Install the following libraries for Arduino:
/**
* LoRa Weather Client
*
* Author: Shawn Hymel
* Date: March 23, 2019
*
* Transmits temperature, humidity, and pressure data over raw
* LoRa radio. Reads data from BME280 sensor and transmits with
* following packet:
*
* | 1B From Addr | 1B To Addr | 2B Temp | 2B Humd | 2B Pres |
*
* Note that temperature, humidity, and pressure values are
* scaled up by 10 and rounded to nearest integer before being
* sent. The server will need to scale received values by 1/10.
* This is to avoid sending full floating point values.
*
* Required libraries:
* - http://www.airspayce.com/mikem/arduino/RadioHead/ (v1.59)
* - https://github.com/adafruit/Adafruit_Sensor
* - https://github.com/adafruit/Adafruit_BME280_Library
*
* License: Beerware
*/
#include <Wire.h>
#include <SPI.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
#include <RH_RF95.h>
#define DEBUG 0
// Parameters
const uint8_t LORA_NODE_ADDR = 0x01; // This node's address
const uint8_t LORA_SERVER_ADDR = 0x00; // LoRa receiving address
const int WAIT_TIME = 3000; // ms
const int TX_BUF_SIZE = 8; // Transmit buffer size
const float RFM_FREQ = 915.0; // Frequency for RFM95W
const int RFM_TX_POWER = 17; // 5..23 dBm, 13 dBm is default
// Pins
// SPI:
// MOSI = 10
// MISO = 12
// SCK = 13
const int RFM_RST_PIN = 2;
const int RFM_INT_PIN = 3;
const int RFM_CS_PIN = 4;
const int BME_CS_PIN = 10;
// Instance of radio driver over SPI
RH_RF95 rfm(RFM_CS_PIN, RFM_INT_PIN);
// Communicate with BME280 over SPI
Adafruit_BME280 bme(BME_CS_PIN);
// Transmit buffer
uint8_t tx_buf[TX_BUF_SIZE];
void setup() {
#if DEBUG
Serial.begin(9600);
#endif
// Manually reset RFM95W
pinMode(RFM_RST_PIN, OUTPUT);
digitalWrite(RFM_RST_PIN, HIGH);
delay(100);
digitalWrite(RFM_RST_PIN, LOW);
delay(10);
digitalWrite(RFM_RST_PIN, HIGH);
delay(10);
// Initialize RFM95W
if ( !rfm.init() ) {
#if DEBUG
Serial.println("Could not initialize RFM95");
#endif
while(1);
}
#if DEBUG
Serial.println("RFM95 initialized");
#endif
// Set RFM95W frequency
if ( !rfm.setFrequency(RFM_FREQ) ) {
#if DEBUG
Serial.println("Could not set frequency on RFM95");
#endif
while(1);
}
#if DEBUG
Serial.print("RFM95 frequency set to ");
Serial.print(RFM_FREQ);
Serial.print(" MHz");
#endif
// Set RFM95W transmit power from PA_BOOST pin
rfm.setTxPower(RFM_TX_POWER, false);
// Initialize BME280
if ( !bme.begin() ) {
#if DEBUG
Serial.println("Could not find BME280 on SPI bus");
#endif
while(1);
}
#if DEBUG
Serial.println("BME280 initialized");
#endif
}
void loop() {
// Read data
float temp = bme.readTemperature();
float humd = bme.readHumidity();
float pres = bme.readPressure() / 100.0;
// Scale (x10) and round data
int16_t tempt = (int16_t)((temp * 10.0) + 0.5);
int16_t humdt = (int16_t)((humd * 10.0) + 0.5);
int16_t prest = (int16_t)((pres * 10.0) + 0.5);
#if DEBUG
Serial.print("Temperature: ");
Serial.print(temp, 1);
Serial.println(" C");
Serial.print("Humidity: ");
Serial.print(humd, 1);
Serial.println("%");
Serial.print("Pressure: ");
Serial.print(pres, 1);
Serial.println(" hPa");
#endif
// Stuff buffer
tx_buf[0] = LORA_NODE_ADDR; // From address (this node) [1 byte]
tx_buf[1] = LORA_SERVER_ADDR; // To address (server) [1 byte]
tx_buf[2] = (0xff & tempt); // Temperature [2 bytes] little-endian
tx_buf[3] = (0xff & (tempt >> 8));
tx_buf[4] = (0xff & humdt); // Humidity [2 bytes] little-endian
tx_buf[5] = (0xff & (humdt >> 8));
tx_buf[6] = (0xff & prest); // Pressure [2 bytes] little-endian
tx_buf[7] = (0xff & (prest >> 8));
#if DEBUG
Serial.print("Sending buffer:");
for ( int i = 0; i < TX_BUF_SIZE; i++) {
Serial.print(" 0x");
Serial.print(tx_buf[i], HEX);
}
Serial.println();
Serial.println();
#endif
// Send data to server
rfm.send(tx_buf, TX_BUF_SIZE);
rfm.waitPacketSent();
delay(WAIT_TIME);
}
Server Code
Install Raspbian (with Desktop) on the Raspberry Pi. From a console, run the following command to install the RFM9x Python package:
pip install adafruit-circuitpython-rfm9x
Save the following code to a file and run it with Python:
# LoRa Weather Server
#
# Author: Shawn Hymel
# Date: March 23, 2019
#
# Receives and display temperature, humidity, and pressure data from raw LoRa
# radio. Run with lora-weather-client.ino on Arduino.
#
# Required packages:
# pip install adafruit-circuitpython-rfm9x
#
# License: Beerware
import time
import busio
from digitalio import DigitalInOut, Direction, Pull
import board
import adafruit_rfm9x
# Configure RFM9x LoRa Radio
CS = DigitalInOut(board.CE1)
RESET = DigitalInOut(board.D25)
spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO)
# Attempt to set up the RFM9x module
try:
rfm9x = adafruit_rfm9x.RFM9x(spi, CS, RESET, 915.0)
print('RFM9x detected')
except RuntimeError:
print('RFM9x error')
# Wait for LoRa packets
while True:
packet = None
packet = rfm9x.receive()
if packet != None:
print("Received:", str(packet))
# Split packet
from_addr = packet[0]
to_addr = packet[1]
temp = int.from_bytes(packet[2:4], byteorder='little') / 10.0
humd = int.from_bytes(packet[4:6], byteorder='little') / 10.0
pres = int.from_bytes(packet[6:8], byteorder='little') / 10.0
# Print results
print("From:", from_addr)
print("To:", to_addr)
print("Temperature:", temp, "C")
print("Humidity:", humd, "%")
print("Pressure:", pres, "hPa")
print()
Running
The Arduino should begin sending temperature, humidity, and pressure data to the Raspberry Pi over the LoRa radio at regular intervals. Check the console on the Raspberry Pi to see the sensor output.
Repository
The whole project may be found in this repository, if you would like to view it, clone it, etc.: https://github.com/ShawnHymel/lora-weather
Arduino Project to Product Video series see the following:
Have questions or comments? Continue the conversation on TechForum, DigiKey's online community and technical resource.
Visit TechForum