Hackster is hosting Hackster Holidays, Ep. 4: Livestream & Giveaway Drawing. Start streaming on Wednesday!Stream Hackster Holidays, Ep. 4 on Wednesday!
cstram
Published © GPL3+

End2End LoRa gateway and device tutorial

I would like to show you how I solved my problem of measuring the temperature and the humidity of my Wine Cellar in the basement of my house

IntermediateFull instructions provided3,075
End2End LoRa gateway and device tutorial

Things used in this project

Hardware components

Arduino Nano R3
Arduino Nano R3
×1
Raspberry Pi 3 Model B
Raspberry Pi 3 Model B
×1
Flash Memory Card, MicroSD Card
Flash Memory Card, MicroSD Card
×1
SD Memory Adapter
×1
Raspberry power supply
×1
Audio / Video Cable Assembly, Ultra Slim RedMere HDMI to HDMI
Audio / Video Cable Assembly, Ultra Slim RedMere HDMI to HDMI
×1
Raspberry Pi Keyboard
Raspberry Pi Keyboard
×1
RFM95W Module
×2
868Mhz Antenna
×2
I2C OLED Display 128x64
×1
DHT22 Temperature Sensor
DHT22 Temperature Sensor
×1

Story

Read more

Schematics

Gateway Circuit Diagram

LoRa Device Diagram

Code

Lora device Sketch for Arduino

Arduino
#include <lmic.h>
#include <hal/hal.h>
#include "U8glib.h"
#include <DHT.h>


// OLED Display configuration
U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_NONE|U8G_I2C_OPT_DEV_0);  // I2C / TWI 

#define DHTTYPE    DHT22     // DHT 22 (AM2302)
#define DHTPIN 8

DHT dht(DHTPIN, DHTTYPE);


// LoRaWAN NwkSKey, network session **** DO NOT USE AS IS. NEEDS TO BE CHANGED AS PER TUTORIAL ******
static const PROGMEM u1_t NWKSKEY[16] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };

// LoRaWAN AppSKey, application session key **** DO NOT USE AS IS. NEEDS TO BE CHANGED AS PER TUTORIAL ******
static const u1_t PROGMEM APPSKEY[16] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };

// LoRaWAN end-device address (DevAddr) **** DO NOT USE AS IS. NEEDS TO BE CHANGED AS PER TUTORIAL ******
static const u4_t DEVADDR = { 0xFFFFFFFF };

// These callbacks are only used in over-the-air activation, so they are
// left empty here (we cannot leave them out completely unless
// DISABLE_JOIN is set in config.h, otherwise the linker will complain).
void os_getArtEui (u1_t* buf) { }
void os_getDevEui (u1_t* buf) { }
void os_getDevKey (u1_t* buf) { }

//static uint8_t mydata[] = "LoRa Device Test";
static osjob_t sendjob;

// Schedule data trasmission in every this many seconds (might become longer due to duty
// cycle limitations).
// we set 10 seconds interval
const unsigned TX_INTERVAL = 10;

// Pin mapping according to Cytron LoRa Shield RFM
const lmic_pinmap lmic_pins = {
  .nss = 10,
  .rxtx = LMIC_UNUSED_PIN,
  .rst = 7,
  .dio = {2, 5, 6},
};

void onEvent (ev_t ev) {

  switch(ev) {
    case EV_TXCOMPLETE:
      Serial.println(F("EV_TXCOMPLETE (includes waiting for RX windows)"));
      // Schedule next transmission
      os_setTimedCallback(&sendjob, os_getTime()+sec2osticks(TX_INTERVAL), do_send);
      break;  
    default:
      Serial.println(F("Unknown event"));
      break;
  }
}

void do_send(osjob_t* j){
  char ValueT[5];
  char ValueH[5];
  int Temperature;
  int Humidity;
  
  // Check if there is not a current TX/RX job running
  if (LMIC.opmode & OP_TXRXPEND) {
    Serial.println(F("OP_TXRXPEND, not sending"));
  } else {
  // Get Temperature
  Temperature=int(dht.readTemperature()*10);
  // Get humidity 
  Humidity=int(dht.readHumidity()*10);

  // Format the values to be printed on OLED Screen
  sprintf(ValueT, "%d.%d", int(dht.readTemperature()),int(dht.readTemperature()*10)-(int(dht.readTemperature())*10));
  sprintf(ValueH, "%d.%d", int(dht.readHumidity()),int(dht.readHumidity()*10)-(int(dht.readHumidity())*10));
  u8g.firstPage();  
  do {
    draw("LoRa", 50, 10);
    draw("Temperature:", 0, 30);
    draw(ValueT, 85, 30);
    draw("C", 115, 30);
    draw("Humidity:", 0, 50);
    draw(ValueH, 85, 50);
    draw("%", 115, 50);
    } while( u8g.nextPage() );
 
    // prepare and schedule data for transmission 
    LMIC.frame[0] = Temperature >> 8; 
    LMIC.frame[1] = Temperature;
    LMIC.frame[2] = Humidity >> 8;
    LMIC.frame[3] = Humidity; 
    LMIC_setTxData2(1, LMIC.frame, 4, 0); // (port 1, 4 bytes, unconfirmed)

    Serial.println(F("Packet queued"));
  }
  // Next TX is scheduled after TX_COMPLETE event.
}

void setup() {
  Serial.begin(9600);
  Serial.println(F("Starting"));
  u8g.setColorIndex(1);         // pixel on for Display 

  // Initialize Temp sensor device.
  dht.begin();

  // LMIC init
  os_init();

  // Reset the MAC state. Session and pending data transfers will be discarded.
  LMIC_reset();

  // Set static session parameters. Instead of dynamically establishing a session
  // by joining the network, precomputed session parameters are be provided.
  uint8_t appskey[sizeof(APPSKEY)];
  uint8_t nwkskey[sizeof(NWKSKEY)];
  memcpy_P(appskey, APPSKEY, sizeof(APPSKEY));
  memcpy_P(nwkskey, NWKSKEY, sizeof(NWKSKEY));
  LMIC_setSession (0x1, DEVADDR, nwkskey, appskey);

  // Disable link check validation
  LMIC_setLinkCheckMode(0);

  // Disable ADR
  LMIC_setAdrMode(false);

  // TTN uses SF9 for its RX2 window.
  LMIC.dn2Dr = DR_SF9;

  // Set data rate and transmit power for uplink (note: txpow seems to be ignored by the library)
  LMIC_setDrTxpow(DR_SF7,14);

  // Start job
  do_send(&sendjob);
}

void loop() {
  os_runloop_once();

}

void draw(char* parola, int posx, int posy) {
  // graphic commands to redraw the complete screen should be placed here  
  u8g.setFont(u8g_font_tpssb);
  u8g.drawStr( posx, posy, parola);
}

Credits

cstram

cstram

16 projects • 21 followers
Passionate about IT, Electronics and DIY. Strong believer in Raspberry and Arduino devices. Experience in digital television and security.

Comments