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:


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


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.


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)).


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.


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!

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.


Now the fun begins.
I purchased about 10 of the Pico ones from you guys .
I have been using the Modified code from Sandeep Mistry that wave share supply.
The C++ SDK from Raspberry Pi is super flakey on windows so I bought a PI400 (slow) and in the end a MacBook Pro the MacBook is the best as every thing works reliably and there is a VSCode Pico OpenOCD plugin available so you can debug it. But I still have the original issue of not being able to get the data too the TTN.


Is there a working Python library for the wave share board for the TTN?

1 Like

Hi Greg,

Just to confirm are you able to see the gateway on TTS?

A couple of other things to check:

  • Have you updated the respective settings in config.h for your your gateway?
  • If you have an SDR do you see any transmission in the frequency you are broadcasting on?

Unfortunately Waveshare have only published Arduino examples for this HAT, there seems to be some discussion around the net, but no libraries


Hi Liam
Yes no problem with the TTN I have plenty of devices talking to it. (Arduino dfrobot lora).
The Pico talks to the TTN no problem with the Sample code from waveshare c++.
Just when I try to add the sensor data to the existing samples that work prior then they no longer work.

 * Copyright (c) 2021 Arm Limited and Contributors. All rights reserved.
 * SPDX-License-Identifier: BSD-3-Clause
 * This example uses OTAA to join the LoRaWAN network and then sends the 
 * internal temperature sensors value up as an uplink message periodically 
 * and the first byte of any uplink messages received controls the boards
 * built-in LED.

#include <stdio.h>
#include <string.h>

#include "hardware/adc.h"
#include "hardware/gpio.h"
#include "hardware/uart.h"

#include "pico/stdlib.h"
#include "pico/lorawan.h"
#include "tusb.h"

// edit with LoRaWAN Node Region and OTAA settings 
#include "config.h"

// pin configuration for SX12xx radio module
const struct lorawan_sx12xx_settings sx12xx_settings = {
    .spi = {
        .inst = spi1,
        .mosi = 11,
        .miso = 12,
        .sck  = 10,
        .nss  = 3
    .reset = 15,
    .busy = 2,
    // sx127x would use dio0 pin, and sx126x dont use it 
    // .dio0  = 7,
    .dio1  = 20

// OTAA settings
const struct lorawan_otaa_settings otaa_settings = {
    .device_eui   = LORAWAN_DEVICE_EUI,
    .app_eui      = LORAWAN_APP_EUI,
    .app_key      = LORAWAN_APP_KEY,
    .channel_mask = LORAWAN_CHANNEL_MASK

// The GPIO pins to which the sonar is wired
#define GPIO_ECHO 6
#define GPIO_TRIGGER 7

// variables for receiving data
int     receive_length = 0;
uint8_t receive_buffer[242];
uint8_t receive_port = 0;

// functions used in main
void    internal_temperature_init();
float   internal_temperature_get();
float   read_depth_sonar();

unsigned char data[4]={};

int main( void )
    // initialize stdio and wait for USB CDC connect
    while (!tud_cdc_connected()) {

    printf("Pico LoRaWAN - OTAA - Temperature + LED + Distance\n\n");

    // initialize the LED pin and internal temperature ADC
    gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT);


    // uncomment next line to enable debug
    // lorawan_debug(true);

    // initialize the LoRaWAN stack
    printf("Initilizating LoRaWAN ... ");
    if (lorawan_init_otaa(&sx12xx_settings, LORAWAN_REGION, &otaa_settings) < 0) {
        while (1) {
    } else {

    // Start the join process and wait
    printf("Joining LoRaWAN network ...");

    while (!lorawan_is_joined()) {
    printf(" joined successfully!\n");

    // loop forever
    while (1) {
        // get the internal temperature
        int8_t adc_temperature_byte = internal_temperature_get();
        int8_t depth_byte = read_depth_sonar();
        // send the internal temperature as a (signed) byte in an unconfirmed uplink message
        printf("sending internal temperature: %d 'C (0x%02x)... and Tank Depth: %d", adc_temperature_byte, adc_temperature_byte,depth_byte);
        if (lorawan_send_unconfirmed(&adc_temperature_byte + depth_byte, sizeof(adc_temperature_byte + depth_byte), 2) < 0) {
        } else {

        // wait for up to 60 seconds for an event
        if (lorawan_process_timeout_ms(60000) == 0) {
            // check if a downlink message was received
            receive_length = lorawan_receive(receive_buffer, sizeof(receive_buffer), &receive_port);
            if (receive_length > -1) {
                printf("received a %d byte message on port %d: ", receive_length, receive_port);

                for (int i = 0; i < receive_length; i++) {
                    printf("%02x", receive_buffer[i]);

                // the first byte of the received message controls the on board LED
                gpio_put(PICO_DEFAULT_LED_PIN, receive_buffer[0]);

    return 0;

void internal_temperature_init()

float internal_temperature_get()
    const float v_ref = 3.3;

    // select and read the ADC
    uint16_t adc_raw = adc_read();

    // convert the raw ADC value to a voltage
    float adc_voltage = adc_raw * v_ref / 4095.0f;

    // convert voltage to temperature, using the formula from 
    // section 4.9.4 in the RP2040 datasheet
    float adc_temperature = 27.0 - ((adc_voltage - 0.706) / 0.001721);

    return adc_temperature;
 * SPDX-License-Identifier: BSD-3-Clause

float read_depth_sonar() {
// Send an impulse trigger of 10us
gpio_put(GPIO_TRIGGER, 1);
gpio_put(GPIO_TRIGGER, 0);
// Read how long is the echo
uint32_t signaloff, signalon;
do {
signaloff = time_us_32();
} while (gpio_get(GPIO_ECHO) == 0);
do {
signalon = time_us_32();
} while (gpio_get(GPIO_ECHO) == 1);
// Actual echo duration in us
const float dt = signalon - signaloff;
// distance in meter:
// echo duration (us) x speed of sound (m/us) / 2 (round trip)
return dt * 0.000343 / 2.0;
1 Like

1 Like

Hi Liam
I have bought the Picos and WaveShare Lorawan shields from Core and have been playing around getting the A01NYUB to work but still cant manage a single read from the sensor only a constant stream .
I need to be able to wake up and do a read then send it to the TTN and go back to sleep for 1/2 a day. Here is working code that I have created to read but if you alter the delay to more than 300uS it completely fails.

#include <time.h>
#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/uart.h"
#include "hardware/irq.h"

unsigned char data[4];
/// \tag::uart_advanced[]

#define UART_ID uart0
#define BAUD_RATE 9600
#define DATA_BITS 8
#define STOP_BITS 1

// We are using pins 0 and 1, but see the GPIO function select table in the
// datasheet for information on which other pins can be used.
#define UART_TX_PIN 0
#define UART_RX_PIN 1
uint8_t ch=0;
static int chars_rxed = 0;
int16_t distance;  // The last measured distance
bool newData = false; // Whether new data is available from the sensor
uint8_t buffer[4];  // our buffer for storing data
uint8_t idx = 0;  // our idx into the storage buffer

// RX handler
void read_serial_sensor() {
    while (uart_is_readable(UART_ID)) {
    uint8_t c = uart_getc(UART_ID);
    // See if this is a header byte
    if (idx == 0 && c == 0xFF) {
      buffer[idx++] = c;
    // Two middle bytes can be anything
    else if ((idx == 1) || (idx == 2)) {
      buffer[idx++] = c;
    else if (idx == 3) {
      uint8_t sum = 0;
      sum = buffer[0] + buffer[1] + buffer[2];
      if (sum == c) {
        distance = ((uint16_t)buffer[1] << 8) | buffer[2];
        newData = true;
      idx = 0;
    if (newData) {
    printf("Distance: ");
    printf(" mm \n");
    newData = false;

int main() {
    // Set up our UART with a basic baud rate.
    uart_init(UART_ID, 9600);

    // Set the TX and RX pins by using the function select on the GPIO
    // Set datasheet for more information on function select
    gpio_set_function(UART_TX_PIN, GPIO_FUNC_UART);
    gpio_set_function(UART_RX_PIN, GPIO_FUNC_UART);

    // Actually, we want a different speed
    // The call will return the actual baud rate selected, which will be as close as
    // possible to that requested
    int __unused actual = uart_set_baudrate(UART_ID, BAUD_RATE);

    // Set UART flow control CTS/RTS, we don't want these, so turn them off
    uart_set_hw_flow(UART_ID, false, false);

    // Set our data format
    uart_set_format(UART_ID, DATA_BITS, STOP_BITS, PARITY);

    // Turn off FIFO's - we want to do this character by character
    uart_set_fifo_enabled(UART_ID, false);

    while (1){
      busy_wait_us(300);  // Change this and it falls apart.
1 Like

Just found these so it must be possible:


1 Like