How can I parse Bluetooth serial data?

I'm sending data from an Arduino to an AI2 app in the format of each setting name is separated by an equals sign from each setting value and each set of name=value are separated by a pipe symbol delimiter. So "name=value|name2=value2|name3=value3".

I'd like to parse this in an AI2 procedure, first splitting the name/value sets with the pipe delimiter and then further splitting each single set by the equals delimiter and have conditional statements where if the local variable settingName matches the label name XXXX, then set the label XXXX to the value of the local variable settingValue.

Am I approaching this correctly with my procedure fn_SplitAndDisplay? I've been reading the documentation and trying to learn the syntax, but I can't get this parsing to work.

    currentTimeStamp = millis(); 
    if ((currentTimeStamp - btLastSendTimeStamp) > 2500)
    {
        BTSerial.println("lblTest1=Standard|lblTest2=Disabled");
        btLastSendTimeStamp = millis();
        Serial.println("Sending Bluetooth Data");
    }

Here is an updated blocks sample illustrating these ideas ...

BlueTooth_delimiter_sample.aia (3.4 KB) global message

(Change the split character to '|' to match yours)

I'm struggling to get this working.

If I understand the illustration correctly, you

  • instantiate the global variable message
  • instantiate a global list message_labels (is this a fixed sized array that has to be hard coded to match the number of labels to be updated?)
  • call the Bluetooth client when it has bytes available, until there are no more bytes and set message to that delimited string being sent by the Arduino
  • call the function split_and_display, sending it the unparsed string, the delimiter and the array of label names
  • the function split_and_display will instantiate array named items splitting the string variable message by the delimiter you pass to the function
  • a foreach loop is called (why start at 1 instead of 0?) (why go from 1 to min and not max?) (why 1 to both arrays, does it mean the it loops to whichever array.count value is lower?)
  • the loop sets the label[index].text to the item[index].text

Is that understanding accurate? I'm getting a Java error that says "the operation length of list cannot accept the arguments ..." and it shows the entire string I'm sending.

If I were writing this in c#, I would parse the list with something similar to what is below. Can I do this in AI2? The flexibility this offers would allow me to not have to hard code so many things and I could still process lines like arduinoTxLine2 and arduinoTxLine3.

string arduinoTxLine = "label1=value1|label2=value2|label3=value3";
string arduinoTxLine2 = "label1=value1|label2=value2|label4=value4|label6=value6";
string arduinoTxLine3 = "label1=value1|label4=value4|label3=value3|label7=value7|label2=value2|label6=value6|label5=value5";

string[] arduinoTxLineArr = arduinoTxLine.Split("|");
foreach (item in arduinoTxLineArr)
{
    string[] itemSplit = item.Split("=");
    string settingName = itemSplit[0];
    string settingValue = itemSplit[1];
    var labels = Controls.OfType<Label>;
    foreach (var label in labels)
    {
        if (settingName == label.Name)
        {
            label.Text = settingValue;
        }
    }
}

Yes.

This uses the special case of -1 bytes requested, which scans for the \n in the incoming BT buffer. If no \n yet, it will hang. If a \n found, it will grab what precedes it, but leave the rest in the buffer for the next time around. This factors out all timing considerations, as long as the receiving end can keep up with the sending end eventually. The objective of this is to get exactly one complete message, without receiving fragments or losing messages.

yes ...

AI2 lists start at item 1, not 0 like in some other languages.

The min() call is needed to prevent trying to select item numbers out of range in short lists, in both cases

  • incoming message has fewer parts than we have Labels
  • incoming message has more parts than we have Labels

There are alternative ways to handle this, like for a short incoming message blank out the Labels past the list length of the split message, instead of leaving them untouched. On the other hand, if temperature varies fast but humidity varies slowly, and you send temp,humidity once in a while but just temp occasionally, that would leave the humidity Label untouched since last update.

Right.

Pretty much. Hover your cursor over the BlueTooth.ReceiveText block to see its tool tip, for that -1 trick.

Show us. Maybe you used the wrong field delimiter (',' vs '|') ?

(Canned Reply: ABG - Download those blocks and post them here)

Please download and post each of those event block(s)/procedures here ...
P.S. These blocks can be dragged directly into your Blocks Editor workspace.

See Download Block Images for a demo.

Ok, here are my blocks. What about the psuedo c# code? Does AI2 have the ability to do that same thing?

Bluetooth has a message size limit of 20 bytes, from what I hear, so here is an individually labelled approach:

temperature and humidity on separate lines

If you want to send your temperature and humidity on separate lines, you could alternatively send tag:value pairs, like
T:98.6\n
H:100\n
which would arrive individually if you use Delimiter 10 (\n).
Your AI2 logic would look like

if BT.bytesAvailable > 0 then
  set local message to BT.ReadText(-1)   (to get only 1 line)
  if contains(message, ":") then
      set local splits to split message at ":"
      If (select item 1 of splits) = "T" then
         set LabelTemperature.Text to (select item 2 of splits)
      else if (select item 1 of splits) = "H" then
         set LabelHumidity.Text to (select item 2 of splits)
     else set LabelWhatHappenned to local message
end if

If you don't like the ELSEIF ladder, you could look up Label components in a dictionary based on text prefix.

This format also works well with the AI2 Charts component.

Regarding your blocks, those empty sockets in your Label list look like trouble.

I'll start with the elseif ladder for simplicity, am I getting close? Am I able to see the code behind the blocks?

0f4ab8f55542d6bca5cb8b45b383fb41ccf35887_2_690x484

The split block gives you a list.

Make a list would have added an extra list wrapper around that, which you don't want.

No.

Thank you, I've got this working well now.

Is there a tutorial on how to send this data to a second screen in the AI2 app, instead of the primary screen which is where I have It displayed now?

Switching screens breaks connections, so avoid that.

Use stacked vertical arrangements, one visible at a time.

TinyDB is available cross screen, if you don't mind the disconnect.

Ok, I do not want to drop the Bluetooth connection, so I will do that.

This is what you are referring to?

https://community.appinventor.mit.edu/t/overlap-view-extension-create-overlying-layout/27540/15

No.

See
https://ai2.appinventor.mit.edu/reference/other/manyscreens.html

That was actually surprisingly easy to implement and is working now, thanks!

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