Trouble with reading bluetooth bytes

Hello everyone! I'm currently working on a University project with an Arduino connected to the MIT App Inventor.
The Arduino is tracking Distance in front of the sensor, Temp and Humidity. All that data is clumped into one string separated by "|" and sent to and processed on the MIT App side. The problem I'm having right now is that updates to the text_box text aren't as reliable as I'd want them to be. After some further testing with simplifying the process to only one integer being sent to the MIT App that does not need processing, it seems that the text_box buffer is overflowing as seen in the screenshot below.

Simplified Test:
The 11 10 10 10 are all separate lines of Collision_Distance sent from the Arduino. It's intended to only be showing one value per Bluetooth read.

String to process test:
When sending the appended string all in one line however, the text updates completely freeze up or are very slow.

After looking into similar topics, I've set the Arduino delay per loop to 600ms while having the App Inventor clock speed set to 300ms. It hasn't showed any different results so far.
I've done these tests through the Companion App and Apk Build
Any help would be appreciated!

Hello Aj

Well, if you had searched the forum a bit more, you would have found the cause of the problem - "Bytes Available to Receive" is a flag stating there are bytes available to receive, not an indication of how many bytes to be received. Replace that block with a Math Block -1.

-1 says 'collect all bytes'.

Don't use a delay, use milliseconds passed.

Now, why are you displaying the values at 300 millisecond intervals? Is your App specifically designed to give the User a headache? The human eye cannot tolerate that rate of change, is it really necessary? :upside_down_face:

@ChrisWard thanks for taking a look at my problem!

It is important for updates to be frequent because the data received is used to throw an audio alert at the user as soon as the distance is below a certain threshold. Something I could do is receive data frequently enough for alert purposes but not necessarily update the text field as often if that helps with performance and user convenience as you mentioned.

I've also updated my block to include the -1 but I wasn't seeing any results until I also set the Bluetooth client's DelimiterBytes to Math Block 10. The updates have definitely shot up in reliability but I'm still getting instances of "overflow of receivables" every now and then as shown in the screenshot below.

I've since changed my arduino code from delay to the suggested millis() instead and upped the arduino timer to 1.5secs and MIT App's timer to 1sec but the overflowing problem persists.

  unsigned long currentMillis = millis(); 
  if (millis() - previousMillis > 1500){
    previousMillis = currentMillis;

    Serial.println(Distance);
  }

This is definitely a step in the right direction and I appreciate your help Chris!

Yes! Is the User really going to be watching the screen all the time? An automatic alert, (including a sound?) - much more practical

I don't actually see that in the screenshot, but if you move your code away from "machine gun" value display to an alert system it may not matter. However, the App time Interval should be about 20% faster than the Sketch, so 1200 milliseconds required but no faster.

In the Sketch, is Distance an individual value or a concatenated string of all values?

Not at all. There's definitely more leeway I can compromise on in favor of performance and rely more on audio alerts for quicker responses.

Regarding the rest, both screenshots with Distance at 997 and 777 are values sent out by the Arduino at 9\n 9\n 7\n and 7\n 7\n 7\n where the intended result would have been only a 9 to late update text to 7 or only a 7 in the second screenshot.

Distance in this simplified example is an individual value. I toned down the work the App has to do while I figure things out but the end result would be the concatenated string of Distance|Temp|Humidity|Battery to be later split into tokens and assigned to their respective text_boxes.

That's a good strategy. Currently, you are adding symbols to incoming values but that is unnecessary, they can be permanent;

Temp ºC: 25
Humidity %: 15
Battery %: 15

I see - strange. If you are using an actual TextBox, change that to a Label (as a component it does less work). That might make the difference. Same for all values.

Don't concatenate the values in the Sketch, send in sequence:

void loop()
{
	  if(millis() - lgUpdateTime > 2000) //Loop approx every 2 seconds
      {
           lgUpdateTime = millis();

               //To App via Bluetooth

               Serial.print(iDistance);
               Serial.print('|'); 
               Serial.print(iTemp);
               Serial.print('|');            //Value Seperator
               Serial.print(iHumidity);
               Serial.print('|'); 
               Serial.print(iBattery);
               Serial.println();             //This tells App "End of Data" = Ascii LineFeed Char Num 10
      }
}

That's a good idea, that'll definitely help with processing the string.

Oh I'm sorry, I misspoke when I said I was concatenating the string into each other. I was thinking about it from the MIT App side of things with how it would receive it.

I am sending it out like you mentioned. This is what I was playing with before switching to millis() and toning it down to only sending out Distance till I have the problem sorted.

  /*
  Serial.print(Distance);
  Serial.print("|");
  Serial.print(DHT11_Temperature);
  Serial.print("|");
  Serial.print(DHT11_Humidity);  
  Serial.print("|");
  Serial.println(Battery);
  delay(500);  
  */

I'll switch things to Label and report back.
And yea, it is a strange problem! I cannot figure out what could be causing such an issue and I haven't been able to find other threads with similar issues.

Something else I'd like to mention is the Delimiter block that I added with the Math Block -1.
After reading the reference documentation related to the function, am I misusing it?

ReceiveText(numberOfBytes)
Reads a number of bytes from the input stream and converts them to text.
If numberOfBytes is negative, read until a delimiter byte value is read.

And from another thread's comment:

In the AI2 Designer, set the Delimiter attribute of the BlueTooth Client component to 10 to recognize the End of Line character.

This is the block I'm currently using to receive Distance.

That is correct, though in the Sketch I like to leave the Serial.println() stand-alone. Should not make any difference but for reasons unknown, it sometimes does.

Do not set the delimiter byte (end of data) in the Timer (loop), set it on Screen Initialisation. Only enable the Timer when the App is ready to receive data.

Edit: When the App is closed, be sure to disable all Timers first.

Also, joining the input with a space or null does not help matters, and setting input to space or null does nothing - whatever the value is, it is overwritten by the next packet.

With the changes you suggested made, this is the sketch I've got right now. I'll include a screenshot of the full sketch in hopes you may be able to spot something weird in what I've done. Again, thank you for making the time to help me out here!

I've added turned off the Clock Timer by default and added a switch to turn it on when Bluetooth is connected. I've added the Delimiter to 10 onto the Screen Initiate rather than on the loop, that was an oversight on my part.

The red arrows indicate which Block is responsible for moving onto the alternative "screen". I've read that adding screens can be very taxing on memory and that simply making things visible/not visible is a more effective approach to faking a second screen. Home_Button and Options_Button Blocks are messy and are using an earlier implementation of making things visible and not, before I added everything into one Layout and worked on that instead like I did with the Settings_Button.

That's good to know! I've edit as suggested. I've also since removed the join with space block you mentioned, it was the placeholder for adding "m" for meters onto the value but I've moved away from that and added the units as a static text with the Labels to the left.

2114 is what MIT App's reading and adding onto the Label appropriate to Distance. However, from Arduino's side this is what we have and we're expecting to read 21 then for 14 to replace 21 on the next read.

Arduino Serial Monitor:

19:30:56.393 -> 21
19:30:57.882 -> 14

Hi Aj - did you change the TextBoxes to Label components?

Can't read anything on your large image :grin: Right-mouse in the Blocks work area and select "Download Blocks as image" (Don't edit it!)

As for the Sketch, it will upload here if you change the extension to .txt

Adding Screens adds to complexity if they are to share data and yes each Screen has an allocation of memory. A fake or Virtual Screen though is the way to go if an extra screen is necessary. You may have noticed that many commercial Apps use a (long) Scrollable screen in favour of multiple screens, rather like a webpage.

However, 'living' on one screen with components being hidden/displayed is not good design - imagine if that were the case on a car dashboard or aircraft instrument panel - confusion.

Why are you sending units to Arduino?

Yea, TextBoxes have been switched to Labels where updates are being made. I haven't gotten to changing the static TextBoxes that don't receive any updates.

I'm sending a character back to the Arduino on button press to let the Arduino know to convert the data's units to the user's preference before sending it out to the App from now on.
That way the bulk of the work stays on the Arduino instead of adding more work to the App. (meters <-> inches, Celsius <-> Fahrenheit)

I'll upload an image of the sketch, add the .aia file and show the updated logic to sending out data from the Arduino if that helps in any way!

This is how I'm sending data out right now.
Distance every 1.5secs, Temp|Humidity|Baterry every 20seconds.

  if (millis() - previousMillis > 1500){      //run every 1.5seconds
    previousMillis = millis();
    
    Serial.println(Distance);

    if (millis() - previousMillis1 > 20000){   //run every 20seconds
      previousMillis1 = millis();
      
      //temp|humidity|battery
      dht.temperature().getEvent(&event);
      Serial.print(int(event.temperature));
      Serial.print("|");
      dht.humidity().getEvent(&event);
      Serial.print(int(event.relative_humidity));  
      Serial.print("|");
      Serial.println(Battery);
    
    } 
  }

As for identifying when the App knows if it's receiving Distance or the Temp/Humidity/Battery block, I've been experimenting with checking if the input contains "|" and act appropriately from there.

Hope this helps and hope you're having a good day Chris!


Android_App_copy.aia (204.3 KB)

The App already has to do more work in sending this info to Arduino. If the User wants to switch units, just do that in-App. Since it is unlikely that the User will want to continually change units, why not have that up-front when the App is first opened, store in TinyDb for next time? If the Arduino is not using the Units selected (btw, the near-equivalent of meters is feet), conversion on receiving should be more than fine.

The code will not work to any advantage, it just complicates the App side. Better to send all four values together in one packet, even if three of them needn't be so frequent. Keep it Simple. Remember what I said about Serial.println() too.

Sending the values should always be last and uninterrupted.