Shopping Cart

LoRa 32 example with RainmanWeather IoT Professional LoRa Weather Station

Posted by RainmanWeather on 2/9/2022 to RainmanWeather IoT LoRa Weather Station
LoRa 32 example with RainmanWeather IoT Professional LoRa Weather Station

Creating a custom receiver/gateway with a Heltec LoRa 32 using the Arduino IDE

The RainmanWeather IoT Professional LoRa Weather Station WiFi Wireless utilizes open LoRa protocol compatible with Arduino and ESP32 LoRa IoT devices. This example shows how to configure a Heltec WiFi Lora 32 IoT dev board to receive data from the LoRa Weather Station and create display on the OLED screen. This example also includes gateway functionality to forward data to the Blynk IoT Platform where the data can be visualized on Blynk Cloud and accessed via Blynk App.

Note this example assumes familiarity or ability to learn how to install and configure Aurduino IDE and Blynk applications. We do not provide assistance with these general topics as there are already many sources of information and tutorials available. We will be happy to assist with any details pertaining specifically to integration with the RainmanWeather IoT Professional LoRa Weather Station. Please use the comments section on this blog, contact us form, or Facebook comments to submit any questions or comments that you may have.

The Heltec Lora 32 is an ESP32 module with an SX1276 chip and a little 0.96" OLED screen. These are readily available for purchase on Amazon. There are several listings available with differing descriptions, so pick the best value/availability just be careful that the board itself is actually a Heltec (V2) dev board to get all the latest functionality like upgraded FLASH and better LoRa signal.
Following is a link to the particular version used for this example: https://www.amazon.com/dp/B07916Q3T6/ (an independent seller on Amazon in no way associated with RainmanWeather, not an affiliate link).

Heltec LoRa 32

To get the LoRa 32 board set up in the Arduino IDE follow the steps here: Heltec ESP32+LoRa Series Quick Start

In addition to the board installation, the following libraries are required in Arduino IDE for this example:

SPI.h
LoRa.h - by Sandeep Mistry https://github.com/sandeepmistry/arduino-LoRa
WiFi.h
WiFiClient.h
BlynkSimpleEsp32.h - https://github.com/blynkkk/blynk-library
U8g2lib.h - by oliver https://github.com/olikraus/u8g2

Note it is important to use only the specified libraries and especially Do Not include the Heltec library to ensure correct functionality.

Prior to using the example source code change the Blynk Template ID, Device Name, and Authentication Token to those generated by your Blynk Application. These values will be found under Device Info in the Blynk application. Also change the WiFi SSID and Password to match your device settings.

Upload the code to the LoRa 32 dev board via Arduino IDE and that's it! you should see the example receiving packets every 16 seconds and displaying the raw packet along with outdoor Temperature, Humidity, Wind Speed and Wind Gust on the OLED display. Although there is much more information in the packet, these are the most interesting parameters that can be displayed reasonably within a 16 second window. All of the available values are uploaded to Blynk for display on the cloud and app.

LoRa 32 OLED

Blynk Cloud Display

Blynk Cloud Display

Blynk Cloud Display

Blynk App Display

Blynk Cloud Display

LoRa Data Protocol

Decoding is performed by breaking up the LoRa packet into "nibbles" (or half-bytes) and reassembling per theĀ  data format map.

FT020T LoRa Data Format

Source code for receiver/gateway


#define rainmanwx_width 60
#define rainmanwx_height 58
static unsigned char rainmanwx_bits[] = {
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xff,
   0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xff, 0x03, 0x00, 0x00, 0x00,
   0x00, 0x00, 0xf8, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xff,
   0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x3f, 0x00, 0x00, 0x00,
   0x00, 0x80, 0x3f, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x0f, 0x00,
   0xf8, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x07, 0x00, 0xf0, 0x01, 0x00, 0x00,
   0x00, 0xe0, 0x03, 0x00, 0xe0, 0x03, 0x00, 0x00, 0x00, 0xf0, 0x03, 0x00,
   0xc0, 0x03, 0x00, 0x00, 0x00, 0xf0, 0x01, 0x00, 0xc0, 0x07, 0x00, 0x00,
   0x00, 0xf0, 0x01, 0x00, 0x80, 0x07, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00,
   0x80, 0x3f, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0xff, 0x01, 0x00,
   0x80, 0xff, 0x00, 0x00, 0x00, 0xff, 0x07, 0x00, 0xc0, 0xff, 0x00, 0x00,
   0x00, 0xff, 0x1f, 0x00, 0xe0, 0x1f, 0x00, 0x00, 0x00, 0xe1, 0x3f, 0x00,
   0xf0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0xf8, 0x01, 0x00, 0x00,
   0x00, 0x00, 0x7c, 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00,
   0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x01, 0x3c, 0x00, 0x00, 0x00,
   0x00, 0x00, 0xe0, 0x01, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x01,
   0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0x1e, 0x00, 0x00, 0x00,
   0x00, 0x00, 0xc0, 0x03, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x03,
   0x1e, 0x00, 0x40, 0x80, 0x00, 0x02, 0xc0, 0x03, 0x1e, 0x00, 0xe0, 0xc0,
   0x01, 0x07, 0xc0, 0x03, 0x1e, 0x00, 0xf0, 0xe0, 0x81, 0x07, 0xc0, 0x03,
   0x1e, 0x00, 0xf0, 0xe0, 0x81, 0x07, 0xe0, 0x03, 0x3e, 0x00, 0xf0, 0xe0,
   0x81, 0x07, 0xe0, 0x01, 0x7c, 0x00, 0xf8, 0xf0, 0xc1, 0x03, 0xf0, 0x01,
   0x78, 0x00, 0x78, 0xf0, 0xc1, 0x03, 0xf8, 0x00, 0xf8, 0x00, 0x78, 0xf0,
   0xc1, 0x03, 0x7c, 0x00, 0xf0, 0x03, 0x78, 0xf0, 0xc1, 0x03, 0x7e, 0x00,
   0xe0, 0x1f, 0x3c, 0xf8, 0xe0, 0xe1, 0x3f, 0x00, 0xc0, 0x3f, 0x3c, 0xf8,
   0xe0, 0xf1, 0x0f, 0x00, 0x00, 0x3f, 0x3c, 0xf8, 0xf0, 0xf1, 0x07, 0x00,
   0x00, 0x3c, 0x1e, 0xf8, 0xf0, 0xf0, 0x01, 0x00, 0x00, 0x20, 0x1e, 0x78,
   0xf0, 0x20, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x78, 0xf0, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x1f, 0x7c, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x3c,
   0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x3c, 0x78, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x0f, 0x3c, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x1e,
   0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x0f,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x07, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x80, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };

// Fill following credentials from Blynk account

#define BLYNK_TEMPLATE_ID           "************"
#define BLYNK_DEVICE_NAME           "*******"
#define BLYNK_AUTH_TOKEN            "********************************"

#include <SPI.h>
#include <LoRa.h>
#include <WiFi.h>
#include <WiFiClient.h>
#include <BlynkSimpleEsp32.h>
#include <U8g2lib.h>

U8G2_SSD1306_128X64_NONAME_F_SW_I2C u8g2(U8G2_R0, /* clock=*/ 15, /* data=*/ 4, /* reset=*/ 16);

char auth[] = BLYNK_AUTH_TOKEN;

int readByte = 0;
int bottom_nibble  = 0;
int top_nibble = 0;
int i = 0;
int start;
int device;
int fix_id;
int low_batt_flag;
float average_wind;
int average_windi;
int average_windb;
float gust_wind;
int gust_windi;
int gust_windb;
int wind_dir;
int wind_dirb;
float rainfall;
float temperature;
int humidity;
int light;
int lighta;
int lightb;
float uvi;

int station[31];

// Your WiFi credentials.
// Set password to "" for open networks.
char ssid[] = "************";
char pass[] = "**********";

void setup() {
  Serial.begin(115200);
  while (!Serial);

  Blynk.begin(auth, ssid, pass);

  SPI.begin(5, 19, 27, 18);
  LoRa.setPins(18, 14, 26); // set CS, reset, IRQ pin

  u8g2.setFont(u8g2_font_6x10_tf);
  u8g2.begin();

  u8g2.drawXBM( 32, 0, rainmanwx_width, rainmanwx_height, rainmanwx_bits);
  u8g2.sendBuffer();
  u8g2.setFont(u8g2_font_8x13B_tf);
  u8g2.drawStr(8, 64, "RainmanWeather");
  u8g2.sendBuffer();
  delay(6000); 

  Serial.println("LoRa Receiver");
  u8g2.clearBuffer();
  u8g2.drawStr(5, 20, "LoRa Receiver");
  u8g2.sendBuffer();

  if (!LoRa.begin(915800000)) {
    Serial.println("Starting LoRa failed!");
    u8g2.clearBuffer();
    u8g2.drawStr(5, 20, "Starting LoRa failed!");
    u8g2.sendBuffer();    
    while (1);
  }
   LoRa.setSpreadingFactor(7);
   LoRa.setSignalBandwidth(500E3);
   LoRa.crc();
}

void loop() {

Blynk.run();

  i=0;
  // try to parse packet
  int packetSize = LoRa.parsePacket();
  if (packetSize) {

    // received a packet
    Serial.print("Received packet size "); Serial.print (packetSize, HEX);  Serial.print(" '");
  
    u8g2.setFont(u8g2_font_6x10_tf);
    u8g2.clearBuffer();
    u8g2.drawStr(0, 10, "Received packet size ");
    u8g2.setCursor(122, 10);
    u8g2.print(packetSize, HEX);
    u8g2.sendBuffer();
    u8g2.setCursor(0, 20);

if (packetSize == 0x0f) {

    // read packet
    while (LoRa.available()) {

      readByte=(LoRa.read());

      Serial.print(readByte,HEX); Serial.print(" ");
      u8g2.print(readByte, HEX);
           
   top_nibble = ( readByte >> 4 ) & 0xf;
   station[i] = top_nibble;
   i++;
   bottom_nibble = readByte & 0xf;
   station[i] = bottom_nibble;
   i++;

    }

    u8g2.sendBuffer();

    // print RSSI of packet
    Serial.print("' with RSSI ");
    Serial.println(LoRa.packetRssi());

    start = (station[0] << 4) | (station[1]);
    device = station[2];
    fix_id = (station[3] << 4) | (station[4]);
    low_batt_flag = bitRead(station[5], 0);
    average_windi = (station[6] << 4) | (station[7]);
    average_windb = bitRead(station[5], 0);
    average_wind = (average_windb << 8 | average_windi);
    average_wind = (average_wind * 2.237) / 10;
    gust_windi = (station[8] << 4) | (station[9]);
    gust_windb = bitRead(station[5], 1);
    gust_wind = (gust_windb << 8 | gust_windi);  
    gust_wind = (gust_wind * 2.237) / 10; 
    wind_dir = (station[10] << 4) | (station[11]);
    wind_dirb = bitRead(station[5], 2);
    wind_dir =  (wind_dirb << 8 | wind_dir);  
    rainfall = (station[12] << 12 | station[13] << 8 |station[14] << 4 | station[15]);
    rainfall = (rainfall / 25.4) / 10; 
    temperature = (station[17] << 8 |station[18] << 4 | station[19]);
    temperature = (temperature - 400) / 10;
    humidity = (station[20] << 4) | (station[21]);
    light = (station[22] << 12 | station[23] << 8 |station[24] << 4 | station[25]);
    lighta = bitRead(station[16], 0); lightb = bitRead(station[16], 1);
    light = (lightb << 17 | lighta << 16 | light);
    light = (light * 0.0079);
    uvi = (station[26] << 4) | (station[27]);
    uvi = round(uvi /10);

    Serial.print("start: "); Serial.print(start, HEX);
    Serial.print(" device: "); Serial.print(device, HEX);
    Serial.print(" fix_id: "); Serial.print(fix_id, HEX);
    Serial.print(" low_batt_flag: "); Serial.print(low_batt_flag, HEX);
    Serial.print(" average_wind: "); Serial.print(average_wind);
    Serial.print(" gust_wind: "); Serial.print(gust_wind);
    Serial.print(" wind_dir: "); Serial.print(wind_dir);
    Serial.print(" rainfall: "); Serial.print(rainfall);
    Serial.print(" temperature: "); Serial.print(temperature);
    Serial.print(" humidity: "); Serial.print(humidity);
    Serial.print(" light: "); Serial.print(light);
    Serial.print(" uvi: "); Serial.print(uvi, 0);
    Serial.println();


    Blynk.virtualWrite(V0, temperature);
    Blynk.virtualWrite(V1, humidity);
    Blynk.virtualWrite(V2, average_wind);
    Blynk.virtualWrite(V3, gust_wind);
    Blynk.virtualWrite(V4, wind_dir);
    Blynk.virtualWrite(V5, rainfall);
    Blynk.virtualWrite(V6, light);
    Blynk.virtualWrite(V7, uvi);
    Blynk.virtualWrite(V8, low_batt_flag);


    u8g2.drawStr(0, 30, "Temperature: ");
    u8g2.setFont(u8g2_font_fub25_tf);
    u8g2.setCursor(0, 60);
    u8g2.print(temperature);
    u8g2.setFont(u8g2_font_fub11_tf);
    u8g2.print((char)176);
    u8g2.print((char)70);
    u8g2.sendBuffer();
    delay(3000);

    u8g2.clearBuffer();
    u8g2.setFont(u8g2_font_6x10_tf);
    u8g2.drawStr(0, 30, "Humidity: ");
    u8g2.setFont(u8g2_font_fub25_tf);
    u8g2.setCursor(0, 60);
    u8g2.print(humidity);
    u8g2.setFont(u8g2_font_fub11_tf);
    u8g2.print((char)37);
    u8g2.sendBuffer();
    delay(3000);

    u8g2.clearBuffer();
    u8g2.setFont(u8g2_font_6x10_tf);
    u8g2.drawStr(0, 30, "Average Wind: ");
    u8g2.setFont(u8g2_font_fub25_tf);
    u8g2.setCursor(0, 60);
    u8g2.print(average_wind);
    u8g2.setFont(u8g2_font_fub11_tf);
    u8g2.print("mph");
    u8g2.sendBuffer();
    delay(3000);    

    u8g2.clearBuffer();
    u8g2.setFont(u8g2_font_6x10_tf);
    u8g2.drawStr(0, 30, "Wind Gust: ");
    u8g2.setFont(u8g2_font_fub25_tf);
    u8g2.setCursor(0, 60);
    u8g2.print(gust_wind);
    u8g2.setFont(u8g2_font_fub11_tf);
    u8g2.print("mph");
    u8g2.sendBuffer();

   } else { Serial.println("Packet Discarded'"); }
  }
}

Tagged Products

 
 
comments powered by Disqus