Parsing a simple string

Hey Everyone, First post...
I'm fairly versed in Arduino coding which led me to MIT, great stuff! But... I'm stuck on extracting a string being sent via bluetooth to my app. Below is the code from the Arduino
indent preformatted text by 4 spaces
delay(5000);
float f = dht.readTemperature(true);
float h = dht.readHumidity();
Serial.print(f);
Serial.print(" ");
Serial.println(h);
indent preformatted text by 4 spaces
So far I'm able to pull the first part of the serial stream off and put it in a label using "Segment Text" start "1" and Length "2" (that is the temperature) the rest of the info, ie the (h) humidity seems to get left behind, dropped, or disappeared??? In other words after I segment the text to show the first piece of info, there doesn't seem to be any left, LOL. Do I need more than one bluetooth client? Do I need a separate timer? Am I using the right function "Segment Text"? The stream from the bluetooth looks just like this.. 74.54 56.33 The numbers obviously change but it's simply 4 digits, a space, and 4 more digits.

Hello Dar

I'm assuming you are using Classic Bluetooth (not BLE).

Your Arduino sends a BT package. Inside that is the payload - your data. For the data to be read correctly, the receiving App needs to know how the data is formatted. Two critical items:

  1. The End Of Data marker
  2. The Value Separator

I recommend using a bar char "|" as the value separator because it's highly unlikely to be included in a string and a human reading the string can instantly identify it as a seperator - and so can the App.

In your Arduino Sketch, output should look like this:

f = dht.readTemperature(true);
h = dht.readHumidity();
Serial.print(f);
Serial.print("|");
Serial.print(h);
Serial.println();

Define your variables outside of the loop. Use elapsed milliseconds to control the send data time interval - and make that time interval as large as is practical for the purpose.

In the App Blocks:

  1. Screen Initialize, Set the BT Delimiter Byte to '10' = \n, which is what the Arduino code is using.
  2. Split the received bytes (text) at the value separator ("|") into a List variable. In your case, the List will consist of two items, item 1 will be the temperature value and item 2 the humidity.
  3. Note that the Bytes receiving Block must be inside a Clock Timer Block whose interval is approx 20% faster than the Arduino Sketch Loop time interval. This prevents the memory buffer being exceeded - or put it another way, prevents the packets from being in a highway crash.
1 Like

Wow Chris you nailed it!
My arduino loop is set for 5000ms, and I just adjusted my MIT timer to 4000ms. I changed the delimiter byte to 10 and added the whole string to a global variable. From there into a list and I can call index 1 and index 2 and now they separate into their respective labels. Thanks!! I couldn't if figured that out on my own! LOL

Actually you seem to have dodged a common gotcha - the phone's location must be switched on, so you're on a winning streak.

You can split the data straight into a List:

This is the snip of what I ended up with. Not sure if this is sloppy code per se... But if yours is cleaner, I'd gladly make adjustments.

Using a Delay() in your loop stops everything. Your Arduino and sensors will appreciate the elapsed time method for interval control. Like this:

//ArduinoToApp.ino  02/03/2021 02:38:48

//Fake data stream to demo elapased milliseconds timing

//vars
unsigned long lgUpdateTime;

void setup()
{
               Serial.begin(9600);
               lgUpdateTime = millis();
}

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

                         if (Serial.connected())
                         {
                                   //Bluetooth to App
                                   Serial.print("Hello");
                                   Serial.print("|");
                                   Serial.print("World");
                                   Serial.print("|");
                                   Serial.println();        //This last line tells App "End of Data" = Ascii LineFeed Char Number 10
                         }
               }
}

I've seen mills used so often but I was always afraid to try, I think I'm in on this. BTW, the Temp, Hum is an extremely small portion of this project. It started with a way to monitor my methanol tank in the car. I built the whole system first with a small TFT screen, then upgraded to a Nextion touch screen, now it's turned into an app. I sincerely appreciate this community, so far the MIT community is great!

Great project!

Hello Chris,

I am hoping you can assist me as well when you have the time. I will probably start a new topic on this.
I have a somewhat similar problem. I am sending data from my Arduino to my application, but it looks as though the app is not receiving the data packages correctly, or I have not conditioned it to do so properly. For example I will send 127.9 to the app, and at first it will read the whole number, then when I send a different value to replace that one, it displays the data almost as though it is scrolling across the screen, .9 --> 7.9 ---> 27.9 ---> 127.9 --->.9 and then it just stays as .9 until I send in a different value. What I need is for the value to display fully every time.
NB, this is for a coin acceptor. So when the person enters a coin, the value of the coin is subtracted from CreditDue and then CreditDue is sent to the app to be displayed.

void CreditCounter(float CreditDue)
{
float CreditAmountStorage = 0;

while(CreditInserted < CreditDue)
{
  i=i+1;
  Serial.println(CreditDue);
  
  if (i >= 30 and impulsCount == 1){CreditInserted = CreditInserted + 0.2; impulsCount = 0; EEPROM.put(0, CreditInserted);}
  delayMicroseconds(2);
  
  if (i >= 30 and impulsCount == 2){CreditInserted = CreditInserted + 0.5; impulsCount = 0; EEPROM.put(0, CreditInserted);}
  delayMicroseconds(2);
  
  if (i >= 30 and impulsCount == 3){CreditInserted = CreditInserted + 1; impulsCount = 0; EEPROM.put(0, CreditInserted);}
  delayMicroseconds(2);
  
  if (i >= 30 and impulsCount == 4){CreditInserted = CreditInserted + 2; impulsCount = 0; EEPROM.put(0, CreditInserted);}
  delayMicroseconds(2);
  
  if (i >= 30 and impulsCount == 5){CreditInserted = CreditInserted + 5; impulsCount = 0;}
  delayMicroseconds(2);
  
  if(CreditInserted > 0){CreditDue = CreditDue - CreditInserted; CreditInserted = 0; break;}/*Reduces outstanding amount by amount inserted and 
  zeros the amount inserted variable to prevent the code from deducting the inserted amount with every cycle of the while loop.*/
  
}    

}

Hi Msindisi

Yes, this should be your own Topic. There is a lot going on in your Clock Timer Block. Firstly, "BytesAvailableToReceive" is just a true-false flag.

So the BT part of the code should look like this:

Also I notice that you have controls being hidden/displayed - I recommend avoiding that scenario, such behaviour in a GUI is confusing - you wouldn't want controls disappearing from your car dashboard whilst driving, right?

What does the "ConfirmScreen" actually do? Is it doing something that a Notifier cannot do? If so, could it not be a virtual screen instead?

That test only needs to be performed once, but what is it for? Rather than have a while loop, which is then a loop inside a loop, and a repeated delay, something like this:

//Switch example 16/10/2021 10:35:30

unsigned long lgUpdateTime;
float fCreditInserted;
float fCreditDue;
int iImpulsCount;
int iCount = 0;

void setup()
{
         lgUpdateTime = millis();
}

void loop()
{
         //Excute loop every 0.002 Milliseconds (= 2 Microseconds)

	     if((millis() - lgUpdateTime) > 0.002) 
         {
               lgUpdateTime = millis();

               iCount = iCount + 1;

               if(fCreditInserted < fCreditDue)
               {

                         if (iCount >= 30)
                         {
                              switch (impulsCount) {
                         
                                case 1:
                                         fCreditInserted = fCreditInserted + 0.2;
                                         break;
                         
                                case 2:
                                         fCreditInserted = fCreditInserted + 0.5;
                                         break;
                         
                                case 3:
                                         fCreditInserted = fCreditInserted + 1.0;
                                         break;
                         
                                case 4:
                                         fCreditInserted = fCreditInserted + 2.0;
                                         break;
                         
                                case 5:
                                         fCreditInserted = fCreditInserted + 5.0;                        
                                         break;
                              }
                         
                              iImpulsCount = 0; EEPROM.put(0, fCreditInserted);
                         }
               }
         }
}

It is considered bad to mix floats with integers so just ensure that the added values are floats.

1 Like

Hey Chris,

Thank you so much for your assistance. Legendary stuff as always.

So, I have made my Bluetooth blocks look exactly like the ones you have above, and the app is now displaying a stable value, however, the value does not change. When I insert the coin, the value changes on the serial monitor, but it doesn't update on the app. Do you know what might be causing this?

Thank you again.

I wanted to do this, but I wasn't sure it would work if it was not inside the void loop() function. My thinking was that the program would just run through this piece of code once and go back to the void loop() function.
I need this code to loop as long as the difference between the CreditDue and CreditInserted variables is 0. I am jot sure how to achieve this using millis().

....The Loop continues no matter what, but you can add:

                         if(fCreditDue > 0.00)

to determine if the remaining lines of code should be executed.

Well, I have only supplied a snippet as an improvement of your snippet, so I don't know why the data sent to the App is wrong.