Go Down

Topic: Serial.readBytesUntil() (Read 3126 times) previous topic - next topic

Delta_G

#15
Aug 06, 2017, 06:21 pm Last Edit: Aug 06, 2017, 06:21 pm by Delta_G
OK. Read it again. Still  haven't received any "advice...to make use of". Reply #3 is almost irrelevant at Robin2 doesn't like serialEvent() and provides no examples to that end.

Do you know what serialEvent() is?  It's just an empty function that gets called after loop.  It's a convenient place to put serial handling code,  but there's nothing special about it.  You could put the same code at the end of your loop function and get the exact same end result. 

So the methods in @Robin2's thread DO apply, you can put the code wherever you want.  If you're really married to using the serialEvent function then do it.  It makes absolutely no difference.  It works exactly the same as having that code in loop. 

Now what's the problem?
|| | ||| | || | ||  ~Woodstock

Please do not PM with technical questions or comments.  Keep Arduino stuff out on the boards where it belongs.

Delta_G

Code: [Select]
int main(void)
{
init();

initVariant();

#if defined(USBCON)
USBDevice.attach();
#endif

setup();
   
for (;;) {
loop();
if (serialEventRun) serialEventRun();
}
       
return 0;
}



See where serialEventRun gets called?  That calls serialEvent if Serial.available() returns greater than 0.  IT doesn't do anything magic and IMHO it makes the code harder to maintain because it is being called by this "hidden" function.  Weren't you against all of that the other day? 

There's never any case where you *need* to us serialEvent. 
|| | ||| | || | ||  ~Woodstock

Please do not PM with technical questions or comments.  Keep Arduino stuff out on the boards where it belongs.

sterretje

I don't know if Robin likes serial event or not. And it does not matter.

The principle behind his examples is that it's non-blocking. So you have time to do other stuff till the full message is received and hence no need to use serial event. The only reason, as I stated in reply #7 is if your current code uses it and it's easier to implement in which case you can mix and match.
If you understand an example, use it.
If you don't understand an example, don't use it.

Electronics engineer by trade, software engineer by profession. Trying to get back into electronics after 15 years absence.

adwsystems

#18
Aug 06, 2017, 11:06 pm Last Edit: Aug 06, 2017, 11:13 pm by adwsystems
This sounds like a problem for every solution.

Post your complete program and then maybe someone can suggest something useful.

...R
Careful what you ask for.

Code: [Select]
/*
int i = 0;

void setup() {

  // open serial connection
    Serial.begin(9600);

    //Serial.println("CLEARDATA"); // clears sheet starting at row 2
    Serial.println("CLEARSHEET"); // clears sheet starting at row 1
   
  // define 5 columns named "Date", "Time", "Timer", "Counter" and "millis"
    Serial.println("LABEL,Date,Time,Timer,Counter,millis");

  // set the names for the 3 checkboxes
    Serial.println("CUSTOMBOX1,LABEL,Stop logging at 250?");
    Serial.println("CUSTOMBOX2,LABEL,Resume log at 350?");
    Serial.println("CUSTOMBOX3,LABEL,Quit at 450?");

  // check 2 of the 3 checkboxes (first two to true, third to false)
    Serial.println("CUSTOMBOX1,SET,1");
    Serial.println("CUSTOMBOX2,SET,1");
    Serial.println("CUSTOMBOX3,SET,0");
}

void loop() {

    // simple print out of number and millis. Output e.g.,: "DATA,DATE,TIME,TIMER,4711,13374,AUTOSCROLL_20"
      Serial.println( (String) "DATA,DATE,TIME,TIMER," + i++ + "," + millis() + ",AUTOSCROLL_20" );
      // alternative writing method:
      /*   Serial.print("DATA,DATE,TIME,TIMER,");
        Serial.print(i++); Serial.print(",");
        Serial.println(millis());
        Serial.print(","); Serial.println("SCROLLDATA_20"); */

    // clear some cells in Excel (rectangle range from B10 to D20)
      if(i==100)
        Serial.println("ClearRange,B,10,D,20");

    // do a simple beep in Excel on PC
      if(i==150)
        Serial.println("BEEP");

    // read a value (in this case integer) from Excel (from a sheet by name)
      if(i==200)
      {
        Serial.println("CELL,GET,FROMSHEET,Simple Data,E,4"); // ==> request value from sheet
          // Serial.println("CELL,GET,E4"); ==> short version to read from active sheet in Excel
        int readvalue = Serial.readStringUntil(10).toInt(); // get response. Note: the '10' is important! Always use but never change ;-)
        Serial.println( (String) "Value of cell E4 is: " + readvalue); // result displayed in Excel DirectDebugWindow to double check
      }

    // check value of custombox1 on PLX DAQ in Excel and if
    // checkbox is checked then send the command to pause logging
      if(i==250)
      {
        Serial.println("CUSTOMBOX1,GET");
        int stoplogging = Serial.readStringUntil(10).toInt();
        // this information can be seen in the direct debug window on PLX DAQ in Excel
        Serial.println( (String) "Value of stoplogging/checkbox is: " + stoplogging);
        if(stoplogging)
          Serial.println("PAUSELOGGING");
      }

    // get a true random number from the computer
      if(i==300)
      {
        Serial.println("GETRANDOM,-4321,12345"); // between -4321 to 12345
        int rndseed = Serial.readStringUntil(10).toInt();
        Serial.println( (String) "Got random value '" + rndseed + "' from Excel" );
        // Note: this information is not posted to the Excel sheet because "DATA" is missing
        // instead this information can be seen in the direct debug window
      }

    // and now resume logging
      if(i==350)
      {
        Serial.println("CUSTOMBOX2,GET");
        int resumelogging = Serial.readStringUntil(10).toInt();
        if(resumelogging)
          Serial.println("RESUMELOGGING");
      }   

    // post to specific cells on default sheet as well as named sheet
      if(i==400)
      {
        Serial.println("CELL,SET,G10,400 test 1 string"); // default sheet active in PLX DAQ Excel
        Serial.println("CELL,SET,ONSHEET,Simple Data,G,11,400 test 2 string"); // named sheet available in PLX DAQ Excel
      }
       
    // and for forced quit of Excel with saving the file first
      if(i==123)
      {
        Serial.println("CUSTOMBOX3,GET");
        if(Serial.readStringUntil(10).toInt()) {
          Serial.println("SAVEWORKBOOKAS,450-Lines-File");
          Serial.println("FORCEEXCELQUIT");
        }
        else
          Serial.println("No forced Excel quit requested!");
      }
}


OK. Here is the start of my program. I haven't been able to add my code because the blocking commands keep fouling it up. So I need to remove all the blocking commands.  Hmmm...Sounds like the point of the post #1.

I haven't a clue how this helps answer the original question. But if you would like to help rewrite, thanks.

Although on an aspect more to the original post, does Serial.readBytesUntil() get interrupted while waiting by ISRs?

Delta_G

Although on an aspect more to the original post, does Serial.readBytesUntil() get interrupted while waiting by ISRs?
Yes it does. 



I read your whole post.  It seems like all you need to do is to read your serial line in a non-blocking fashion.  Why do you not like the methods in the Serial Input Basics thread that was linked earlier?  They seem like they'd do exactly what you want. 


For example change this:

Code: [Select]
if(i==200)
      {
        Serial.println("CELL,GET,FROMSHEET,Simple Data,E,4"); // ==> request value from sheet
          // Serial.println("CELL,GET,E4"); ==> short version to read from active sheet in Excel
        int readvalue = Serial.readStringUntil(10).toInt(); // get response. Note: the '10' is important! Always use but never change ;-)
        Serial.println( (String) "Value of cell E4 is: " + readvalue); // result displayed in Excel DirectDebugWindow to double check
      }


 to just fill a static char buffer with characters as they arrive and then have an if test for the 10 to indicate it is done and put that last line inside it so it only runs when the message is complete. 
|| | ||| | || | ||  ~Woodstock

Please do not PM with technical questions or comments.  Keep Arduino stuff out on the boards where it belongs.

Delta_G

#20
Aug 06, 2017, 11:21 pm Last Edit: Aug 06, 2017, 11:22 pm by Delta_G
Really, an even better solution would be to have all the serial handling code in its own function and call it from the top or bottom of loop every time.  Then instead of having to write the non-blocking receive out in each of those blocks, you could just use a boolean flag to let them know when their data was ready.  Each one of those blocks in your code could simply check the flag to see if they had got their answer back and move on if not. 
|| | ||| | || | ||  ~Woodstock

Please do not PM with technical questions or comments.  Keep Arduino stuff out on the boards where it belongs.

adwsystems

#21
Aug 06, 2017, 11:23 pm Last Edit: Aug 06, 2017, 11:24 pm by adwsystems
Really, an even better solution would be to have all the serial handling code in its own function and call it from the top or bottom of loop every time.  Then instead of having to write the non-blocking receive out in each of those blocks, you could just use a boolean flag to let them know when their data was ready. 
Even better, why not combine the non-blocking code into one entire subroutine and have that routine automatically executed when data is available?

Delta_G

Even better, why not combine the non-blocking code into one entire subroutine and have that routine automatically executed when data is available?
By what definition of automatically executed?  Remember, you're going to write non-blocking code now, so it does you no good to know that you got your transmission when you're off in the middle of loop somewhere.  You only need that data at the point in loop where you need the data.  The whole point is that now loop will be fast enough to hit the point where you need the data again really soon.  I think you're chasing a problem that doesn't exist. 

So yeah, you wrap that call to the serial handling function in a Serial.available check or you check in that function.  It doesn't make much difference. 
|| | ||| | || | ||  ~Woodstock

Please do not PM with technical questions or comments.  Keep Arduino stuff out on the boards where it belongs.

Delta_G

#23
Aug 06, 2017, 11:30 pm Last Edit: Aug 06, 2017, 11:31 pm by Delta_G
Note:  If you were talking about serialEvent, remember that it doesn't do anything automatically.  It just gets called at the end of loop in a if(Serial.available()) just like the function I'm talking about writing.  The only difference is that the whole thing is hidden in main and IIRC you were pretty upset about that a couple of days ago.  I was talking about having it called from the loop function where you can see it. 
|| | ||| | || | ||  ~Woodstock

Please do not PM with technical questions or comments.  Keep Arduino stuff out on the boards where it belongs.

adwsystems

#24
Aug 06, 2017, 11:35 pm Last Edit: Aug 07, 2017, 12:12 am by adwsystems
Automatically executed as in interrupt driven, not via a polled function call.

Code: [Select]
int main(void)
{
init();

initVariant();

#if defined(USBCON)
USBDevice.attach();
#endif

setup();
   
for (;;) {
loop();
if (serialEventRun) serialEventRun();
}
       
return 0;
}



See where serialEventRun gets called?  That calls serialEvent if Serial.available() returns greater than 0.  IT doesn't do anything magic and IMHO it makes the code harder to maintain because it is being called by this "hidden" function.  Weren't you against all of that the other day? 

There's never any case where you *need* to us serialEvent. 
Seems like we have just had this conversation. In a previous post I was informed serialEvent() was interrupt driven. You have shown proof to the contrary.

If Arduino.h is calling serialEventRun() and Serial.available() is called in loop(), then isn't the same thing being checked twice?

Delta_G

If Arduino.h is calling serialEventRun() and Serial.available() is called in loop(), then isn't the same thing being checked twice?
The compiler would optimize out the one that's not being used. 
|| | ||| | || | ||  ~Woodstock

Please do not PM with technical questions or comments.  Keep Arduino stuff out on the boards where it belongs.

Robin2

#26
Aug 06, 2017, 11:41 pm Last Edit: Aug 06, 2017, 11:42 pm by Robin2
In a previous post I was informed serialEvent() was interrupt driven.
Post a link to the post that said that.

Right through this Thread people have been telling you that it is not - including in my Serial Input Basics

Careful what you ask for.
I don't see any serialEvent() or readBytesUntil() in the code in that Reply

...R
Two or three hours spent thinking and reading documentation solves most programming problems.

adwsystems

#27
Aug 06, 2017, 11:47 pm Last Edit: Aug 07, 2017, 12:03 am by adwsystems
I'm not search four years worth the posts. It's not that important. It is what I was told. Both of you are showing with the program serialEvent() is not an ISR. At that time I was just starting and didn't know the Arduino lingo well. Likely I didn't ask the question using the correct lingo, which is very important on this site (if the words in the question are not just right, people fly off in every direction with their responses). Until recently the word 'interrupt' hasn't appeared in this thread nor in Serial Input Basics. Neither say serialEvent() is or is not an ISR.

I'm fine with changing that thought process, it is a matter of having the correct information.

You are correct there is no serialEvent() nor readBytesUntil(). It is the original code that needs to be modified to remove the five readStringUntil() instances. Unless I'm about to find out that readStringUntil() is non-blocking.

P.S. My bad. I asked about the wrong function. My mistake.

westfw

Here's the core of a non-blocking read-line thing.  Just call it "reasonable often" in loop; it returns a non-zero value when its seen a line terminator...   Don't forget that the Serial core is interrupt driven but has a relatively small buffer...

Code: [Select]
uint8_t parseGetline_nb(void)
{
    int c;

    c = Serial.read();
    switch (c) {
    case 127:
    case CTRL('H'):
    /*
     * Destructive backspace: remove last character
     */
    if (inptr > 0) {
        Serial.print("\010 \010");
        linebuffer[--inptr] = 0;
    }
    break;
    case CTRL('R'):
    /*
     * Ctrl-R retypes the line
     */
    Serial.print("\r\n");
    Serial.print(linebuffer);
    break;
    case CTRL('U'):
    /*
     * Ctrl-U deletes the entire line and starts over.
     */
    Serial.println("XXX");
    memset(linebuffer, 0, sizeof(linebuffer));
    inptr = 0;
    break;
    case CTRL('M'):
    Serial.println();            /* Echo newline too. */
    return inptr;
    default:
    /*
     * Otherwise, echo the character and put it into the buffer
     */
    linebuffer[inptr++] = c;
    Serial.write(c);
    case -1:
    /*
     * No character present; don't do anything.
     */
    return 0;
    break;
    }
}


Code: [Select]
void setup() {
  while (!Serial)
    delay(500);
  Serial.begin(115200);
  Serial.print(F("Enter command: "));
  parseReset();
  pinMode(13, OUTPUT);
}

boolean delay_without_delaying(unsigned long &since, unsigned long time) {
  // return false if we're still "delaying", true if time ms has passed.
  // this should look a lot like "blink without delay"
  unsigned long currentmillis = millis();
  if (currentmillis - since >= time) {
    since = currentmillis;
    return true;
  }
  return false;
}

int red, blue, green;
unsigned long ledtime;

void loop() {
  char *p;
  int8_t cmd;
  int n;

  if (parseGetline_nb()) {
    do {
      enum {
        CMD_RED, CMD_GREEN, CMD_BLUE, CMD_RESET  // make sure this matches the string
      };
      cmd = parseKeyword(PSTR("red green blue reset")); // look for a command.

      if (cmd >= 0) {
        n = parseNumber();
      }
      switch (cmd) {
        case CMD_RED:
          red = n;
          break;
        case CMD_BLUE:
          blue = n;
          break;
        case CMD_GREEN:
          green = n;
          break;
        case CMD_RESET:
          red = green = blue = 0;
          break;
        case PARSER_EOL:
          Serial.print("RED = "); Serial.print(red);
          Serial.print(" GREEN = "); Serial.print(green);
          Serial.print(" BLUE= "); Serial.println(blue);
          break;
        default:
          Serial.println("Invalid command");
          break;
      }
    } while (cmd >= 0);
    parseReset();
    Serial.print(F("Enter command: "));
  } // if line
 
  static int ledstate = false;
  if (delay_without_delaying(ledtime, 500)) {
    ledstate = !ledstate;
    digitalWrite(13, ledstate);
  }
}

westfw

(The rest of the "parser library", such as it is, is at https://github.com/WestfW/parser)

Go Up