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();
}
// ...
}
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");
}
}
}