TTN water sensor

Hi I purchased a couple of the ADA4664/ dfrobot ultrasonic sensors and I am using them to sense the water level in 2 concrete water tanks.
I have on working on an arduino uno with the dragon lora shield, but I cant seem to get an instantaneous value and send it to the things network.
I am using the MCCI LMIC library (GitHub - thomaslaurenson/arduino-lmic: LoraWAN-in-C library, adapted to run under the Arduino environment).
the example works perfectly. but when I try to put the data from the sensor into the send array nothing arrives at the things network.

Would be awesome if someone had a sample of how to send that string from the sensor to the TTN.
Thanks heaps any help would be greatly appreciated. Is my wife is sick of running out of water.

1 Like

Hi Greg,

Welcome back :slight_smile:

Before we jump into troubleshooting any code.

  • Is there a gateway nearby? TTN Mapper plots gateways around you
  • Were you using TTNv2 or TTNv3(or The Things Stack)?

I havent had the chance to tinker around with the TTS just yet but we should be able to get your project on track one-way or another :smiley:

Liam

Thanks Liam
I am using a Dragino LPS8 indoor gateway in my workshop.
It is connected to the things network and my UNO is communicating with it and the TTN.

1 Like

TTNv3

1 Like
/*******************************************************************************

 * Copyright (c) 2015 Thomas Telkamp and Matthijs Kooijman

 * Copyright (c) 2018 Terry Moore, MCCI

 * Copyright (c) 2018 Betina Wendel and Thomas Laurenson

 *

 * Permission is hereby granted, free of charge, to anyone

 * obtaining a copy of this document and accompanying files,

 * to do whatever they want with them without any restriction,

 * including, but not limited to, copying, modification and redistribution.

 * NO WARRANTY OF ANY KIND IS PROVIDED.

 * 

 * Sketch Summary:

 * Target device: Dragino LoRa Shield (US900) with Arduino Uno

 * Target frequency: AU915 sub-band 2 (916.8 to 918.2 uplink)

 * Authentication mode: Over the Air Authentication (OTAA)

 *

 * This example requires the following modification before upload:

 * 1) Enter a valid Application EUI (APPEUI)

 *    For example: { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };

 * 2) Enter a valid Device EUI (DEVEUI)

 *    For example: { 0x33, 0x22, 0x11, 0x11, 0x88, 0x88, 0x11, 0x22 };

 *    This is little endian format, so it is in reverse order that the server

 *    provides. In the example above, the original value was: 2211888811112233

 * 3) Enter a valid Application Key (APPKEY)

 *    For example: { 0xe4, 0x17, 0xd3, 0x3b, 0xef, 0xf3, 0x80, 0x7c, 0x7c, 0x6e, 0x42, 0x43, 0x56, 0x7c, 0x21, 0xa7 };

 * 

 * The DEVEUI and APPKEY values should be obtained from your LoRaWAN server

 *  (e.g., TTN or any private LoRa provider). APPEUI is set to zeroes as 

 * the LoRa Server project does not requre this value.

 *

 *******************************************************************************/

#include <lmic.h>

#include <hal/hal.h>

#include <SPI.h>

#include <string.h>

/************************************************************************************

*

* My Mods to get thr ultrasonic sensor to read the tank level and then send it to ttn

* 30/04/2022 greg O.

*************************************************************************************/

#include <SoftwareSerial.h>

SoftwareSerial mySerial(11,12); // RX, TX

unsigned char data[4]={};

unsigned char dat_to_send[4]={};

int           distance;

//

// For normal use, we require that you edit the sketch to replace FILLMEIN

// with values assigned by the TTN console. However, for regression tests,

// we want to be able to compile these scripts. The regression tests define

// COMPILE_REGRESSION_TEST, and in that case we define FILLMEIN to a non-

// working but innocuous value.

//

#ifdef COMPILE_REGRESSION_TEST

# define FILLMEIN 0

#else

# warning "You must replace the values marked FILLMEIN with real values from the TTN control panel!"

# define FILLMEIN (#dont edit this, edit the lines that use FILLMEIN)

#endif

// This EUI must be in little-endian format, so least-significant-byte

// first. When copying an EUI from ttnctl output, this means to reverse

// the bytes. For TTN issued EUIs the last bytes should be 0xD5, 0xB3,

// 0x70.

static const u1_t PROGMEM APPEUI[8]= {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

void os_getArtEui (u1_t* buf) { memcpy_P(buf, APPEUI, 8);}

// This should also be in little endian format, see above.

static const u1_t PROGMEM DEVEUI[8]= { Blagh Blagh};

void os_getDevEui (u1_t* buf) { memcpy_P(buf, DEVEUI, 8);}

// This key should be in big endian format (or, since it is not really a

// number but a block of memory, endianness does not really apply). In

// practice, a key taken from the TTN console can be copied as-is.

static const u1_t PROGMEM APPKEY[16] = { Blagh Blagh };

void os_getDevKey (u1_t* buf) {  memcpy_P(buf, APPKEY, 16);}

//static uint8_t mydata[]; // = "GDay Num Nuts";

static uint8_t payload[5];

static osjob_t sendjob;

// Schedule TX every this many seconds (might become longer due to duty

// cycle limitations).

const unsigned TX_INTERVAL = 60;

// Pin mapping for Dragino Lorashield

const lmic_pinmap lmic_pins = {

    .nss = 10,

    .rxtx = LMIC_UNUSED_PIN,

    .rst = 9,

    .dio = {2, 6, 7},

};

/*********************************************************

*

*  Read ultrasonic sensor data and format it.

*

**********************************************************/

int read_sonde()

{

    do{

     for(int i=0;i<4;i++)

     {

       data[i]=mySerial.read();

     }

  }while(mySerial.read()==0xff);

  mySerial.flush();

  if(data[0]==0xff)

    {

      int sum;

      sum=(data[0]+data[1]+data[2])&0x00FF;

      if(sum==data[3])

      {

        distance=(data[1]<<8)+data[2];

        if(distance>280)

          {

         // strncpy(mydata,distance,sizeof(distance));

           Serial.print("distance=");

           Serial.print(distance);

           Serial.println("mm");

          }else 

              {

                Serial.println("Below the lower limit");        

              }

      }else Serial.println("ERROR");

     }

    return(distance);

}

/* End */

void onEvent (ev_t ev) {

    Serial.print(os_getTime());

    Serial.print(": ");

    switch(ev) {

        case EV_SCAN_TIMEOUT:

            Serial.println(F("EV_SCAN_TIMEOUT"));

            break;

        case EV_BEACON_FOUND:

            Serial.println(F("EV_BEACON_FOUND"));

            break;

        case EV_BEACON_MISSED:

            Serial.println(F("EV_BEACON_MISSED"));

            break;

        case EV_BEACON_TRACKED:

            Serial.println(F("EV_BEACON_TRACKED"));

            break;

        case EV_JOINING:

            Serial.println(F("EV_JOINING"));

            break;

        case EV_JOINED:

            Serial.println(F("EV_JOINED"));

            {

              u4_t netid = 0;

              devaddr_t devaddr = 0;

              u1_t nwkKey[16];

              u1_t artKey[16];

              LMIC_getSessionKeys(&netid, &devaddr, nwkKey, artKey);

              Serial.print("netid: ");

              Serial.println(netid, DEC);

              Serial.print("devaddr: ");

              Serial.println(devaddr, HEX);

              Serial.print("artKey: ");

              for (int i=0; i<sizeof(artKey); ++i) {

                Serial.print(artKey[i], HEX);

              }

              Serial.println("");

              Serial.print("nwkKey: ");

              for (int i=0; i<sizeof(nwkKey); ++i) {

                Serial.print(nwkKey[i], HEX);

              }

              Serial.println("");

            }

      Serial.println(F("Successful OTAA Join..."));

            // Disable link check validation (automatically enabled

            // during join, but because slow data rates change max TX

          // size, we don't use it in this example.

            LMIC_setLinkCheckMode(0);

            break;

        /*

        || This event is defined but not used in the code. No

        || point in wasting codespace on it.

        ||

        || case EV_RFU1:

        ||     Serial.println(F("EV_RFU1"));

        ||     break;

        */

        case EV_JOIN_FAILED:

            Serial.println(F("EV_JOIN_FAILED"));

            break;

        case EV_REJOIN_FAILED:

            Serial.println(F("EV_REJOIN_FAILED"));

            break;

            break;

        case EV_TXCOMPLETE:

            Serial.println(F("EV_TXCOMPLETE (includes waiting for RX windows)"));

            if (LMIC.txrxFlags & TXRX_ACK)

              Serial.println(F("Received ack"));

            if (LMIC.dataLen) {

              Serial.print(F("Received "));

              Serial.print(LMIC.dataLen);

              Serial.println(F(" bytes of payload"));

            }

            // Schedule next transmission

            os_setTimedCallback(&sendjob, os_getTime()+sec2osticks(TX_INTERVAL), do_send);

            break;

        case EV_LOST_TSYNC:

            Serial.println(F("EV_LOST_TSYNC"));

            break;

        case EV_RESET:

            Serial.println(F("EV_RESET"));

            break;

        case EV_RXCOMPLETE:

            // data received in ping slot

            Serial.println(F("EV_RXCOMPLETE"));

            break;

        case EV_LINK_DEAD:

            Serial.println(F("EV_LINK_DEAD"));

            break;

        case EV_LINK_ALIVE:

            Serial.println(F("EV_LINK_ALIVE"));

            break;

        /*

        || This event is defined but not used in the code. No

        || point in wasting codespace on it.

        ||

        || case EV_SCAN_FOUND:

        ||    Serial.println(F("EV_SCAN_FOUND"));

        ||    break;

        */

        case EV_TXSTART:

            Serial.println(F("EV_TXSTART"));

            break;

        default:

            Serial.print(F("Unknown event: "));

            Serial.println((unsigned) ev);

            break;

    }

}

void do_send(osjob_t* j){

 

    // 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, mydata, sizeof(mydata)-1, 0);

        itoa(distance,dat_to_send,10); 

        Serial.print(distance);

        LMIC_setTxData2(1, dat_to_send, sizeof(dat_to_send)-1, 0);

        Serial.println(F("Packet queued"));

        Serial.print(F("Sending packet on frequency: "));

        Serial.println(LMIC.freq);

    }

    // Next TX is scheduled after TX_COMPLETE event.

}

void setup() {

  LMIC_setAdrMode(1);

  LMIC_setLinkCheckMode(1);

  

    Serial.begin(115200); /* For arduino debugging info */

    mySerial.begin(9600); /* For Ultrasonic sensor serial port */

    Serial.println(F("Starting"));

    #ifdef VCC_ENABLE

    // For Pinoccio Scout boards

    pinMode(VCC_ENABLE, OUTPUT);

    digitalWrite(VCC_ENABLE, HIGH);

    delay(1000);

    #endif

    #if defined(CFG_au921)

    Serial.println(F("Loading AU915/AU921 Configuration..."));

    #endif

    // LMIC init

    os_init();

    // Reset the MAC state. Session and pending data transfers will be discarded.

    LMIC_reset();

  

    LMIC_setDrTxpow(DR_SF7, 14);

    LMIC_selectSubBand(1);

    LMIC_setClockError(MAX_CLOCK_ERROR * 1 / 100);

    

    // Start job (sending automatically starts OTAA too)

    do_send(&sendjob);

}

void loop() {

    os_runloop_once();

}
1 Like

Hi Greg,

I havent read the code super in depth but notice you’ve left the EUI and APP ID in the code still, someone can use that to tap into your feed and send data, I’d remove your details ASAP

1 Like

Thanks Liam……done that….Im not that bright……sorry.

2 Likes

Hi Greg,

Its definitely stung me, and a big change from most projects where you can copy-paste your code.

1 Like

Hi Greg,

Nothing jumps out as being the culprit.

Just to confirm, can you see the node connect in TTS?

It’s not unusual that there’s a bit of delay, depending on the region you are using (AU915 or AS923 it can be routed through to a different country and then back to your browser (note though that AU915 is becoming the norm in Australia)).

Liam/

Thanks Liam
At least I am not as crazy as I thought.
The problem is in the data packet for sure as if I take out my data from the sensor and use the static data in the sketch example it works flawlessly .
I think maybe the serial fetch from the sensor is fouling up the RTOS calls that are sending the packet. I am too much of a newbie at the RTOS stuff so I may have to find another way.
Its amazing all the LoraWAN examples out there and not one using an ultrasonic sensor .
Might give it a go on a Pico and see how I go.
Thanks Heaps.
Greg

2 Likes

Looks like the A01NYUB/ dfrobot sensor has a really tight window and no handshaking so it is virtually useless for any real applications.
The LoraWAN RTOS polls randomly so it cannot meet the required <150ms polling cycle and therefore causes my program to fail.

Hi Greg,

Sorry to hear that the sensor hasn’t worked out.

I have some basic experience in FreeRTOS in which you are able to schedule tasks based on the onboard timers, but it might be a bit of a jump.

If you do decide to pivot into using the Pico there are some great tutorials on multithreading using the two cores.

Let us know how you go, if you run into any roadblocks let us know and we’ll try and send through some helpful resources!
Liam

Thanks Liam
Sounds interesting is there a Lorawan board available for the pico?

1 Like

Hi Greg,

I stumbled across this module, not sure if the documentation is great, it has docs for TTS so relatively up to date and might be worth a test.

1 Like