Adding sensors to a lora node


(Nidaros) #1

Hi all,

I'm new to the LoRaWAN network, I've succesfully setup my gateway and a node.

For the gateway im using a rasperry pi with a Dragino Lora/GPS hat. And for my node I use an Arduino UNO with a Dragino Shield.

I recently bought a 36 in 1 sensor kit for the Arduino. I've setup most of the simple sensors, including the analog temperature sensor, it seems to work just fine.

I've done some google searches and I can't seem to find any detailed instructions on how to send sensor data to LoRa WAN.

Can anyone help me with this?

This is my temp sensor script:

#include <math.h>
 
int sensorPin = A5; // select the input pin for the potentiometer
 
double Thermistor(int RawADC) {
  double Temp;
  Temp = log(10000.0*((1024.0/RawADC-1))); 
  Temp = 1 / (0.001129148 + (0.000234125 + (0.0000000876741 * Temp * Temp ))* Temp );
  Temp = Temp - 273.15;            // Convert Kelvin to Celcius
   //Temp = (Temp * 9.0)/ 5.0 + 32.0; // Convert Celcius to Fahrenheit
   return Temp;
}
 
void setup() {
 Serial.begin(9600);
}
 
void loop() {
 int readVal=analogRead(sensorPin);
 double temp =  Thermistor(readVal);
 
 Serial.println(temp);  // display tempature
 //Serial.println(readVal);  // display tempature
 
 delay(500);
}

I hope to find some information from the real experts here :slight_smile:

  • Stijn

(Nidaros) #2

Bump

How to send payload from a sensor over LoRa?


(Rustie0125) #3

You need to use a Lorawan library, im not sure if the Dragino Shield bboard comes with its own library but i used this guide and setup, all you need todo is change your pins to match the UNO.

https://www.thethingsnetwork.org/forum/t/got-adafruit-feather-32u4-lora-radio-to-work-and-here-is-how/6863


(Nidaros) #4

Hey, Thanks for the reply. I got my lora node working, I was just wondering how to add a sensor (temp, gps eg.) and send the payload to TTN. As of now I can only send simple strings.


#5

Something like this:

I assume your data is an integer (int) value and therefore 2 bytes:

// map it to dataTX; 2 data bytes
  /* *************************************************
   * Suggested payload function for this data
   *
   *   var temperature = (bytes[0] << 8) | bytes[1];  
   *   return { payload: temperature / 100.0 };
   *
   * ************************************************/
dataTX[0] = temp100 >> 8;
dataTX[1] = temp100 & 0xFF;

And the send would then be something like: LMIC_setTxData2(1, dataTX, sizeof(dataTX), 0);
You don't need sizeof -1 as you are not sending a string.

If you have more data, you can use [2],[3] etc.

Note; consider temperature being negative! I normally add 273.1 to Celcius to get degrees Kelvin. You can subtract this in the payload function.


(Nidaros) #6

This is what we have right now:

// MIT License
// https://github.com/gonzalocasas/arduino-uno-dragino-lorawan/blob/master/LICENSE
// Based on examples from https://github.com/matthijskooijman/arduino-lmic
// Copyright (c) 2015 Thomas Telkamp and Matthijs Kooijman

// Adaptions: Andreas Spiess

#include <lmic.h>
#include <hal/hal.h>
//#include <credentials.h>
#include <math.h>
#include <stdlib.h>

#ifdef CREDENTIALS
static const u1_t NWKSKEY[16] = NWKSKEY1;
static const u1_t APPSKEY[16] = APPSKEY1;
static const u4_t DEVADDR = DEVADDR1;
#else
static const u1_t NWKSKEY[16] = { 0x84, 0xF4, 0x45, 0x69, 0x52, 0x66, 0x5F, 0x2E, 0x3F, 0xC4, 0xFC, 0x50, 0x29, 0x64, 0x10, 0x35 };
static const u1_t APPSKEY[16] = { 0xDE, 0xBD, 0xAF, 0xC8, 0xDD, 0xC4, 0x3B, 0xF3, 0x82, 0xEA, 0x9C, 0xC9, 0x8D, 0x25, 0xA4, 0xFF };
static const u4_t DEVADDR = 0x260110BF;
#endif

// 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 osjob_t sendjob;

// Schedule TX every this many seconds (might become longer due to duty
// cycle limitations).
const unsigned TX_INTERVAL = 20;

// Pin mapping Dragino Shield
const lmic_pinmap lmic_pins = {
    .nss = 10,
    .rxtx = LMIC_UNUSED_PIN,
    .rst = 9,
    .dio = {2, 6, 7},
};

int sensorPin = A5; // select the input pin for the potentiometer

void onEvent (ev_t ev) {
    if (ev == 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);
    }
}


double read_thermistor(int RawADC) {
  double Temp;
  Temp = log(10000.0*((1024.0/RawADC-1))); 
  Temp = 1 / (0.001129148 + (0.000234125 + (0.0000000876741 * Temp * Temp ))* Temp );
  Temp = Temp - 273.15;            // Convert Kelvin to Celcius
   //Temp = (Temp * 9.0)/ 5.0 + 32.0; // Convert Celcius to Fahrenheit
   return Temp;
}

void convert_to_string(char * outStr, int length, double val){
    snprintf(outStr,length,"%f",val);
}


void do_send(osjob_t* j, double temp){
    // Payload to send (uplink)
    uint8_t message[10];

    convert_to_string(message,2,temp);
    
    // Check if there is not a current TX/RX job running
    if (LMIC.opmode & OP_TXRXPEND) {
        Serial.println(F("OP_TXRXPEND, not sending"));
    } else {
      
        // Prepare upstream data transmission at the next possible time.
        LMIC_setTxData2(1, message, sizeof(message)-1, 0);
        Serial.println(F("Sending uplink packet..."));
    }
    // Next TX is scheduled after TX_COMPLETE event.
}

void setup() {
    Serial.begin(115200);
    Serial.println(F("Starting..."));

    // LMIC init
    os_init();

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

    // Set static session parameters.
    LMIC_setSession (0x1, DEVADDR, NWKSKEY, APPSKEY);

    // Disable link check validation
    LMIC_setLinkCheckMode(0);

    // 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_SF12,14);

    // Start job
    int readVal=analogRead(sensorPin);
    do_send(&sendjob, read_thermistor(readVal));
}

void loop() {
    os_runloop_once();
}

It does send an uplink packet but I can't see any data coming in to the console


(Somsak) #7

I might try to start with Arduino sketch generator here.

Somsak


(Nidaros) #8

This is very nice, but I'm using a different sensor and radio, will this still work?

radio: RFM96W (Using a Dragino shield)
sensor: dallas 18b20


(Nidaros) #9

I also have an analog temperature sensor btw


(Nidaros) #10

Tried the analog temperature sensor. I got some output on the TTN backend but it makes literally no sense. I have no clue at this point:

// MIT License
// https://github.com/gonzalocasas/arduino-uno-dragino-lorawan/blob/master/LICENSE
// Based on examples from https://github.com/matthijskooijman/arduino-lmic
// Copyright (c) 2015 Thomas Telkamp and Matthijs Kooijman

// Adaptions: Andreas Spiess

#include <lmic.h>
#include <hal/hal.h>
//#include <credentials.h>

#ifdef CREDENTIALS
static const u1_t NWKSKEY[16] = NWKSKEY1;
static const u1_t APPSKEY[16] = APPSKEY1;
static const u4_t DEVADDR = DEVADDR1;
#else
static const u1_t NWKSKEY[16] = { 0xC7, 0x0B, 0xC7, 0xDA, 0x80, 0x44, 0x6A, 0x7A, 0xEF, 0x7B, 0x5A, 0x1B, 0x83, 0x0E, 0x4E, 0xF8 };
static const u1_t APPSKEY[16] = { 0x23, 0x85, 0xF6, 0x35, 0x5C, 0x8D, 0x21, 0x04, 0xB8, 0xD1, 0xCD, 0x81, 0x88, 0x7D, 0xFD, 0xD6 };
static const u4_t DEVADDR = 0x2601109D;
#endif

// 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 osjob_t sendjob;

// Schedule TX every this many seconds (might become longer due to duty
// cycle limitations).
const unsigned TX_INTERVAL = 20;

// Pin mapping Dragino Shield
const lmic_pinmap lmic_pins = {
    .nss = 10,
    .rxtx = LMIC_UNUSED_PIN,
    .rst = 9,
    .dio = {2, 6, 7},
};
void onEvent (ev_t ev) {
    if (ev == 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);
    }
}

void do_send(osjob_t* j){
    // Payload to send (uplink)
    static uint8_t message[] = "hi";

    // Check if there is not a current TX/RX job running
    if (LMIC.opmode & OP_TXRXPEND) {
        Serial.println(F("OP_TXRXPEND, not sending"));
    } else {
        // Prepare upstream data transmission at the next possible time.
        LMIC_setTxData2(1, message, sizeof(message)-1, 0);
        Serial.println(F("Sending uplink packet..."));
    }
    // Next TX is scheduled after TX_COMPLETE event.
}

void setup() {
    Serial.begin(115200);
    Serial.println(F("Starting..."));

    // LMIC init
    os_init();

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

    // Set static session parameters.
    LMIC_setSession (0x1, DEVADDR, NWKSKEY, APPSKEY);

    // Disable link check validation
    LMIC_setLinkCheckMode(0);

    // 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_SF12,14);

    // Start job
    do_send(&sendjob);
}

void loop() {
    os_runloop_once();
}

(Somsak) #11

I think RFM96W work with the sketch generator providing that your pin mapping is correct. What I have been tested so far is to use with my RFM95 and AM2320. Now, I am trying to find the way to reduce its power consumption.


(Nidaros) #12

I think its an RFM95, but it uses the RFM96W chip. Very confusing, you can look it up on the manufacturers site


(Nidaros) #13

UPDATE:

It works! (kinda) I received data for around half an hour.

// MIT License
// https://github.com/gonzalocasas/arduino-uno-dragino-lorawan/blob/master/LICENSE
// Based on examples from https://github.com/matthijskooijman/arduino-lmic
// Copyright (c) 2015 Thomas Telkamp and Matthijs Kooijman

// Adaptions: Andreas Spiess

#include <lmic.h>
#include <hal/hal.h>
//#include <credentials.h>
#include <math.h>
#include <stdlib.h>

#ifdef CREDENTIALS
static const u1_t NWKSKEY[16] = NWKSKEY1;
static const u1_t APPSKEY[16] = APPSKEY1;
static const u4_t DEVADDR = DEVADDR1;
#else
static const u1_t NWKSKEY[16] = { 0xC7, 0x0B, 0xC7, 0xDA, 0x80, 0x44, 0x6A, 0x7A, 0xEF, 0x7B, 0x5A, 0x1B, 0x83, 0x0E, 0x4E, 0xF8 };
static const u1_t APPSKEY[16] = { 0x23, 0x85, 0xF6, 0x35, 0x5C, 0x8D, 0x21, 0x04, 0xB8, 0xD1, 0xCD, 0x81, 0x88, 0x7D, 0xFD, 0xD6 };
static const u4_t DEVADDR = 0x2601109D;
#endif

// 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 osjob_t sendjob;

// Schedule TX every this many seconds (might become longer due to duty
// cycle limitations).
const unsigned TX_INTERVAL = 20;

// Pin mapping Dragino Shield
const lmic_pinmap lmic_pins = {
    .nss = 10,
    .rxtx = LMIC_UNUSED_PIN,
    .rst = 9,
    .dio = {2, 6, 7},
};

int sensorPin = A5; // select the input pin for the potentiometer

void onEvent (ev_t ev) {
    if (ev == 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);
    }
}


double read_thermistor(int RawADC) {
  double Temp;
  Temp = log(10000.0*((1024.0/RawADC-1))); 
  Temp = 1 / (0.001129148 + (0.000234125 + (0.0000000876741 * Temp * Temp ))* Temp );
  Temp = Temp - 273.15;            // Convert Kelvin to Celcius
   //Temp = (Temp * 9.0)/ 5.0 + 32.0; // Convert Celcius to Fahrenheit
   return Temp;
}

void convert_to_string(char * outStr, int length, double val){
    Serial.println(F("Converting..."));
    dtostrf(val,2,0,outStr);
    Serial.println(outStr);
}


void do_send(osjob_t* j){
    // Payload to send (uplink)
    uint8_t message[8];
    int readVal=analogRead(sensorPin);
    double temp = read_thermistor(readVal);
    convert_to_string(message,8,temp);
    
    // Check if there is not a current TX/RX job running
    if (LMIC.opmode & OP_TXRXPEND) {
        Serial.println(F("OP_TXRXPEND, not sending"));
    } else {
      
        // Prepare upstream data transmission at the next possible time.
        LMIC_setTxData2(1, message,2, 0);

//        LMIC_setTxData2(1, message, sizeof(message)-1, 0);
        Serial.println(F("Sending uplink packet..."));
//        Serial.println(temp);
    }
    // Next TX is scheduled after TX_COMPLETE event.
}

void setup() {
    Serial.begin(115200);
    Serial.println(F("Starting..."));

    // LMIC init
    os_init();

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

    // Set static session parameters.
    LMIC_setSession (0x1, DEVADDR, NWKSKEY, APPSKEY);

    // Disable link check validation
    LMIC_setLinkCheckMode(0);

    // 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_SF12,14);

    // Start job
    do_send(&sendjob);
}

void loop() {
    os_runloop_once();
}