LoRa P2P Wireless Gate Alarm

Updated 12 April 2023

Introduction

My front gate is a long way from the house at around 300m. I don’t want people wandering around my property without knowing about it.  This project uses two Raspberry Pi Pico’s and two LoRa modules. One standard Pico is at the gate and the other is a wifi model which is at my house. When the gate is opened a micro switch is triggered and a text signal is sent via a LoRa radio signal to the house Pico. The house device plays a tone and writes to a log file. I decided to write the software using modular techniques so that any or all of it could be easily extracted for other projects or so that this project could be easily extended.

Project Goals

  • Detect when the Front Gate is opened and play a sound inside the house.
  • Create modular software that can be reused on other projects.

Requirements

  • The gate end is solar-powered as no power is available.
  • The budget is ~ $100.00 dollars AU
  • Log details of Gate Open events on the house device
  • Indicate if the connection between the house and the gate has timed out by an error LED on the house device.
  • Indicate the number of gate open events on the house device LED since the last reset
  • Log battery levels of the gate device on the house device
  • Indicate if the battery power level at the gate is low by a warning LED on the house device
  • Publish the house device log file via a mini web server on the house device
  • Set the Date Time on the house device via a WIFI connection to an SNTP time server
  • Set the Date Time on the gate device via a LoRa message from the house device
  • Encrypt / Decrypt messages sent between devices
  • Allow easy addition of extra devices and message fields

Possible Project Extensions

  • Water Tank Monitoring and Logging
  • Dam Level Monitoring and Logging
  • Wind Speed Monitoring and Logging
  • Rainfall Monitoring and Logging
  • Temperature Monitoring and Logging
  • Anything you can measure with a Pico

Each of these ideas could be implemented on a remote device, reported back and logged on the central unit.  Controls and adjustments could be implemented via the web interface. Some devices could fulfil multiple roles with messages easily passed back and forward to named devices and ignored on others.

Hardware

Main House


Pin Connections

  • LED connected to physical Pin 7(GPIO 5)  and physical pin 8 (Gnd)
  • Speaker connected physical Pin 17 (GPIO 13) and physical pin 18 (Gnd)

Parts

Main Gate

 

Pin Connections

  • Micro Switch connected to physical Pin 19(GPIO 14)  and physical pin 18 (Gnd)

Parts

Process

I started by sourcing all the different major components although I didn’t build the cases until I knew what the finished dimensions of the components would be.

Note: The LoRa modules came with an antenna, a small 600 mAh Lipo battery and a charging circuit to power and charge from the Pico USB port. This saved the need to make a whole lot of decisions.

When programming, I always break things down into manageable chunks. I found or constructed a demo code for each of the major functions. I installed each piece of the demo code in isolation to test and get that feature working. Then I debugged and adjusted the code to get the desired result. For example, how to play a sound or how to flash the LED in a pattern. I then incorporated each concept into the main program sharing code between modules where possible.

The tough one was the code for the LoRa modules. I lucked out and found a very detailed library that does exactly what I needed. Care needs to be taken here to make sure you meet the Radio Frequency requirements of your location.  Even though the little things in this example are set up as peer-to-peer (P2P) their range is amazing over clear ground. (many kilometres)

Range Test Results


350m     Success
1000m  Not Tested
2000m  Not Tested


Further than this around my house, the hills get in the way.  Long-range stuff needs to be in line of sight but unconfirmed reports indicate 3-15 km may be possible. Use the Ping Pong test found in the SX126 drivers folders to do a simple range test.  Run the example on Device 1 and the LED on Device 2 will toggle when the devices are in range.  The devices send the word ping and the reply is the word pong.

Software

The full code for this project can be downloaded from the GitHub repository.

Some of the bleeding edge versions of Micropython had some very odd problems connecting to the pico once my programs became complicated.  I believe the last stable version at the time of writing was working fine.

MicroPython v1.19.1 on 2022-06-18; Raspberry Pi Pico with RP2040

The most recent development version was also working.

Rp2-pico-20230209-unstable-v1.19.1-859-g41ed01f13.uf2

Micropython image downloads are available here https://micropython.org/download/rp2-pico/ 

Files Required

Common to both devices

Filename Description
Sx1262.py LoRa library (details below)
Sx126x.py LoRa library (details below)
_sx126x.py LoRa library (details below)
debug.py

Reusable debugging output code. Output is to a file on the pico and to the serial console.

secrets.py Contains Encryption/Decryption keys generated by  “GenerateEncryptionKeys.py” This file is provided but not required on either device. Run the program once and share the keys on both devices.
constants.py User-modifiable constants for configuration.
encryption.py Contains code specific to the encryption/decryption process.
loraMessage.py Contains code specific to the transfer of messages between devices.
mpyaes.py Contains the third-party implementation of the built-in python encryption library
debugCounter.txt Contains a counter for the debug unique identifier
debuglog.csv Debugging log file automatically created

House Device

Filename Description
main.py Renamed from mainHouse.py for the House Device to allow for automatic execution at startup
secretsHouse.py Contains SSID and Password for your wifi network
dateTime.py Formats date and time into your preferred format (used in debugging) currently configured for DD/MM/YYYY HH:MM:SS
ntpClientTZ.py

Third-party code to retrieve the time from the internet using SNTP protocol.

Blink.py Contains code specific to flashing a LED in a specific sequence to indicate gate open events or error messages
Subprocess.py This file contains the code that runs as a second thread on processor 2.
tone.py

Specific code that plays an audible tone on the house device speaker

webServer.py Code that displays a web page published on the local IP address (port 80). 

Gate Device

Filename Description
main.py Renamed from mainGate.py to allow execution on startup

Program Flow

The messages sent between the LoRa devices can be considered to be in plain text. Think of this like an old-style serial / teletype connection where a character is pushed in one end of the cable and it pops out the other end. The way this is performed doesn’t really matter to us, the application programmers.   The magic is done within the hardware, software drivers and the LoRa communication module detailed below.

The LoRa connection and configuration is based on an example found here.

https://github.com/ehong-tl/micropySX126X

Here is a link to my initial Core Electronics Forum Question discussing the LoRa configuration options and country requirements. Don’t forget there will likely be rules you need to follow in your country although the default settings seemed to comply for me in Australia.

Configuring a Pico and Lora P2P | Core Electronics Forum

To install, save the above “Required Files” onto each device, renaming mainHouse.py and mainGate.py to main.py on their destination device so they execute automatically at startup.

If you don’t know how to connect to your Pico and copy files there is a great example here.

How to Setup a Raspberry Pi Pico and Code with Thonny

To make the program code easier to modify and maintain I have decided to send the text messages from one device to the other using the standard JSON format. This forces things to be done in a standard way and multiple values can easily be encoded/decoded into a single text string.

The simple JSON format I have used looks like this.

{Name:Value,Name:Value} 

Importable built-in MicroPython modules exist for the JSON format so it makes sense to do it this way.

 

Startup

The first thing that mainGate.py does on startup is to send a message to mainHouse.py. This includes the Date and Time the gate Pico has stored. As this device had no battery backed time source this date time will be incorrect at power up. (It’s something like 01 Jan 2020). Every message sent between the two devices contains the sending device’s timeStamp. After this it waits for incoming messages and gate open events.  Currently the only messages the gate unit will receive are time updates.

When mainHouse.py starts up it connects to the local wifi access point and updates its internal clock from a NTP server on the internet using a standard protocol Simple Network Time Protocol (SNTP). This process is performed by a library named ntpClientTZ.py. This module was based on an example found here.

https://gist.github.com/aallan/581ecf4dc92cd53e3a415b7c33a1147c

The modified library can offset the time for your local time zone but it is not daylight savings aware.

When mainHouse.py receives a message from mainGate.py it checks the time stamp received in the message against its own system time and if the time is out by more than one minute a message is sent back to mainGate.py with the field “TimeUpdate:True” instructing the device to update it’s time with the time stamp contained in the message.

The gate device is solar powered with a battery backup. So a warning can be displayed at the house device if the voltage drops below a certain level. The gate device’s voltage is contained within every message sent to the house device. A warning state can be signaled on the house device LED.  The minimum voltage can be specified in constants.py.

Not that it’s a big deal but anyone within range of the LoRa modules could listen in on my gate open and close events without much trouble so I decided to include encryption for the json messages strings.  I found an example implementation of the inbuilt micropython ucryptolib here.  

https://github.com/iyassou/mpyaes 

In future I could think of other things to monitor and encryption as standard could be a good thing to have.

 

# Things to do (mainGate.py)

# ===============================================================

# Flash LED on gate if voltage is low.

# Publish log from mini web server on house device

# ===============================================================

 

Getting all the code to work was a major part of the project. I usually find the easiest way to get code working is to include debugging output within the code. I have a standard library to assist with this named debug.py. I usually leave the debugging code in place for the next guy and to assist in solving unforeseen problems and to understand how the code works. It can simply be disabled by setting the debug level to zero in constants.py. Debug output can be directed to the console and or a file.

 

Error Messages

Flash the LED to Indicate an error Long flash then Short flashe(s).

1 long, 1 short = Low voltage at mainGate.py (Less than value set in constants.py)

1 long, 2 short = Unable to connect to Wifi at mainHouse.py

1 long, 3 short = Unable to set system clock at mainHouse.py

1 long, 4 short = Unable to open socket for inbound web page connection

1 long, 5 short = Heartbeat message timed out

1 long, 6 short = Unable to contact NTP time server

How Debug Logging Works

 Each call to debug passes the following values

Field Number Description
1 Debug level that triggered the message
2 Sequential counter for the debug log
3 Name of the function that the debug message comes from
4 The actual debug message
5 A boolean to enable or disable logging the message to a file debugLog.csv

LED Message and Error Indicator

File Name: blink.py

The House Device has an LED connected to PIN 7(GPIO 5) and ground on pin 18

Several different status indications are sent as follows.

A “Gate Open”  event adds to a counter of short flashes indicating how many times the gate has been opened since the last reset.

 

Error messages start with a long flash and then several short flashes. Usually, error messages are fatal and require intervention.

Example Code

Here is some example code from blink.py so you can assess the readability of my coding style.

The full code is available in the GitHub repository.

https://github.com/pollardd/Lora-Gate-Alarm/

These functions blink the LED and look for a press of the button.  If these two things aren’t combined while the LED is flashing the button pressed could be missed.  The button is used to reset the gate open count indication.

from machine import Pin
import time
import constants
import counters
import debug

# Constants
DEBUG = constants.DEBUG
LOGTOFILE =  constants.LOGTOFILE
ENCRYPTION = constants.ENCRYPTION

button =  Pin(14, Pin.IN, Pin.PULL_UP)  # Physical Pin 19 Gnd = 13

def flash(ledPin, long, short):
    if(DEBUG >=2):
        debug.debug(DEBUG, "blink.flash()", " ", LOGTOFILE)

    led = Pin(ledPin, Pin.OUT)

    # Long
    count=0
    while(count < long):
        led.on()
        #print("LED ON")
        time.sleep(1.2)
        led.off()
        #print("LED OFF")
        time.sleep(0.75)
        count = count+1
    # Short
    count=0
    while(count < short):
        led.on()
        # time.sleep(0.5)
        checkButtonPress(.3)  # Check the button while holding the flash on
        led.off()
        # time.sleep(0.5)
        checkButtonPress(.3)  # Check the button while holding the flash off
        count = count+1

    #time.sleep(1.5)
    checkButtonPress(1.25)  # Check the button while waiting between flash groups


def checkButtonPress(seconds):
    # If Button pressed clear the flash count
    # This function also acts as a timer for the main program loop    
    if(DEBUG >=2):
        debug.debug(DEBUG, "blink.checkButtonPress()", " ", LOGTOFILE)

    while seconds>0:
        if(DEBUG >=3):
            debug.debug(DEBUG, "checkButtonPress(seconds)", "seconds="+str(seconds), LOGTOFILE)

        if(getButton()==1):
            if(DEBUG >=1):
                debug.debug(DEBUG, "checkButtonPress(seconds)    ", "Reset Count Button Pressed, openCount=0", LOGTOFILE)
                counters.openCount=0
        time.sleep(0.125)  
        seconds=seconds - 0.125

def getButton():
    # Invert button value so 1=button pressed and 0=not pressed
    if(DEBUG >=2):
        debug.debug(DEBUG, "blink.getButton()", " ", LOGTOFILE)
    buttonPressed= not button.value()
    if(DEBUG >=3):
        debug.debug(DEBUG, "getButton()    ", "buttonPressed="+ str(buttonPressed), LOGTOFILE)
    return buttonPressed

Message Format

Messages are sent between devices in standard text format to keep things consistent and make adding new features easier.  I have come up with the following fields to be encoded into json format which are then encrypted.

Field Name Data Description
Message Number Sequential number generated by the sending device. House Device starts at 1 and the Gate Device starts at 10,000
TimeStamp Time on the clock of the sending device
SrcDevice Name of the sending device
DstDevice Name of the target device
TimeUpdate Should the destination device update its clock to this time? (True or False)
GateOpen Is the gate currently open? (True or False)
Voltage Battery Voltage of the sending device as a percentage 100% being full.
Text Free Form Text. This can simply be a human-readable description or could also be used to implement additional functions.

Example Code

{
"MessageNumber":"1",
"SrcDevice":"MainGate",
"DstDevice":"MainHouse",
"TimeStamp":"(2023, 1, 14, 13, 49, 34, 5, 14)",
"TimeUpdate":"False",
"BatteryVoltage":"99.1443",
"GateOpen":"False",
"TextMessage":"StartUp Message"
}

Here is a picture taken from the house where the House device is positioned. You can see where I have marked the front gate approximately 350m away.

Have a question? Ask the Author of this guide today!

Please enter minimum 20 characters

Your comment will be posted (automatically) on our Support Forum which is publicly accessible. Don't enter private information, such as your phone number.

Expect a quick reply during business hours, many of us check-in over the weekend as well.

Comments


Loading...
Feedback

Please continue if you would like to leave feedback for any of these topics:

  • Website features/issues
  • Content errors/improvements
  • Missing products/categories
  • Product assignments to categories
  • Search results relevance

For all other inquiries (orders status, stock levels, etc), please contact our support team for quick assistance.

Note: click continue and a draft email will be opened to edit. If you don't have an email client on your device, then send a message via the chat icon on the bottom left of our website.

Makers love reviews as much as you do, please follow this link to review the products you have purchased.