Maker.io main logo

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

Hardware Setup

Connect the Arduino as shown:

arduino-client

Connect the Raspberry Pi as shown:

rpi-server

Arduino Software

Install the following libraries for Arduino:

Copy Code
/**

 * 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:

Copy Code
pip install adafruit-circuitpython-rfm9x

Save the following code to a file and run it with Python:

Copy Code
# 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.

rpi-server-receiving

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:

制造商零件编号 A000066
ARDUINO UNO R3 ATMEGA328P BOARD
Arduino
制造商零件编号 3072
RFM95W LORA RADIO TRANSCEIVER BR
Adafruit Industries LLC
RAK811 BREAKOUT BOARD 915MHZ
制造商零件编号 PIS-1027
RAK811 BREAKOUT BOARD 915MHZ
Pi Supply
制造商零件编号 2652
SENSOR HUM/PRESS I2C/SPI BME280
Adafruit Industries LLC
制造商零件编号 BB-32621
BREADBOARD TERM STRIP 3.20X2.08"
Bud Industries
制造商零件编号 PRT-12795
JUMPER WIRE M/M 6" 20PCS
SparkFun Electronics
Add all DigiKey Parts to Cart
TechForum

Have questions or comments? Continue the conversation on TechForum, DigiKey's online community and technical resource.

Visit TechForum