Reading Mixed Serial If/Else ? ?

I've been trying to figure this out when I feeling like pulling my hair out for the last couple of months now. I'm using an Uno R3 and interfacing with this https://www.sparkfun.com/products/9555

When the shield is connected a couple of commands needs to be sent to set the shield to work properly. I need to send the commands ATI and verify the response before moving on. The response is the version of the chip"Elm327 V1.3". If the chip doesn't respond with the version and instead returns with a "?" I need to reset the board by sending the command "ATZ".

Once I have read the board version the next set of commands I need to send are "AT L1" and "AT SP2" . After each command is sent the board will respond in 1 of 2 ways. If it's accepted it will respond with "OK", if it's not accepted it will respond with a "?". If I get an "Ok" response I can move on to the next command, if not I need to reset the board.

The final command I need to send is to check the baud rate, I send the command "ST CBR" and it will respond with a 3 to 7 digit number reflecting the current baud rate. Depending on the value it responds with I may also need to change the baud rate, if I do need to change it I would send "STSBR 115200" and receive the response of "OK".

I already know people work around what I'm trying to do with delay but none of them actually address reading the response and sending the next command based on how the shield responded. People just use delay, assign values and assume it's going to work correctly.

What I've been trying to do is figure out a way to send the command, wait for a response and compare the value and then either send the next command or send the command to reset the shield if the command was not accepted. Since I'm dealing with letters, numbers and a symbol in varying lengths of responses I don't even know where to start. I've tried this in so many different methods all ending the same way, they either won't compile, start an endless loop or totally ignore what the response was. I've deleted every sketch I've tried previously and I'm starting fresh.

I've read numerous posts and articles over the last couple of months but none of them address a mixed string or a string of unknown length, if they did it was not something that was clearly given as an example. I'm a visual learner so with out a working example it doesn't help me get what I need out of it.

I would be VERY grateful is someone could provide with with an example of sending even just one command, reading the response back and then determine if it should send the next command or reset(Command is ATZ).

Thanks

Here is a non-blocking AT response check I coded recently:

boolean foundOK()
{
  const char targetString[] = {'O', 'K', '\r', '\n'};
  static byte index = 0;
  boolean found = false;

  while (Serial3.available() > 0)
  {
    c = Serial3.read();
    Serial.write(c);
    if (c == targetString[index])
    {
      index++;
      if (index >= sizeof(targetString))
      {
        index = 0;
        Serial.println("found \"OK<cr><lf>\" termination string");
        found = true;
      }
    }
  }
  return found;
}

Delta_G:
Have you looked at: Serial Input Basics Lots of examples in there.

Well that's a shame. What we do here is help you fix the code you wrote. Since you don't have any now, what you need to do is write one and tell us what the difference is between what you expect it to do and what it does and we will help you fix it.

I have looked over that several times before and they don't work in this situation. Example 5 is the only one that could possibly have been of any use but it expects the numbers and letters to come in a preset order, there is no way to alter start and end markers from the shield or to separate the values for parsing the response.

I'm not the 1st person to have this issue, you will find just about EVERY sketch written for any board like this uses the same format and this isn't how it should be done; if they intended to ignore the response from each command they would have simply omitted a reply with these commands.

 Serial.print("Reset elm");
  elm327.println("ATZ\r");
  delay(1000);
 
 elm327.println("ATZ\r");
  delay(1000);
 
 elm327.println("ATI\r");
  Serial.print("ATI");
  delay(1000);
  
  Serial.flush();
  elm327.flush();
//Wait for a little while before sending the reset command to the OBD-II-UART
  delay(100);
  //Reset the OBD-II-UART
  Serial1.print("ATI\r");
  //Wait for a bit before starting to send commands after the reset.
  delay(100);
  Serial1.print("AT H1\r");
  //Wait for a bit before starting to send commands after the reset.
  delay(100);
  Serial1.print("AT AL\r");
  //Wait for a bit before starting to send commands after the reset.
  delay(100);
  // turn echo off
  Serial1.print("ATE0\r");

aarg:
Here is a non-blocking AT response check I coded recently:

boolean foundOK()

{
 const char targetString[] = {'O', 'K', '\r', '\n'};
 static byte index = 0;
 boolean found = false;

while (Serial3.available() > 0)
 {
   c = Serial3.read();
   Serial.write(c);
   if (c == targetString[index])
   {
     index++;
     if (index >= sizeof(targetString))
     {
       index = 0;
       Serial.println("found "OK" termination string");
       found = true;
     }
   }
 }
 return found;
}

could you provide a bit more of the code I could look over to see how it worked with the rest of the sketch?

Here is some code I had written to show how to use a circular buffer to wait for a specific string or timeOut

You can test this code and it gives you 5 seconds to say ‘Hello’

#define ESPSEPRIAL Serial

// --------------------------------------
// waitForString wait max for duration ms whilst checking if endMarker string is received
// on the ESP Serial port returns a boolean stating if the marker was found
// --------------------------------------

boolean waitForString(const char * endMarker, unsigned long duration)
{
  int localBufferSize = strlen(endMarker); // démo Use of a circular buffer
  char localBuffer[localBufferSize];
  int index = 0;
  boolean endMarkerFound = false;
  unsigned long currentTime;

  memset(localBuffer, '\0', localBufferSize); // clear buffer

  currentTime = millis();
  while (millis() - currentTime <= duration) {
    if (ESPSEPRIAL.available() > 0) {
      if (index == localBufferSize) index = 0;
      localBuffer[index] = (uint8_t) ESPSEPRIAL.read();
      endMarkerFound = true;
      for (int i = 0; i < localBufferSize; i++) {
        if (localBuffer[(index + 1 + i) % localBufferSize] != endMarker[i]) {
          endMarkerFound = false;
          break;
        }
      }
      index++;
    }
    if (endMarkerFound) break;
  }
  return endMarkerFound;
}


void setup() {
  Serial.begin(115200);
}

void loop() {
  if (waitForString("Hello", 5000ul)) {
    Serial.println("Good Morning!");
  } else {
    Serial.println("be polite please!”);
  }
}

aarg:
Here is a non-blocking AT response check I coded recently:

boolean foundOK()

{
  const char targetString[] = {'O', 'K', '\r', '\n'};
  static byte index = 0;
  boolean found = false;

while (Serial3.available() > 0)
  {
    c = Serial3.read();
    Serial.write(c);
    if (c == targetString[index])
    {
      index++;
      if (index >= sizeof(targetString))
      {
        index = 0;
        Serial.println("found "OK" termination string");
        found = true;
      }
    }
  }
  return found;
}

This is « pretty weak code” :wink:

it probably does the job for AT commands in most cases but would say you got your correct response if I were to send something like ESP DEADLOCKED\r\n or IRON\rLIKE\n or ROLLNECK\r\n basically just needs to have the letters in the right order even if you have junk in between. If you want to be true to the ask, you need a buffer as long as what you are searching for

J-M-L:
Here is some code I had written to show how to use a circular buffer to wait for a specific string or timeOut

You can test this code and it gives you 5 seconds to say ‘Hello’

#define ESPSEPRIAL Serial

// --------------------------------------
// waitForString wait max for duration ms whilst checking if endMarker string is received
// on the ESP Serial port returns a boolean stating if the marker was found
// --------------------------------------

boolean waitForString(const char * endMarker, unsigned long duration)
{
 int localBufferSize = strlen(endMarker); // démo Use of a circular buffer
 char localBuffer[localBufferSize];
 int index = 0;
 boolean endMarkerFound = false;
 unsigned long currentTime;

memset(localBuffer, '\0', localBufferSize); // clear buffer

currentTime = millis();
 while (millis() - currentTime <= duration) {
   if (ESPSEPRIAL.available() > 0) {
     if (index == localBufferSize) index = 0;
     localBuffer[index] = (uint8_t) ESPSEPRIAL.read();
     endMarkerFound = true;
     for (int i = 0; i < localBufferSize; i++) {
       if (localBuffer[(index + 1 + i) % localBufferSize] != endMarker[i]) {
         endMarkerFound = false;
         break;
       }
     }
     index++;
   }
   if (endMarkerFound) break;
 }
 return endMarkerFound;
}

void setup() {
 Serial.begin(115200);
}

void loop() {
 if (waitForString("Hello", 5000ul)) {
   Serial.println("Good Morning!");
 } else {
   Serial.println("be polite please!”);
 }
}

This is a solid start and the most progress I have made in months. He's what I've done just to see how it progresses and that each line was working. I used your code and added this in Void loop.

void loop() {
   Serial.println("");// Clear the input field
   Serial.println("ATI");//Send the 1st command to get things rolling
         if (waitForString("Elm327 v1.3", 5000ul)) {Serial.println("AT AL"); } 
              //Using ANY part of this works, tested "Elm327 v1.3" -Works, "7 v.1" - Works, Elm326 calls the 
              //Else part seems to be working as intended
          else {Serial.println("ATZ"); }//ATZ is reset of the shield
          
          if (waitForString("OK", 5000ul)) {Serial.println("AT H1"); } 
                //Changed to AT H3(not a valid command) and it calls the Else block
           else {Serial.println("ATZ"); }

               if (waitForString("OK", 5000ul)) {Serial.println("AT MA"); } 
        else {Serial.println("ATZ"); }
         delay(10000);
}

No I should leave well enough alone here since this works but it still has a lot of room for improvement since technically I only need to reset the shield if the ATI command fails. I could create several functions and call them in order, if one fails then let the else block call the previous function to move back one step. I'm not all that well versed in the arduino code and have been doing a lot of android lately but I know from previous experience the methods I would use for Android don't work in arduino.

What would be the best way to separate these IF statements so I could step back one statement at a time if something were to not respond with the expected response? I feel this is important since my code in the void loop could call one of the blocks if it encountered an error. I would rather call the last of the IF,Else statements and let it work it's way though them in reverse rather then a total reset right off.

Well you need to decide what happens if a command does not work - is there another command to send or does the full thing needs reboot (can you control power supply to do a hardware reset).

In one project I created a function testing various ways of resetting my device, all the way to switching off the power of my serial device and then rebooting the arduino (in that last case the call to the function never returns and your full project reboots). Let’s say this function is called progressiveReset() and if it neeeds context to take action you can pass as parameter the last failed command to that function (may be can try again). If it’s too complicated just reboot the whole thing :slight_smile:

I also created a function wrapping the call to waitForString() by also adding a c-String command to issue

boolean runCommand(const char * command, const char * endMarker, unsigned long duration)
{
   ESPSerial.print(command); // using print to not add arbitrarily the \r\n at the end. caller responsibility
   if (! waitForString(endMarker, duration)) progressiveReset(command); // die here possibly
  return true; // if we came back then the progressiveReset resolved the situation 
}

Now all the complexity is hidden in the progressiveReset() function and the main loop can look simple as you know if you come back from the call it means things got sorted out

void loop() {
   Serial.println(); // Clean up Serial Console
   runCommand("ATI\r\n”, “Elm327 v1.3", 5000ul); // Send the 1st command to get things rolling
   runCommand("AT AL\r\n”, “OK\r\n", 5000ul);
   runCommand("AT H1\r\n”, “OK\r\n", 5000ul);
   runCommand("AT MA\r\n”, “OK\r\n", 5000ul);
}

Just an idea

PeteS160:
I have looked over that several times before and they don't work in this situation. Example 5 is the only one that could possibly have been of any use but it expects the numbers and letters to come in a preset order, there is no way to alter start and end markers from the shield or to separate the values for parsing the response.

The idea behind my tutorial is to give you ideas about how to receive data. It was never intended to provide complete solutions - people's requirements are too varied. In many cases it will be necessary to make significant modifications.

What is the end-marker sent from the device you are communicating with?
Does the device send a start-marker? if so what is it?

I would start by creating the ability to display the responses from the device on the Serial Monitor. When you can do that you will have information that will help you with the task of interpreting and dealing with the responses. Develop your program in small steps.

...R

J-M-L:
This is « pretty weak code” :wink:

it probably does the job for AT commands in most cases but would say you got your correct response if I were to send something like ESP DEADLOCKED\r\n or IRON\rLIKE\n or ROLLNECK\r\n basically just needs to have the letters in the right order even if you have junk in between. If you want to be true to the ask, you need a buffer as long as what you are searching for

Oops. Does this not fix the issue?:

boolean foundOK()
{
  const char targetString[] = {'O', 'K', '\r', '\n'};
  static byte index = 0;
  boolean found = false;

  while (Serial3.available() > 0)
  {
    c = Serial3.read();
    Serial.write(c);
    if (c == targetString[index])
    {
      index++;
      if (index >= sizeof(targetString))
      {
        index = 0;
        Serial.println("found \"OK<cr><lf>\" termination string");
        found = true;
      }
    }
    else
    {
      index = 0;
    }
  }
  return found;
}

aarg:
Oops. Does this not fix the issue?:

No :slight_smile:

try typing "OOK\r\n"

your code sees the first O, it's a match at index 0 so index goes to 1
then your code sees the second O, it's not a match so index goes back to 0
then your code sees the K, it's not a match as you are waiting for 'O' your index stays at 0 and you blew it :slight_smile:

Now before you say "oh i need to check against the first one again"... no, won't work. Consider you are waiting for $$$123 and I type $$$$$$$123

see the issue? you need a memory of what has been entered

PeteS160:
could you provide a bit more of the code I could look over to see how it worked with the rest of the sketch?

It's important to know whether your sketch needs or tries to be non-blocking before we get into that. Can the program "sit and twiddle its thumbs" while waiting for an AT response, or does it need to be servicing other tasks during that time?

This worked out pretty good, still working on making it into a function so I can have it move forward or backwards though the commands rather then resetting the board.

void loop() {
   Serial.println("");// Clear the input field
   Serial.println("ATI");//Send the 1st command to get things rolling
         if (waitForString("Elm327 v1.3", 5000ul)) {Serial.println("AT AL"); } 
              //Using ANY part of this works, tested "Elm327 v1.3" -Works, "7 v.1" - Works, Elm326 calls the 
              //Else part seems to be working as intended
          else {Serial.println("ATZ"); }//ATZ is reset of the shield
          
          if (waitForString("OK", 5000ul)) {Serial.println("AT H1"); } 
                //Changed to AT H3(not a valid command) and it calls the Else block
           else {Serial.println("ATZ"); }

               if (waitForString("OK", 5000ul)) {Serial.println("AT MA"); } 
        else {Serial.println("ATZ"); }
         delay(10000);
}

PeteS160:
This worked out pretty good,

Relying on time to gather specific characters is a recipe for failure. It will work fine until the moment when it doesn't work. What do you do then?

Your receiving code should not rely on good behaviour by the sending device.

...R

Robin

Let me disagree there in specific cases and this is one of those

Waiting for the OK from your device to go to the next step of setup is key, there is usually no point continuing or you need to have a degraded experience mode. How do you get there if you don’t have a specific timeout ? Wait forever and can’t try alternative way to come back from the failure ?

If you look at the suggestion the timeout is a max wait time if you get the OK before obviously the function returns and you can proceed

You could sure have a totally async way of handling this with a callback or active check in the loop so that your app can continue without waiting (state machine driven for example) but in most cases the app needs the resource configured in a certain way to work and this is handled in the setup

J-M-L:
How do you get there if you don’t have a specific timeout ?

If it is just a safety timeout then that may be fine. The waitForString() function was not in the OP's snippet.

But I would not like a function that just twiddles its thumbs until the timeout expires. I would prefer to have the timeout separate so that other stuff can continue during the waiting period.

...R

I have actually put this into setup, what I posted was in the loop since I was just testing it out. Prior to the board being configured the way I need nothing else can be done in this application so the wait time isn't an issue. Now after having moved this from void loop to void setup the commands actually started going out too fast and the shield wasn't even ready to get commands yet. I've had to add a delay in the beginning of setup since it was spitting the commands out and hitting the time out portion before the shield was even ready to accept commands sending it into an endless reset loop. Even with the delay added total time to load when I power on the arduino and shield is less then 2 seconds to have the shield fully configured and ready to use.

This is a far better option than what I have found in pretty much every other sketch I've looked at that just used delay between each line waiting long enough between commands that it should have accepted the command but never actually knowing if it was setup properly.

What I'm trying to do now is set the code up in a series of steps 1,2,3, 4 etc so that rather then resetting the board if one line fails I could redirect it to simply move back to the previous line. It starts up and sends commands 1,2,3 and errors on command 4, instead of starting over it's sent back to line 3 and resend's the command, if line line 3 also fails it's sent back to line 2 and then line 1. If line 1 failed it resets the board. While it may seem slow it would be safer to have the program never make it out of setup if the boards not configured correctly. So what I'm now trying to figure out how to do it and have it run some type of count for the number of times it tries to setup each line before just exiting into an empty loop that turns on a light to signal an issue with startup.

This is possible with a recursive approach and an array of commands to send / answers to expect but I’d say - in my humble opinion - that if you got to step 3 fine (got the OK answer) and step 4 fails, then you need to retry step 4, there are rarely needs to redo step 3, 2 or 1 if they have already been successful - and this actually might be counter productive. Repeating a few times a step that fails is ok to consider, hence the idea of the progressiveReset() function I mentioned in post #8 to which you pass the failed command. If after repeating a couple times it does not work, you are probably better off rebooting (either the slave device or the whole thing if it fails again - hence this idea of ‘progressive’)

Robin2:
If it is just a safety timeout then that may be fine. The waitForString() function was not in the OP's snippet.

But I would not like a function that just twiddles its thumbs until the timeout expires. I would prefer to have the timeout separate so that other stuff can continue during the waiting period.

...R

Yes - I agree, it’s case specific. I think in the OP’s case it is justified

Alright I'll work on getting this done. Thanks for all the help.