BytesAvailableToReceive error

Hi,

I send to the app via the HC-05 module and an Atmel microprocessor, character strings composed as follows:

[IdCommandParameterChecksum]

Why does the app sometimes perform two readings, i.e. one only the first [ and the other all the rest of the string....?

[ 'first reading
IdCommandParameterChecksum] 'second reading

I'm not a bluetooth nor hardware guru but I'm thinking that the method ReceiveText(numberOfBytes) reads only the bytes currently available. So if the full message hasn't arrived yet (due to slight delay in serial transmission), it reads part of the message, like only [, and then on the next timer tick, it gets the rest.
So I think you should modify your logic to:

Clock.Timer {
  if (BluetoothClient1.IsConnected) {
    if (BluetoothClient1.BytesAvailableToReceive > 0) {
      let newText = BluetoothClient1.ReceiveText(BluetoothClient1.BytesAvailableToReceive)
      set global DataBuffer to (global DataBuffer + newText)

      while (DataBuffer contains "[" and "]") {
        let startIndex = position of "[" in DataBuffer
        let endIndex = position of "]" in DataBuffer

        if (startIndex < endIndex) {
          // Extract message content between [ and ]
          let fullMessage = segment(DataBuffer, startIndex + 1, endIndex - startIndex - 1)

          // Process message
          if (fullMessage contains "TNN") {
             // your logic here
          }

          // Remove processed message from buffer
          set global DataBuffer to segment(DataBuffer, endIndex + 1, length of DataBuffer - endIndex)
        } else {
          // If closing bracket is before opening one, discard everything before first "[" to resync
          set global DataBuffer to segment(DataBuffer, startIndex, length of DataBuffer - startIndex + 1)
        }
      }
    }
  } else {
    // Not connected
    call Notifier1.ShowAlert with message "Not connected"
  }
}

The → sign is initialize global variable <variableName> to ~ block.
And the ← sign is the set <variable> to ~ block.

I created this pseudoCode-style code assuming that your message format is: [TNN] or [GM1234] etc.

thanks for the kind reply...
I also think that was the problem...
could you kindly give me an example of how to modify the blocks... thanks.

Okay, can you give me a moment to create the blocks? Thanks.
Since I do not know the UI of the app, I will just create the part with the problem that you had.

sure thanks...

I've finished making the example project.
You can look at the pseudocode post above that I wrote earlier if you don't understand the code.
bluetoothExample.aia (3.7 KB)

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.

...

Hi,
I'm testing your solution...

a question but why did you put "while test do" and not "if then" ??

to see if more data packets have been received?

to avoid modifying the firmware can I also put the ascii code of ] in Delimiter Byte?

Can, yes.

Should, ?

I tried it works, but the ] doesn't insert it into the read string?

I would need to see the code to answer that.
I am on my phone

Yes, however, I did some tests, everything is ok and I confirm that the Delimiter Byte character does not read it..
basically this is the implementation of the proposal of "cnitll311" Thanks for your precious advice...

Dear Antonio,
I see that you've already solved with @ABG's and @cnitll311's advices, and that's fine.
What I would confirm is that effectively the terminator byte is not a part of the received string. When the BT clients procedure encounters it, it stops receiving and filling the buffer. For this reason we (as @ABG does in his BT FAQ's) always suggest to use characters that normally aren't a part of the string itself (typically nonprintable characters like "linefeed" = 10 or 0x0A or "carriage return" = 13 or 0x0D) .
Therefore if you want that the received string contains also the "]", you'd better change your firmware code by sending the strings terminating them with the Linefeed character: this is very simply achieved by using the Bluetooth.println("string") function.
In a nutshell: that's not "mandatory", of course, but i's a good programming practice.
Best, Ugo.

Yes, thats the purpose for the while block.
If it's malfunctioning or causes lagging, you can add the break blocks.

If you want the while loop to run once, then remove the whole while statement block and then insert it into another event block such as Screen1.Initialize.
That way the data reciever blocks will run in an interval and the while block can still process it.