shafeen Ahmed
shafeen Ahmed

Reputation: 1

Is there a way to handle LoRaWAN bi-directional communication using python multi-threading?

So, basically, there are two devices (Elastel eg500, which is basically a Raspberry Pi 4). When I am using one device as sender and another as receiver, and using the code below, it is working just fine.

Here, the situation is, the sender will wait until it receives data from a radar device using RS-232 communication. When the sender receives the data, it will flash its own LEDs and then send a packet to the receiver device via LoRa. So, the receiver is waiting to receive LoRa packets.

When it receives a packet, it flashes its own LEDs.

Sender code:

import threading
import time
import subprocess
import serial

radar_value = 0
flash_duration = 0
flash_lock = threading.Lock()

def radar_data():
    global radar_value
    global flash_duration
    try:
        ser = serial.Serial('/dev/ttyACM1', 115200, timeout=5)
        if ser.isOpen():
            print("Serial port is open.")
            print("Waiting for data...")
            while True:
                data = ser.readline()
                if data:
                    with flash_lock:
                        radar_value = 1
                        flash_duration = max(flash_duration, 10)
                    #print("Received:", data.decode().strip())
                    time.sleep(10)
                else:
                    with flash_lock:
                        radar_value = 0
                    #print("No data received.")
                time.sleep(1)  # Small delay to avoid flooding the output
    except serial.SerialException as e:
        print("Error opening serial port:", str(e))
    except KeyboardInterrupt:
        print("Interrupted by user.")
    finally:
        if 'ser' in locals() and ser.isOpen():
            ser.close()
            print("Serial port closed.")

def send_packet(data):
    command = ["sudo", "/usr/sbin/lora_send", "-f", "903.9", "-d", f"{{{data}}}"]
    try:
        result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True)
        #print(f"Packet sent: {data}, Result: {result.stdout.decode().strip()}")
    except subprocess.CalledProcessError as e:
        print(f"Failed to send data: {e}, Error: {e.stderr.decode().strip()}")

def write_do(device_number, value):
    """Execute the write_do command to control a device."""
    try:
        result = subprocess.run(["write_do", device_number, value], stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True)
        #print(f"Device {device_number} set to {value}, Result: {result.stdout.decode().strip()}")
    except subprocess.CalledProcessError as e:
        print(f"Command failed: {e}, Error: {e.stderr.decode().strip()}")
    except Exception as e:
        print(f"Error executing command: {str(e)}")

def monitor_radar():
    global radar_value
    global flash_duration
    last_state = 0
    while True:
        with flash_lock:
            current_state = radar_value
            if current_state != last_state:
                send_packet(current_state)
                if current_state == 1:
                    #print("Radar active packet sent")
                    flash_duration = max(flash_duration, 10)
                else:
                    print("Radar inactive packet sent")
                last_state = current_state
        time.sleep(1)
        #print(f"Radar value: {radar_value}, Flash duration: {flash_duration}")

def flash_lights():
    global flash_duration
    devices = ["0", "1", "2"]
    while True:
        with flash_lock:
            if flash_duration > 0:
                flash_duration -= 1
                for device in devices:
                    write_do(device, "1")
                time.sleep(0.5)
                for device in devices:
                    write_do(device, "0")
                time.sleep(0.5)
            else:
                time.sleep(1)
        #print(f"Flash duration: {flash_duration}")

if __name__ == "__main__":
    data_thread = threading.Thread(target=radar_data)
    monitor_thread = threading.Thread(target=monitor_radar)
    flash_thread = threading.Thread(target=flash_lights)

    data_thread.start()
    monitor_thread.start()
    flash_thread.start()

    data_thread.join()
    monitor_thread.join()
    flash_thread.join()

Receiver Code:

import subprocess
import threading
import time

flash_thread = None
stop_flashing = threading.Event()

def lora_receive():
    """Function to continuously receive LoRa data using a specific command."""
    command = ["sudo", "/usr/sbin/lora_receive", "-a", "904.3", "-b", "905.0", "&"]
    try:
        while True:
            result = subprocess.run(command, capture_output=True, text=True)
            if result.returncode == 0:
                data = result.stdout.strip()
                #print(f"LoRa receive data: {data}")
                process_packet_data(data)
            else:
                print(f"Failed to receive LoRa packet: {result.stderr}")
    except subprocess.CalledProcessError as e:
        print(f"Failed to receive LoRa packet: {e}")
    except Exception as e:
        print(f"Error during LoRa reception: {str(e)}")

def get_lora_data():
    """Function to repeatedly fetch LoRa data."""
    while True:
        try:
            result = subprocess.run(["get_lora_data"], capture_output=True, text=True)
            if result.returncode == 0:
                data = result.stdout.strip()
                #print(f"LoRa get data: {data}")
                process_packet_data(data)
            else:
                print(f"Failed to fetch LoRa data: {result.stderr}")
        except subprocess.CalledProcessError as e:
            print(f"Failed to fetch LoRa data: {e}")
        except Exception as e:
            print(f"Error during data fetching: {str(e)}")
        time.sleep(1)  # Adjust the sleep time as necessary

def process_packet_data(data):
    """Process the data received from LoRa and control lights accordingly."""
    global flash_thread
    #print(f"Processing packet data: {data}")
    if data == "{1}":
        if flash_thread is None or not flash_thread.is_alive():
            stop_flashing.clear()
            flash_thread = threading.Thread(target=flash_lights, args=(["0", "1", "2"],))
            flash_thread.start()
    elif data == "{0}":
        stop_flashing.set()
        control_lights(False)
        #print("Stopped flashing due to {0} packet.")

def control_lights(turn_on):
    """Control lights based on the received data."""
    devices = ["0", "1", "2"]
    if turn_on:
        flash_lights(devices)
    else:
        for device in devices:
            write_do(device, '0')

def flash_lights(devices):
    """Flash lights based on the received data."""
    #print("Starting flash_lights.")
    while not stop_flashing.is_set():
        for device in devices:
            write_do(device, "1")
        time.sleep(0.5)
        for device in devices:
            write_do(device, "0")
        time.sleep(0.5)
    #print("Stopped flashing lights.")

def write_do(device_number, value):
    """Execute the write_do command to control a device."""
    try:
        result = subprocess.run(["write_do", device_number, value], stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True)
        '''if result.returncode == 0:
            print(f"Device {device_number} set to {value}, Result: {result.stdout.decode().strip()}")
        else:
            print(f"Command failed: {result.stderr.decode().strip()}")'''
    except subprocess.CalledProcessError as e:
        print(f"Command failed: {e}")
    except Exception as e:
        print(f"Error executing command: {str(e)}")

if __name__ == "__main__":
    # Start threads for both LoRa data fetching methods
    thread1 = threading.Thread(target=lora_receive)
    thread2 = threading.Thread(target=get_lora_data)
    thread1.start()
    thread2.start()

    thread1.join()
    thread2.join()

The problem is,when I tried to make both devices bi-directional, that is both devices can send and receive, it is not working properly. The scenario is, both devices should wait for data from the radar via RS-232 as well as from other devices via LoRa. When I combined the code, it is not working. I think there is an issue with threading here.

I tried the code below. I expected it would work, but it didn't. It receives data via RS-232 and sends packet via LoRa, but the other device does not receive any packet via LoRa. Code is given below:|

import time
import subprocess
import serial
import json

# Global variables
radar_value = 0
flash_duration = 0
flash_lock = threading.Lock()
stop_flashing = threading.Event()
flash_thread = None

def load_config():
    global flash_duration
    try:
        with open('config.json', 'r') as f:
            config = json.load(f)
            flash_duration = config.get('flash_duration', 10)
            print(f"Config loaded: flash_duration={flash_duration}")
    except Exception as e:
        print(f"Failed to load config: {str(e)}")

def radar_data():
    global radar_value
    global flash_duration
    try:
        ser = serial.Serial('/dev/ttyACM1', 115200, timeout=5)
        if ser.isOpen():
            print("Serial port is open.")
            print("Waiting for data...")
            while True:
                data = ser.readline()
                if data:
                    with flash_lock:
                        radar_value = 1
                        flash_duration = max(flash_duration, 10)
                    print("Received from radar:", data.decode().strip())
                    time.sleep(10)
                else:
                    with flash_lock:
                        radar_value = 0
                    print("No data received from radar.")
                time.sleep(1)  # Small delay to avoid flooding the output
    except serial.SerialException as e:
        print("Error opening serial port:", str(e))
    except KeyboardInterrupt:
        print("Interrupted by user.")
    finally:
        if 'ser' in locals() and ser.isOpen():
            ser.close()
            print("Serial port closed.")

def send_packet(data):
    command = ["sudo", "/usr/sbin/lora_send", "-f", "903.9", "-d", f"{{{data}}}"]
    try:
        result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True)
        print(f"Packet sent: {data}, Result: {result.stdout.decode().strip()}")
    except subprocess.CalledProcessError as e:
        print(f"Failed to send data: {e}, Error: {e.stderr.decode().strip()}")

def write_do(device_number, value):
    """Execute the write_do command to control a device."""
    try:
        result = subprocess.run(["write_do", device_number, value], stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True)
        print(f"Device {device_number} set to {value}, Result: {result.stdout.decode().strip()}")
    except subprocess.CalledProcessError as e:
        print(f"Command failed: {e}, Error: {e.stderr.decode().strip()}")
    except Exception as e:
        print(f"Error executing command: {str(e)}")

def monitor_radar():
    global radar_value
    global flash_duration
    last_state = 0
    while True:
        with flash_lock:
            current_state = radar_value
            if current_state != last_state:
                send_packet(current_state)
                if current_state == 1:
                    print("Radar active packet sent")
                    flash_duration = max(flash_duration, 10)
                else:
                    print("Radar inactive packet sent")
                last_state = current_state
        time.sleep(1)
        print(f"Radar value: {radar_value}, Flash duration: {flash_duration}")

def flash_lights():
    global flash_duration
    devices = ["0", "1", "2"]
    while True:
        with flash_lock:
            if flash_duration > 0:
                flash_duration -= 1
                for device in devices:
                    write_do(device, "1")
                time.sleep(0.5)
                for device in devices:
                    write_do(device, "0")
                time.sleep(0.5)
            else:
                time.sleep(1)
        print(f"Flash duration: {flash_duration}")

def lora_receive():
    """Function to continuously receive LoRa data using a specific command."""
    command = ["sudo", "/usr/sbin/lora_receive", "-a", "904.3", "-b", "905.0", "&"]
    try:
        while True:
            result = subprocess.run(command, capture_output=True, text=True)
            if result.returncode == 0:
                data = result.stdout.strip()
                print(f"Received LoRa data: {data}")
                process_packet_data(data)
            else:
                print(f"Failed to receive LoRa packet: {result.stderr}")
    except subprocess.CalledProcessError as e:
        print(f"Failed to receive LoRa packet: {e}")
    except Exception as e:
        print(f"Error during LoRa reception: {str(e)}")

def get_lora_data():
    """Function to repeatedly fetch LoRa data."""
    while True:
        try:
            result = subprocess.run(["get_lora_data"], capture_output=True, text=True)
            if result.returncode == 0:
                data = result.stdout.strip()
                print(f"Fetched LoRa data: {data}")
                process_packet_data(data)
            else:
                print(f"Failed to fetch LoRa data: {result.stderr}")
        except subprocess.CalledProcessError as e:
            print(f"Failed to fetch LoRa data: {e}")
        except Exception as e:
            print(f"Error during data fetching: {str(e)}")
        time.sleep(1)  # Adjust the sleep time as necessary

def process_packet_data(data):
    """Process the data received from LoRa and control lights accordingly."""
    global flash_thread
    print(f"Processing packet data: {data}")
    if data == "{1}":
        if flash_thread is None or not flash_thread.is_alive():
            stop_flashing.clear()
            flash_thread = threading.Thread(target=flash_lights, args=(["0", "1", "2"],))
            flash_thread.start()
    elif data == "{0}":
        stop_flashing.set()
        control_lights(False)
        print("Stopped flashing due to {0} packet.")

def control_lights(turn_on):
    """Control lights based on the received data."""
    devices = ["0", "1", "2"]
    if turn_on:
        flash_lights(devices)
    else:
        for device in devices:
            write_do(device, '0')

if __name__ == "__main__":
    load_config()

    # Start threads for radar data, monitoring, flashing lights, and LoRa communication
    data_thread = threading.Thread(target=radar_data)
    monitor_thread = threading.Thread(target=monitor_radar)
    flash_thread = threading.Thread(target=flash_lights)
    lora_receive_thread = threading.Thread(target=lora_receive)
    get_lora_data_thread = threading.Thread(target=get_lora_data)

    data_thread.start()
    monitor_thread.start()
    flash_thread.start()
    lora_receive_thread.start()
    get_lora_data_thread.start()

    data_thread.join()
    monitor_thread.join()
    flash_thread.join()
    lora_receive_thread.join()
    get_lora_data_thread.join()

type here

Upvotes: 0

Views: 59

Answers (0)

Related Questions