LoRa Packet Transmission

I am trying to transmit packetized audio file that is stored locally on my pi through LoRa to another set. One set includes a pi that is connected to Arduino using UART connection (Tx, Rx and GND) and a Dragino LoRa shield is stacked on the Arduino itself (Hardware Serial Connection) However after packetizing on the pi side and sending it to my Arduino through UART, I am not receiving the packets in numerical order (Sequence Number). Any help would be appreciated. Thank you!

import wave
import serial
import time
from pydub import AudioSegment

# Constants
PAYLOAD_SIZE = 102
SERIAL_PORT = '/dev/serial0'
BAUD_RATE = 9600
PACKET_DELAY = 0.1 
MAX_RETRIES = 1  # Maximum number of retries for missing or out-of-sequence packets

# Read the .wav file
def read_wav_file(filename):
    wav = wave.open(filename, 'rb')
    sample_width = wav.getsampwidth()
    sample_rate = wav.getframerate()
    num_channels = wav.getnchannels()
    frames = wav.readframes(wav.getnframes())
    wav.close()
    return sample_width, sample_rate, num_channels, frames

# Compress the audio data using MP3 format
def compress_audio(data, sample_rate):
    audio = AudioSegment(
        data=data,
        sample_width=1,  # Assuming sample_width=2 for 16-bit audio
        frame_rate=sample_rate,
        channels=1  # Assuming mono audio
    )
    audio.export('compressed_audio.mp3', format='mp3', bitrate='3k')
    compressed_data = open('compressed_audio.mp3', 'rb').read()
    return compressed_data

def packetize_data(compressed_data):
    packetized_data = []
    num_packets = (len(compressed_data) + PAYLOAD_SIZE - 1) // PAYLOAD_SIZE
    for i in range(num_packets):
        packet_payload = compressed_data[i * PAYLOAD_SIZE: (i + 1) * PAYLOAD_SIZE]
        packet = packet_payload
        packetized_data.append(packet)
        print(f"Packet {i}: Payload={packet_payload}")  # Display the generated packet
    return packetized_data

def transmit_packets(packets):
    try:
        ser = serial.Serial(SERIAL_PORT, BAUD_RATE)
        print("Transmitting packets...")
        retries = 0
        for i, packet in enumerate(packets):
            index = i
            ser.write(index.to_bytes(1, byteorder='big'))  # Send the index
            ser.write(packet)
            time.sleep(PACKET_DELAY)
            retries = 0  # Reset the retry counter
            # Wait for acknowledgement
            while True:
                if ser.readable() and ser.in_waiting >= 1:
                    received_index = ord(ser.read())
                    if received_index == index:
                        print(f"Acknowledged packet: Index={received_index}")
                        break  # Acknowledgement received, move to the next packet
                elif retries < MAX_RETRIES:
                    print(f"Retransmitting packet: Index={i}")
                    ser.write(index.to_bytes(1, byteorder='big'))
                    ser.write(packet)
                    time.sleep(PACKET_DELAY)
                    retries += 1
                else:
                    print(f"Max retries reached for packet: Index={i}")
                    break  # Move to the next packet regardless of acknowledgement
        ser.close()
        print("Transmission complete")
    except serial.SerialException as e:
        print(f"Serial communication error: {str(e)}")
    except Exception as e:
        print(f"An error occurred during transmission: {str(e)}")




# Example usage
if __name__ == '__main__':
    # Read the .wav file
    filename = 'recorded_audio2.wav'
    sample_width, sample_rate, num_channels, frames = read_wav_file(filename)

    # Compress the audio data
    compressed_data = compress_audio(frames, sample_rate)

    # Packetize the compressed data
    packets = packetize_data(compressed_data)

    # Print the number of packets
    print("Number of packets to transmit:", len(packets))

    # Transmit packets serially to Arduino
    transmit_packets(packets)

Arduino Code:

#include <RH_RF95.h>
#include <SPI.h>

#define PAYLOAD_SIZE 102

RH_RF95 rf95;

void setup() {
  Serial.begin(9600);  // Initialize serial communication
  while (!Serial);  // Wait for the Serial connection to be established

  if (!rf95.init()) {
    Serial.println("LoRa initialization failed");
    while (1);
  }

  if (!rf95.setFrequency(915.0)) {
    Serial.println("Set frequency failed");
    while (1);
  }

  rf95.setTxPower(13);
}

void loop() {
  byte index =0;
  byte payload[PAYLOAD_SIZE];

  // Read the index and payload from the serial port
  if (Serial.available() >= 2) {
    index = Serial.read();
    payload[0] = Serial.read();
    for (int i = 1; i < PAYLOAD_SIZE; i++) {
      payload[i] = Serial.read();
    }

    // Display the received data
    Serial.print("Received packet from Raspi[");
    Serial.print(index);
    Serial.print("]: Payload=");
    for (int i = 0; i < PAYLOAD_SIZE; i++) {
      Serial.print(payload[i]);
    }
    Serial.println();

    // Transmit the received packet
    rf95.send(payload, PAYLOAD_SIZE);
    rf95.waitPacketSent();
  }
  
  // Check if there is any incoming packet
  if (rf95.available()) {
    // Read the received packet
    if (rf95.recv(payload, PAYLOAD_SIZE)) {
      // Display the received data
      Serial.print("Received packet from RF95: Payload=");
      for (int i = 0; i < PAYLOAD_SIZE; i++) {
        Serial.print(payload[i]);
      }
      Serial.println();
    } else {
      Serial.println("Receive failed");
    }
  }
}

You can implement a sequence number mechanism to ensure the correct order of packets.
I added some in your code:
On the transmitter:

import wave
import serial
import time
from pydub import AudioSegment

# Constants
# ...

def transmit_packets(packets):
    try:
        ser = serial.Serial(SERIAL_PORT, BAUD_RATE)
        print("Transmitting packets...")
        sequence_number = 0  # Initialize the sequence number
        for i, packet in enumerate(packets):
            sequence_number += 1  # Increment the sequence number
            index = i.to_bytes(1, byteorder='big')
            ser.write(index)  # Send the index
            ser.write(sequence_number.to_bytes(1, byteorder='big'))  # Send the sequence number
            ser.write(packet)
            # ...
            
            # Wait for acknowledgement
            while True:
                if ser.readable() and ser.in_waiting >= 2:  # Wait for index and sequence number
                    received_index = ord(ser.read())
                    received_sequence_number = ord(ser.read())
                    if received_index == i and received_sequence_number == sequence_number:
                        print(f"Acknowledged packet: Index={received_index}, Sequence Number={received_sequence_number}")
                        break  # Acknowledgement received, move to the next packet
                # ...
            
            # ...
    except serial.SerialException as e:
        # ...

# ...

on the receiver:

#include <RH_RF95.h>
#include <SPI.h>

#define PAYLOAD_SIZE 102

RH_RF95 rf95;

byte expected_index = 0;
byte expected_sequence_number = 0;

void loop() {
  byte index = Serial.read();
  byte sequence_number = Serial.read();
  byte payload[PAYLOAD_SIZE];

  // Verify the received index and sequence number
  if (index == expected_index && sequence_number == expected_sequence_number) {
    // Update the expected index and sequence number
    expected_index++;
    expected_sequence_number++;

    // ...

    // Transmit the received packet
    rf95.send(payload, PAYLOAD_SIZE);
    rf95.waitPacketSent();
  }
  
  // ...
}
1 Like

Hello thank you for the suggestion. After updating my code accordingly, nothing is showing up on my serial monitor now. Any idea why? Thank you

Pi Code:

import wave
import serial
import time
from pydub import AudioSegment

# Constants
PAYLOAD_SIZE = 102
SERIAL_PORT = '/dev/serial0'
BAUD_RATE = 9600
PACKET_DELAY = 0.1
MAX_RETRIES = 1  # Maximum number of retries for missing or out-of-sequence packets

# Read the .wav file
def read_wav_file(filename):
    wav = wave.open(filename, 'rb')
    sample_width = wav.getsampwidth()
    sample_rate = wav.getframerate()
    num_channels = wav.getnchannels()
    frames = wav.readframes(wav.getnframes())
    wav.close()
    return sample_width, sample_rate, num_channels, frames

# Compress the audio data using MP3 format
def compress_audio(data, sample_rate):
    audio = AudioSegment(
        data=data,
        sample_width=1,  # Assuming sample_width=2 for 16-bit audio
        frame_rate=sample_rate,
        channels=1  # Assuming mono audio
    )
    audio.export('compressed_audio.mp3', format='mp3', bitrate='3k')
    compressed_data = open('compressed_audio.mp3', 'rb').read()
    return compressed_data

def packetize_data(compressed_data):
    packetized_data = []
    num_packets = (len(compressed_data) + PAYLOAD_SIZE - 1) // PAYLOAD_SIZE
    for i in range(num_packets):
        packet_payload = compressed_data[i * PAYLOAD_SIZE: (i + 1) * PAYLOAD_SIZE]
        packet = packet_payload
        packetized_data.append(packet)
        print(f"Packet {i}: Payload={packet_payload}")  # Display the generated packet
    return packetized_data

def transmit_packets(packets):
    try:
        ser = serial.Serial(SERIAL_PORT, BAUD_RATE)
        print("Transmitting packets...")
        sequence_number = 0  # Initialize the sequence number
        for i, packet in enumerate(packets):
            sequence_number += 1  # Increment the sequence number
            index = i.to_bytes(1, byteorder='big')
            ser.write(index)  # Send the index
            ser.write(sequence_number.to_bytes(1, byteorder='big'))  # Send the sequence number
            ser.write(packet)
            # ...
            
            # Wait for acknowledgement
            while True:
                if ser.readable() and ser.in_waiting >= 2:  # Wait for index and sequence number
                    received_index = ord(ser.read())
                    received_sequence_number = ord(ser.read())
                    if received_index == i and received_sequence_number == sequence_number:
                        print(f"Acknowledged packet: Index={received_index}, Sequence Number={received_sequence_number}")
                        break  # Acknowledgement received, move to the next packet
                # ...
            
            # ...
        ser.close()
        print("Transmission complete")
    except serial.SerialException as e:
        print(f"Serial communication error: {str(e)}")
    except Exception as e:
        print(f"An error occurred during transmission: {str(e)}")

# Example usage
if __name__ == '__main__':
    # Read the .wav file
    filename = 'recorded_audio2.wav'
    sample_width, sample_rate, num_channels, frames = read_wav_file(filename)

    # Compress the audio data
    compressed_data = compress_audio(frames, sample_rate)

    # Packetize the compressed data
    packets = packetize_data(compressed_data)

    # Print the number of packets
    print("Number of packets to transmit:", len(packets))

    # Transmit packets serially to Arduino
    transmit_packets(packets)

Arduino Code:

#include <RH_RF95.h>
#include <SPI.h>

#define PAYLOAD_SIZE 102

RH_RF95 rf95;

byte expected_index = 0;
byte expected_sequence_number = 0;

void setup() {
  Serial.begin(9600);  // Initialize serial communication
  while (!Serial);  // Wait for the Serial connection to be established

  if (!rf95.init()) {
    Serial.println("LoRa initialization failed");
    while (1);
  }

  if (!rf95.setFrequency(915.0)) {
    Serial.println("Set frequency failed");
    while (1);
  }

  rf95.setTxPower(13);
}

void loop() {
  byte index = Serial.read();
  byte sequence_number = Serial.read();
  byte payload[PAYLOAD_SIZE];

  // Verify the received index and sequence number
  if (index == expected_index && sequence_number == expected_sequence_number) {
    // Update the expected index and sequence number
    expected_index++;
    expected_sequence_number++;
  }

  // Read the index and payload from the serial port
  if (Serial.available() >= 2) {
    index = Serial.read();
    payload[0] = Serial.read();
    for (int i = 1; i < PAYLOAD_SIZE; i++) {
      payload[i] = Serial.read();
    }

    // Display the received data
    Serial.print("Received packet from Raspi[");
    Serial.print(index);
    Serial.print("]: Payload=");
    for (int i = 0; i < PAYLOAD_SIZE; i++) {
      Serial.print(payload[i]);
    }
    Serial.println();

    // Transmit the received packet
    rf95.send(payload, PAYLOAD_SIZE);
    rf95.waitPacketSent();
  }
  
  // Check if there is any incoming packet
  if (rf95.available()) {
    // Read the received packet
    if (rf95.recv(payload, PAYLOAD_SIZE)) {
      // Display the received data
      Serial.print("Received packet from RF95: Payload=");
      for (int i = 0; i < PAYLOAD_SIZE; i++) {
        Serial.print(payload[i]);
      }
      Serial.println();
    } else {
      Serial.println("Receive failed");
    }
  }
}

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.