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
2x Adafruit RFM95W for North America or 2x Adafruit RFM95W for Europe
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