I'm trying to receive a string from an Arduino to a Raspberry Pi via a Bluetooth socket connection (RFCOMM). I'm using an HC-06 Bluetooth module, an Arduino UNO R3, and an RPi 4 Model B. For the most part, everything is working well --except the first two data samples. This is what my output generally looks like when I view it from the RPi terminal:
0.
06,21,0.01,64
0.06,21,0.01,64
0.06,21,0.01,64
0.06,21,0.01,64
0.06,21,0.01,64
0.06,21,0.01,64
Is there any way I can get the first two lines to show the data properly? This data is a singular string and I chose my buffer to be 32 bits, so I don't think buffer size is the issue. I had to add time.delay(4) in my Py script or else every sample of the data would look messed up. I've tried adjusting the time delay relentlessly, but I can't seem to get the first two samples to display correctly. Below is a bit of my Python code:
while 1:
try:
received_data = bluetoothSocket.recv(24)
formatted_data = received_data.decode("utf-8")
print("Received Data: ", formatted_data)
time.sleep(4)
except KeyboardInterrupt:
print("keyboard interrupt detected")
break
bluetoothSocket.close()
2 Answers 2
You might want to try sending the data in a packet delimited by unique characters, e.g. { and }, similar to the JSON format.
The NMEA GPS standard uses $ and '\n' to delimit messages.
Here is an Arduino non-blocking C++ algorithm which builds upon Majenko's readline() example to read a packet. It returns true when incoming data has been accumulated into a valid packet. It should be fairly straightforward to convert it to Python.
void setup()
{
Serial.begin(115200);
Serial.println(F("\n\nSerial Packet Test\n"));
pinMode(LED_BUILTIN, OUTPUT);
}
void loop()
{
const static unsigned int INTERVAL = 200;
static unsigned long previous_timestamp = millis();
unsigned long current_timestamp = millis();
static bool led_state = false;
if (current_timestamp - previous_timestamp >= INTERVAL)
{
led_state = !led_state;
digitalWrite(LED_BUILTIN, led_state);
previous_timestamp += INTERVAL;
}
static char packet[100];
if (ReadPacket(Serial, packet, sizeof(packet)))
{
Serial.print(F("Received packet >"));
Serial.print(packet);
Serial.println(F("<"));
}
}
bool ReadPacket(Stream& stream, char *const packet, const unsigned int SIZE)
{
const char FIRST = '{';
const char LAST = '}';
static bool read_until_last = false;
static unsigned int i = 0;
char ch;
if (stream.available())
{
ch = stream.read();
if (ch == FIRST)
{
Serial.println(F("Received FIRST."));
i = 0;
packet[0] = ch;
read_until_last = true;
return false;
}
if (read_until_last)
{
i++;
if (i > SIZE - 2)
{
Serial.println(F("Buffer overrun. Resetting to look for next packet."));
i = 0;
read_until_last = false;
return false;
}
if (ch == LAST)
{
Serial.println(F("Received LAST."));
packet[i++] = ch;
packet[i] = 0;
read_until_last = false;
return true;
}
else
{
Serial.print(F("Received char >"));
Serial.print(ch);
Serial.println("<");
packet[i] = ch;
return false;
}
}
}
return false;
}
I don't know what the time.sleep(4) statement fixes, but you could try moving the to the top of the loop instead of the bottom, so it precedes the first read. At the bottom of the loop, it's first execution comes after the first read.
\nand use that as the delimiting factor of your communication instead of assuming 24 bytes.