Why is message received via Bluetooth sometimes being split

I am sending a message to my MIT app via Bluetooth using an Arduino and a HC-06.

Most of the time the message is received intact. Occasionally though the message is received in separate parts. The message is always delivered but sometimes its in multiple parts instead of one. I am saving each message to a list so when this happens it causes issues because the data in the list isn't consistent.

Any guidance or help on how to fix this would be greatly appreciated!

The message being sent to the MIT app is structured as follows. Where the first number is a counter that increases with each message and 100 is a value randomly generated between 100-200.

1|100|100|100.

Here is what this look like in my serial monitor and MIT app. In this test it was the 5th message that got split.

Serial monitor
image

MIT emulated app

Arduino code

// HC-05 is connected via hardware serial pins
#define HC05_RX_PIN 16  // Example: Connect HC-05 TX to GPIO16 (RX)
#define HC05_TX_PIN 17  // Example: Connect HC-05 RX to GPIO17 (TX)

HardwareSerial HC05(1);  // Use Serial1 for HC-05 (1 refers to UART1)

// Variables for received/Sending Bluetooth messages
String AppCommand = "";  // Buffer to hold the incoming message from Bluetooth
String MessageToPhone;   // Message to send to the phone

int HitTargetID = 0;

void setup() {
  Serial.begin(115200);                                    // For debugging
  HC05.begin(9600, SERIAL_8N1, HC05_RX_PIN, HC05_TX_PIN);  // HC-05 operates at 9600 baud rate

  Serial.println("HC-05 initialized. You can now pair it with your device!");
}

void loop() {

  int HitRangeFromTag = random(100, 200);

  if (Serial.available() > 0) {
    // Read the incoming byte
    char incomingByte = Serial.read();

    // Check if the received byte is '0'
    if (incomingByte == '0') {
      HitTargetID++;

      MessageToPhone = String(HitTargetID) + "|" + String(HitRangeFromTag) + "|" + String(HitRangeFromTag) + "|" + String(HitRangeFromTag);
      Serial.print("Forward message to Phone - ");
      Serial.println(MessageToPhone);
      HC05.println(MessageToPhone);
    }
    //Reset counter
    if (incomingByte == '1') {
      HitTargetID = 0;
      Serial.println("Counter is Cleared");
    }
  }
}

MIT App inventor blocks

When I use the bluetooth classic integrated into the ESP32, the messages are received as a single message. The problem is that I am already using ESP-NOW from the same dev board so I can't reliably use it for Bluetooth as well. Basically, I have to use the HC-06 for Bluetooth coms.

Only solution I can thing of is adding start and end tags to the messages that's being sent and then checking for those in the MIT app?

What happens when you don't include the "|"? Since the final three values are always of length 3, you can find the value of the counter, and then partition the string in the app.
If what you're encountering is unavoidable, it might be better to add a letter to the counter, to distinguish it from the random values. Continue adding to the same element if it doesn't contain the letter, or go to the next element if it does.

Be sure to use println() at the end of each message to send from the sending device, to signal end of message.

Only use print() in the middle of a message.

Be sure not to println() in the middle of a message, or you will break it into two short messages and mess up the item count after you split the message in AI2.

Do not rely on timing for this, which is unreliable.

In the AI2 Designer, set the Delimiter attribute of the BlueTooth Client component to 10 to recognize the End of Line character.
BlueToothClient1_Properties
Also, return data is not immediately available after sending a request,
you have to start a Clock Timer repeating and watch for its arrival in the Clock Timer event. The repeat rate of the Clock Timer should be faster than the transmission rate in the sending device, to not flood the AI2 buffers.

In your Clock Timer, you should check

  Is the BlueTooth Client still Connected?
  Is Bytes Available > 0?
     IF Bytes Available > 0 THEN
       set message var  to BT.ReceiveText(-1) 

This takes advantage of a special case in the ReceiveText block:

ReceiveText(numberOfBytes)
Receive text from the connected Bluetooth device. If numberOfBytes is less than 0, read until a delimiter byte value is received.

If you are sending multiple data values per message separated by | or comma, have your message split into a local or global variable for inspection before trying to select list items from it. Test if (length of list(split list result) >= expected list length) before doing any select list item operations, to avoid taking a long walk on a short pier. This bulletproofing is necessary in case your sending device sneaks in some commentary messages with the data values.

Some people send temperature and humidity in separate messages with distinctive prefixes like "t:" (for temperature) and "h:" (for humidity).
(That's YAML format.)

The AI2 Charts component can recognize these and graph them. See Bluetooth Client Polling Rate - #12 by ABG

To receive YAML format messages, test if the incoming message contains ':' . If true, split it at ':' into a list variable, and find the prefix in item 1 and the value in item 2.

...


-1 = receive all bytes available.

The Bluetooth data package is 20 bytes - if you exceed that, the data either gets split or lost.

The issue with the Arduino String library is that while easy to use, it is byte heavy. Use a Char string instead or send the values separately.

Although println(data) should be enough, in practice it is better to use print(data) followed by println().

Why are you sending the same value, 'HitRangeFromTag', 3 times?

1 Like

Thank you very much everyone for the assistance! Its really really appreciated!

Send the characters individually as @ChrisWard worked! I added some blocks that would identify an end character and assign the preceding characters to another variable as the message.

I started stress testing it a little and the only issue that I'm still having is that when I send the message rapidly e.g every 100ms for 55 cycles, eventually the messages stop being received. Here is my latest Arduino IDE code and AI2 blocks.

#define HC05_RX_PIN 16  // Example: Connect HC-05 TX to GPIO16 (RX)
#define HC05_TX_PIN 17  // Example: Connect HC-05 RX to GPIO17 (TX)
#define RX_BUFFER_SIZE 1024

HardwareSerial HC05(1);  // Use Serial1 for HC-05 (1 refers to UART1)

// Variables for received/sending Bluetooth messages
String AppCommand = "";  // Buffer to hold the incoming message from Bluetooth
String MessageToPhone;   // Message to send to the phone

int HitTargetID = 0;

// Variables to store timing and state
unsigned long previousMillis = 0;
const unsigned long interval = 100;  // Interval in milliseconds (300 ms)
int counterTotal = 55;
int counter = 0;
bool executeSendMessage = false;  // State to control the execution of SendMessage

void setup() {
  Serial.begin(115200);                                                           // For debugging
  HC05.begin(9600, SERIAL_8N1, HC05_RX_PIN, HC05_TX_PIN, false, RX_BUFFER_SIZE);  // HC-05 operates at 9600 baud rate

  Serial.println("HC-05 initialized. You can now pair it with your device!");
}

void SendMessage() {
  int HitRangeFromTag = random(100, 200);
  HitTargetID++;
  // Add start '<' and finish '>' characters around the message
  MessageToPhone = String(HitTargetID) + "|" + String(HitRangeFromTag) + "|" + String(HitRangeFromTag) + "|" + String(HitRangeFromTag) + ">";

  Serial.print("Forward message to Phone - ");
  Serial.println(MessageToPhone);

  // Send message character by character
  for (int i = 0; i < MessageToPhone.length(); i++) {
    HC05.print(MessageToPhone[i]);  // Send one character at a time
  }
}

void loop() {
  // Check for incoming serial data
  if (Serial.available() > 0) {
    // Read the incoming byte
    char incomingByte = Serial.read();

    // Reset counter
    if (incomingByte == '0') {
      HitTargetID = 0;
      counter = 0;  // Reset the counter
      executeSendMessage = false;  // Stop any ongoing execution
      Serial.println("Counter is Cleared");
    }

    // Execute SendMessage once immediately
    if (incomingByte == '1') {
      SendMessage();
    }

       if (incomingByte == '2') {
      counter = 0;  // Reset the counter
      executeSendMessage = true;  // Start executing SendMessage at intervals
      previousMillis = millis();  // Reset the timer
    }
  }

  // Handle the timed execution of SendMessage
  if (executeSendMessage) {
    unsigned long currentMillis = millis();

    // Check if 300 ms have passed since the last time the function was executed
    if (currentMillis - previousMillis >= interval) {
      // Save the current time to previousMillis
      previousMillis = currentMillis;

      // Call the function
      SendMessage();

      // Increment the counter
      counter++;

      if (counter >= counterTotal) {
        executeSendMessage = false;
      }
    }
  }
}

The reason that I have 3x of 'HitRangeFromTag' is because eventually i'll be sending that amount of data and instead of adding more random variables, I just reused that one for testing.

I am curious about the the suggestion of "numberOFBytes" to -1. I tried this and it completely froze up the MIT app on my phone?

Did you set the AI2 Bluetooth Delimiter to 10?
What is your Clock Timer period?

2 Likes

I had my clock timer for receiving Bluetooth data set to 200ms which was higher than the rate at which I was sending messages so I suppose the messages all got sent to quickly. Bringing the clock down to 50ms made a big difference and a lot fewer messages are getting lost.

Yes delimiter was set to 10. When I implement "numberOFBytes" to -1 it Freezes the first time I try send a message to the app.

Export and post your Project .aia file?

Here you go. file is attached.

It seems a combination of sending the characters individually and reducing the clock time to 50ms resolved things. This is in combination with delimiter = 0 and numberOFBytes = BytesAvailableToReceive.

Thanks again for all the help. You suggestion about the individual characters was very helpful!

TecTarget_v1r009_Exported.aia (10.0 KB)

Missing HC05.println() after the for loop.

4 Likes

Dear @Salmon, that's probably the reason why your phone gets stuck at the first receiving (the delimiter on Arduino side is missing).
Typically I send hundreths of characters, let's say also long strings, without missing them by using what @ChrisWard and @ABG have already said, that is:
the clock periond on AI2 side shall (read must) be at least the half of the transmission one on Arduino. I normally use 10 milliseconds even when on Arduino the transmission repetition period is far slower: if no characters are available, the clock procedure on AI2 exits immediately but, if Arduino transmits, the gap between the sending and the receiving is at maximum 10 milliseconds.. Be sure that the transmission from Arduino shall be made by using BT.print(); and only the last one shall be a BT.println();

3 Likes

Thanks for all the help, everyone! The above suggestions resolved the issue and everything is working as desired! Big thanks for all the assistance!!!

1 Like