Raspberry Pi Pico with LoRaWAN and The Things Network - Makerverse LoRa-E5 Module - Transmit Weather Data into IoT

I have just shared content in Guides > Wireless “Raspberry Pi Pico with LoRaWAN and The Things Network - Makerverse LoRa-E5 Module - Transmit Weather Data into IoT”





Read more

5 Likes

Thanks for the excellent guide Tim. I’m thinking about making a node with a Pi Zero rather than the Pico you used in your guide. Will a Pi Zero work with the Makerverse LoRa-E5 Breakout Module?

Regards,

Mark

1 Like

Hey Mark,

Cheers for your kind words!

Absolutely this module will work with the Raspberry Pi Zero, though not without reworking the example code. Currently, UART is driven by the machine module which will work with the Raspberry Pi Zero however as this product has so recently hit our shelves we don’t have an example code yet (Watch this Space though!).

Kind regards,

Tim

Thanks Tim! Looking forward to the example code …

Regards,

Mark

1 Like

I tried your setup for this project. I am in Canada. I did use the US setting for the transmit date python program. When I get it setup and run, I get the following. Any idea as to why the data length is an error…
LoRa radio is ready

JoinEUI: 80:00:00:00:00:00:00:06
DevEUI: 2C:F7:F1:20:32:30:21:6D
AppKey: B5C785AB8021BC3C924C9C6201079306

+DR: DR0
+DR: US915 DR0 SF10 BW125K

+JOIN: Start
+JOIN: NORMAL

+JOIN: Network joined
+JOIN: NetID 000013 DevAddr 26:0C:D6:88
+JOIN: Done

sending test messages
+MSGHEX: Length error 11

Hi @Randy225250, welcome to the forums.

Thanks for bringing this one to the forums - I couldn’t exhaustively test this device with other regional settings (since that would be illegal :open_mouth: ) so it’s great to hear from somebody across the pond.

You may want to refer to the resources on the product page, specifically the AT-Command documentation.

Error code 11 implies the command is in the wrong format somehow. Confusing since I wouldn’t intuitively expect that changing the regional settings would affect the AT command syntax at all.

I’ll investigate to make sure a bug didn’t make its way into the tutorial code.

For a sanity check, can you post your full code listing? Remove the API keys if you’d prefer to keep them secret.

1 Like

here you go. its essentially the code from the download…LoRa_transmit_data.py


# Connect to The Things Network (TTN) and publish some test data
#
# How to get started:
#   • Create an account on cloud.thethings.network
#   • Create an application
#   • Run this script to get the JoinEUI and DeviceEUI
#   • On your applicaiton, generate an AppKey and paste it as a string in the code below.
#   • Run this script again
#   • Monitor your applicaiton console and look for a test message being received on TTN
#
# This demo is based off the Getting Started Guide by seeedstudio:
# https://wiki.seeedstudio.com/LoRa-E5_STM32WLE5JC_Module/#getting-started
#
# Refer to the AT Command Specification
# https://files.seeedstudio.com/products/317990687/res/LoRa-E5%20AT%20Command%20Specification_V1.0%20.pdf




# Put your key here (string). This should match the AppKey generated by your application.
#For example: app_key = 'E08B834FB0866939FC94CDCC15D0A0BE'
app_key = 'B5C785AB8021BC3C924C9C6201079306'

#Placed My Generated AppKey Here
#app_key = ''


# Regional LoRaWAN settings. You may need to modify these depending on your region.
# If you are using AU915: Australia
#band='AU915'
#channels='8-15'

# If you are using US915
band='US915'
channels='8-15'
# 
# If you are using EU868
# band='EU868'
# channels='0-2'




from machine import UART, Pin
from utime import sleep_ms
from sys import exit

uart1 = UART(1, baudrate=9600, tx=Pin(4), rx=Pin(5))
join_EUI = None   # These are populated by this script
device_EUI = None

### Function Definitions

def receive_uart():
    '''Polls the uart until all data is dequeued'''
    rxData=bytes()
    while uart1.any()>0:
        rxData += uart1.read(1)
        sleep_ms(2)
    return rxData.decode('utf-8')

def send_AT(command):
    '''Wraps the "command" string with AT+ and \r\n'''
    buffer = 'AT' + command + '\r\n'
    uart1.write(buffer)
    sleep_ms(300)

def test_uart_connection():
    '''Checks for good UART connection by querying the LoRa-E5 module with a test command'''
    
    send_AT('') # empty at command will query status
    data = receive_uart()
    if data == '+AT: OK\r\n' : print('LoRa radio is ready\n')
    else:
        print('LoRa-E5 detected\n')
        exit()

def get_eui_from_radio():
    '''Reads both the DeviceEUI and JoinEUI from the device'''
    send_AT('+ID=DevEui')
    data = receive_uart()
    device_EUI = data.split()[2]

    send_AT('+ID=AppEui')
    data = receive_uart()
    join_EUI = data.split()[2]

    print(f'JoinEUI: {join_EUI}\n DevEUI: {device_EUI}')
    
def set_app_key(app_key):
    if app_key is None:
        print('\nGenerate an AppKey on cloud.thethings.network and enter it at the top of this script to proceed')
        exit()

    send_AT('+KEY=APPKEY,"' + app_key + '"')
    receive_uart()
    print(f' AppKey: {app_key}\n')


def configure_regional_settings(band=None, DR='0', channels=None):
    ''' Configure band and channel settings'''
    
    send_AT('+DR=' + band)
    send_AT('+DR=' + DR)
    send_AT('+CH=NUM,' + channels)
    send_AT('+MODE=LWOTAA')
    receive_uart() # flush
    
    send_AT('+DR')
    data = receive_uart()
    print(data)


def join_the_things_network():
    '''Connect to The Things Network. Exit on failure'''
    send_AT('+JOIN')
    data = receive_uart()
    
    print(data)

    status = 'not connected'
    while status == 'not connected':
        data = receive_uart()
        if len(data) > 0: print(data)
        if 'joined' in data.split():
            status = 'connected'
        if 'failed' in data.split():
            print('Join Failed')
            exit()
        
        sleep_ms(1000)
        
def send_message(message):
    '''Send a string message'''
    send_AT('+MSG="' + message + '"')

    done = False
    while not done:
        data = receive_uart()
        if 'Done' in data or 'ERROR' in data:
            done = True
        if len(data) > 0: print(data)
        sleep_ms(1000)
        
def send_hex(message):
    send_AT('+MSGHEX="' + message + '"')

    done = False
    while not done:
        data = receive_uart()
        if 'Done' in data or 'ERROR' in data:
            done = True
        if len(data) > 0: print(data)
        sleep_ms(1000)



##########################################################
#        
# The main program starts here
#
##########################################################

test_uart_connection()

get_eui_from_radio()

set_app_key(app_key)

configure_regional_settings(band=band, DR='0', channels=channels)

join_the_things_network()


# Send example data
print("sending test messages")
send_message("Hello World!")
send_hex("00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF")

any feedback?

Hi again @Randy225250 - apologies for the delay

My first step would be to isolate the two send_ commands and try to send just a single byte. eg. change:

# Send example data
print("sending test messages")
send_message("Hello World!")
send_hex("00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF")

to become

# Send simpler example data, and isolate to a specific function
print("sending test messages")
send_message("a")
# send_hex("01") # commented out to just test the first case

Let’s try paring the system back to be as simple as possible and only introduce complexity once we have a minimal functionality.

Hi Tim,

Thanks for the guide on LoRa and TTN.
I followed your instructions till the AppKey part. I copied and pasted my AppKey into the LoRa_transmit_data.py script. Then, I ran the script again. I got the following messages:

+DR: DR0
+DR: AU915 DR0 SF12 BW125K

+JOIN: Start
+JOIN: NORMAL

+JOIN: Join failed
+JOIN: Done

Join Failed

It appeared for some reason that I was not able to join the TTN. I am in Melbourne, and I think my area is covered by an TTN gateway. Could you please provide some insights/reasons on this LoRa TTN connection issue?

Best regards,
Rocky

1 Like

Just to close this one off, I found the problem. The north american US915 default DR (at least on my device) is DR=0 which has a payload length of 11. I changed the DR to DR=1 which has a payload length of 53, problem fixed

2 Likes

Hey Rocky,

You can double-check that you are covered using this map here - TTN Coverage - It looks like the image below. Make sure you are viewing The Things Network V3 Nodes on the map.

If you find you are on the edge of one of these nodes and not quite getting a succesfull join you can significantly boost the signal from your device by soldering on this RP-SMA Connector to the Antenna Pads on the bottom of the Makerverse Lora-E5 Breakout and screw in a larger Antenna like this one here.

Kindest regards,
Tim

Awesome detective work @Randy225250 - i’ll update the example code to use DR=1 :smiley:

1 Like

Hi Tim! Awesome project! Thank you for sharing it with us.

I ran into a big problem when i copied your script into thonny. I can run it in thonny without problems or any error messages - it works perfect!
But when i plug the pico into an 5V Power supply, the main.py isn´t executed correctly. It seems, that the script is stucking at the function definitions. All code above the function definitions is executed.

Here is my code: (see the line with “### STUCKS HERE!!” comment)

Iam sure that i made a stupid little mistake, but i have absolute no idea!

Iam using MicroPython v1.19.1-938-g0359aac10 on 2023-03-08 with an normal Pico

Thank you for your help

Greetings from Germany :wink:

from machine import UART, Pin, I2C
from utime import sleep_ms
from sys import exit
from machine_i2c_lcd import I2cLcd
import bme280_int as bme280

# Put your key here (string). This should match the AppKey generated by your application.
#For example: app_key = 'E08B834FB0866939FC94CDCC15D0A0BE'
app_key = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

# Regional LoRaWAN settings. You may need to modify these depending on your region.
band='EU868'
channels='0-2'

led = Pin(25, Pin.OUT)
led.on()
### UART init
uart1 = UART(1, baudrate=9600, tx=Pin(4), rx=Pin(5))
join_EUI = None   # These are populated by this script
device_EUI = None
sleep_ms(1000)

### BME280 init
i2c = machine.I2C(0, sda=machine.Pin(0), scl=machine.Pin(1))
bme = bme280.BME280(i2c=i2c)

# Initialisierung LCD über I2C
lcd = I2cLcd(i2c, 0x27, 2, 16)
lcd.putstr("booting...")  
sleep_ms(2000)

### Function Definitions
def receive_uart():  ### STUCKS HERE!!
    '''Polls the uart until all data is dequeued'''
    rxData=bytes()
    while uart1.any()>0:
        rxData += uart1.read(1)
        sleep_ms(2)
    return rxData.decode('utf-8')

def send_AT(command):
    '''Wraps the "command" string with AT+ and \r\n'''
    buffer = 'AT' + command + '\r\n'
    uart1.write(buffer)
    sleep_ms(300)

def test_uart_connection():
    '''Checks for good UART connection by querying the LoRa-E5 module with a test command'''
    send_AT('') # empty at command will query status
    data = receive_uart()
    if data == '+AT: OK\r\n' :
        print('LoRa radio is ready\n')
        lcd.clear()
        lcd.putstr("LoRa ready!")
        sleep_ms(2000)
    else:
        print('LoRa-E5 detected\n')
        exit()

def get_eui_from_radio():
    '''Reads both the DeviceEUI and JoinEUI from the device'''
    send_AT('+ID=DevEui')
    data = receive_uart()
    device_EUI = data.split()[2]
    lcd.clear()
    lcd.putstr("Dev_EUI...")
    sleep_ms(1000)
    lcd.clear()
    lcd.putstr(device_EUI)
    sleep_ms(1000)
    
    send_AT('+ID=AppEui')
    data = receive_uart()
    join_EUI = data.split()[2]
    lcd.clear()
    lcd.putstr("Join_EUI...")
    sleep_ms(1000)
    lcd.clear()
    lcd.putstr(join_EUI)
    sleep_ms(1000)    
    
def set_app_key(app_key):
    if app_key is None:
        print('\nGenerate an AppKey on cloud.thethings.network and enter it at the top of this script to proceed')
        exit()

    send_AT('+KEY=APPKEY,"' + app_key + '"')
    receive_uart()
    lcd.clear()
    lcd.putstr("App_Key...")
    sleep_ms(1000)
    lcd.clear()
    lcd.putstr(app_key)
    sleep_ms(1000)

def configure_regional_settings(band=None, DR='0', channels=None):
    ''' Configure band and channel settings'''
    
    send_AT('+DR=' + band)
    send_AT('+DR=' + DR)
    send_AT('+CH=NUM,' + channels)
    send_AT('+MODE=LWOTAA')
    receive_uart() # flush
    
    send_AT('+DR')
    data = receive_uart()
    print(data)
    lcd.putstr("ch. settings...")
    sleep_ms(1000)
    lcd.clear()
    lcd.putstr(data)
    sleep_ms(1000)

def join_the_things_network():
    '''Connect to The Things Network. Exit on failure'''
    send_AT('+JOIN')
    data = receive_uart()
    print(data)
    lcd.clear()
    lcd.putstr('connecting...')

    status = 'not connected'
    while status == 'not connected':
        data = receive_uart()
        if len(data) > 0: print(data)
        if 'joined' in data.split():
            status = 'connected'
            lcd.clear()
            lcd.putstr('connect: OK')
        if 'failed' in data.split():
            print('Join Failed')
            exit()
        
        sleep_ms(1000)
        
def send_message(message):
    '''Send a string message'''
    lcd.clear()
    lcd.putstr("sending...")
    send_AT('+MSG="' + message + '"')

    done = False
    while not done:
        data = receive_uart()
        if 'Done' in data or 'ERROR' in data:
            done = True
            lcd.clear()
            lcd.putstr('Message send: OK')
        if len(data) > 0:
            print(data)
            sleep_ms(1000)
        led.toggle()
        
def send_hex(message):
    send_AT('+MSGHEX="' + message + '"')

    done = False
    while not done:
        data = receive_uart()
        if 'Done' in data or 'ERROR' in data:
            done = True
        if len(data) > 0: print(data)
        sleep_ms(1000)



##########################################################
#        
# The main program starts here
#
##########################################################


test_uart_connection()

get_eui_from_radio()

set_app_key(app_key)

configure_regional_settings(band=band, DR='0', channels=channels)

join_the_things_network()

while True:
    ### reading data from BME280
    temperature, pressure, humidity = bme.values # Warmup Sensor
    sleep_ms(1000)
    temperature, pressure, humidity = bme.values # final measurement
   
    
    temperature_float = float(temperature)
    pressure_float = float(pressure)
    humidity_float = float(humidity)
    print(type(temperature))
    print(type(temperature_float))
    print(temperature)
    print(temperature_float)
    
    ## Create negative temp sign if necessary 
    if temperature_float >= 0:
        x = 1
    if temperature_float < 0:
        x = 2
    
    CleanTemperature_int = abs((int(temperature_float*100)))
    #print(CleanTemperature_int)
    CleanPressure_int = int((pressure_float*100))
    #print(CleanPressure_int)
    CleanHumidity_int = int(humidity_float*100)
    #print(CleanHumidity_int)
 
    ## add zeros to the values for every case of result (0999.99 hpa)
    ZeroedCleanTemperature_int = "{:04d}".format(int(CleanTemperature_int))
    print(ZeroedCleanTemperature_int)
    ZeroedCleanPressure_int = "{:06d}".format(int(CleanPressure_int))
    print(ZeroedCleanPressure_int)
    ZeroedCleanHumidity_int = "{:05d}".format(int(CleanHumidity_int))
    print(ZeroedCleanHumidity_int)
    
    ### get VSYS (Battery Voltage)
    pin = machine.ADC(29)
    adc_reading  = pin.read_u16()
    adc_voltage  = (adc_reading * 3.3) / 65535
    vsys_voltage = adc_voltage * 3
    vsys_voltage_rnd = round(vsys_voltage, 3)
    
    ### print payload valies first
    print("Temp indicator (x) --> 2= negative, 1= positive ", x)
    print("Temperature: ", temperature_float)
    print("Pressure: ", pressure_float)
    print("Humidity: ", humidity_float)
    print("Vsys: ", vsys_voltage_rnd)
    print(type(vsys_voltage_rnd))
      
    ### Building payload
    payload = str(x) + (ZeroedCleanTemperature_int) + (ZeroedCleanPressure_int) + (ZeroedCleanHumidity_int) + str(vsys_voltage_rnd)

    # Send data
    print("sending data...")
    send_message(payload)
    led.toggle()
    sleep_ms(30000)

Thanks Tim, good write up. I have a couple of comments that might assist others.

  1. When connecting the Rx and Tx leads to the MKR, I found they need to be connected to Rx and Tx not Rx1 and Tx1 as shown in your write up pics.
  2. I could not see all the TTN device registration details in your screenshot and so used RP001 Regional Parameters 1.0.2 and found I could Join the TTN OK but could not see any test messages or the confirmation in the TTN Device Live Data. I changed the Regional Parameters to RP001 Regional Parameters 1.0.2 revision B and then it was fine.
1 Like

If you use the RP-SMA Connector option(my preferred way to go every time as those u.FL connectors have a limited life) you need to relocate the zero ohm resistor that connects to the antenna or solder a wire link across the pads.

1 Like

Hi Tim!
An extremely clear and helpful tutorial! Thanks for putting it together.
I’m a mechanical engineer and I have been trying to get TTN LoRaWAN working at my farm for more than 2 years! I found the Radiohead and LMIC libraries are just too complicated and I could never get them to work with OTAA. I don’t know how many hundreds of dollars I spent trying with half a dozen different modules/boards with almost zero success.

I came across your tutorial on this new E5 module and the Core Electronics MKRvrs LoRa-E5 breakout board. When I saw it communicated via serial I ordered some test boards immediately! They work straight out of the box! This is a game changer! LoRaWAN has gone from being frustrating and expensive to simple to use and an extremely powerful tool.

For my application, the last part of the chain is the MQTT integration. From there you can get your data into something like Home Assistant. This might be another good tutorial to help people who aren’t that strong using TTN.

I wanted to thank you and the Core Electronics team for putting material like this together. You all deserve a round of applause. Now onto the fun part, sensors and actuators!

5 Likes

Hi Graham/Tim
I too would like to see this applied to something like Home Assistant using say MQTT / Node Red or similar instead of the TTN Gateway. For me this Gateway adds an unnecessary overhead and cost to the project. Interesting, I too am a Mechanical Engineer and are looking at using this on my rural property. My application would be to monitor the site while I’m away (incl security) and to manage multiple water supply tanks with the ability to pump water from a collector tank to a header tank some 30m up a hill. I would be interested in any comments related to the best way to achieve the same objective without using the TTN Gateway.

1 Like

Hi Tim,
Informative and practical project from you, as always.
I have embarked on a water tank pump control and level measuring system using three Pico W units (at various locations), ultrasonic detectors and/or level switches to measure tank levels all sending data to a Raspberry PI which does overall control and does further data coordination and will eventually publish the status of all pumps and tanks so I can view it on my Android phone (and perhaps even control it from the phone). My current problem is one of the Pico W is just outside of my home WiFi range, so I either need to run a long cable between the Pico W and level sensor (perhaps 20m which I feel will give poor results) to get the Pico W in range or run yet another WiFi repeater which would need to be powered over the Ethernet cable and made weather proof. When I read this article, I saw a great solution but then sadly found there is no TTN coverage in my area of Berry NSW.
Is there an alternative way to use LoRaWAN in a personal setup for just me? You have covered the sending part but I will need to set up my own receiving system and then extract the data.

You can check youtube, there are several published projects on LoRa, essentially peer to peer. I think in the download for the project here on pico, there is a peer to peer tx and receive sample code.