0

I have a Yun that accepts commands via the bridge (note the below code is part of a very simple virtual keyboard that accepts commands then runs Keyboard.println on them:

#include <stdlib.h>
#include <string.h>
HardwareSerial *port;

void setup() {
  port = &Serial1;
  port->begin(9600);
}

void loop() {
  if (port->available()) {
    String cmd = port->readString();

    if(cmd.length() > 1){
      String instruction = cmd;
      int arg_comma_index = 0;
      arg_comma_index = instruction.indexOf(",");
      String func = instruction.substring(0, arg_comma_index);
      String args = instruction.substring(arg_comma_index + 1, instruction.length() + 1 );

      Keyboard.begin();
      Keyboard.println(args);
      Keyboard.end();
    }
    port->read(); // Try to throw away whatever's left to prevent looping (which is still happening).
  }
}

If I send it a short string:

  • The first time, immediately after starting the sketch, it works.
  • The second time, it works but loops anywhere from 1-5 times.
  • The third time, it loops more times still.
  • If I leave the device running (even if not operating it), the number of times it loops goes up when I finally do send it an instruction. I'm seeing 20-75 loops after less than 15 minutes of uptime.

Clearly the loops where no instruction is being received are still building up something here. I've tried:

  • Restarting the port each void loop().
  • Flushing the port at the top and/or bottom of each void loop().
  • Setting cmd = "" at the end of each void loop().
  • Checking peek(), available(), and readString() - after I send it one instruction, they return the same values every single time they loop.

3 Answers 3

1

It's quite simple to empty the serial buffer - you just read all the data that is in the buffer and throw it away:

while (port->available()) {
    port->read();
}
7
  • Thank you! Unfortunately, this isn't fixing my looping problem. I put port->read(); at the end of my loop but it is still running a variable number of times.
    – sscirrus
    Commented Aug 7, 2015 at 22:33
  • Maybe if you posted your real code instead of some mashup of c and basic we might be able to help you better.
    – Majenko
    Commented Aug 7, 2015 at 22:35
  • I've posted the code.
    – sscirrus
    Commented Aug 7, 2015 at 23:05
  • That is quite some code. I can't fathom quite what you're attempting to do there. I think your main problem is that you are using readString() which doesn't do what you think it does. I think you want readString('\n') to read up to a newline (or whatever terminator you choose) character. Also, what is all the rest meant to be doing? Whatever it is you seem to have made it overly complex and bizarre. Please expand your question with what you intend it to do.
    – Majenko
    Commented Aug 7, 2015 at 23:28
  • The code I posted above is a much-shortened snippet of the original code, but it displays the same issue (I shortened it for easier reading and debugging). The device is a simple virtual keyboard, accepting some commands over the bridge and outputting them with Keyboard.println.
    – sscirrus
    Commented Aug 7, 2015 at 23:35
1

I wouldn't be using readString myself. In fact I wouldn't be using the String class at all. readString keeps reading until it times out (by default after one second), so it may read more - or less even - than you want. It will also introduce a one-second delay while it sits around waiting for that second to elapse.

You are much better off doing a non-blocking loop, and then processing data when you get a known delimiter, like a newline or comma, or whatever you designate as "ending the stream". Example code that uses a newline delimiter:

// how much serial data we expect before a newline
const unsigned int MAX_INPUT = 50;

void setup ()
  {
  Serial.begin (115200);
  } // end of setup

// here to process incoming serial data after a terminator received
void process_data (const char * data)
  {
  // for now just display it
  // (but you could compare it to some value, convert to an integer, etc.)
  Serial.println (data);
  }  // end of process_data

void processIncomingByte (const byte inByte)
  {
  static char input_line [MAX_INPUT];
  static unsigned int input_pos = 0;

  switch (inByte)
    {

    case '\n':   // end of text
      input_line [input_pos] = 0;  // terminating null byte

      // terminator reached! process input_line here ...
      process_data (input_line);

      // reset buffer for next time
      input_pos = 0;  
      break;

    case '\r':   // discard carriage return
      break;

    default:
      // keep adding if not full ... allow for terminating null byte
      if (input_pos < (MAX_INPUT - 1))
        input_line [input_pos++] = inByte;
      break;

    }  // end of switch

  } // end of processIncomingByte  

void loop()
  {
  // if serial data available, process it
  while (Serial.available () > 0)
    processIncomingByte (Serial.read ());

  // do other stuff here like testing digital input (button presses) ...

  }  // end of loop

In my example code you would do whatever-it-is you want in process_data which is called once an entire line arrives. Then you don't need to discard incoming data (empty the serial buffer), which seems to me to be a bad idea anyway.

0

How do you send your commands, char by char or one shot ?

If there is nothing to read, port->available() may return -1 error code after 1 sec, but you already checked, isn't it ? Anyway, you should check if it returns something > 0 instead of something != 0.

Note that readString() doesn't take any parameter (https://www.arduino.cc/en/Reference/StreamReadString). I suspect this function not acting as read() to decrease the number returned by available(); If you want to catch \n, use readStringUntil('\n') instead (https://www.arduino.cc/en/Serial/ReadStringUntil).

Try to put the full code (with the loop) given by Majenko at the end of your loop() function.

#include <stdlib.h>
#include <string.h>
HardwareSerial *port;

void setup() {
  port = &Serial1;
  port->begin(9600);
}

void loop() {
  if (port->available() > 0) {
    String cmd = port->readString();

    if (cmd.length() > 1) { // Why not 0 ?
      String instruction = cmd;
      int arg_comma_index = 0;
      arg_comma_index = instruction.indexOf(",");
      String func = instruction.substring(0, arg_comma_index);
      String args = instruction.substring(arg_comma_index + 1, instruction.length() + 1 );

      Keyboard.begin();
      Keyboard.println(args);
      Keyboard.end();
    }
    while (port->available() > 0) {
        port->read();
    }
  }
}
1
  • I suspect this function not acting as read() to decrease the number returned by available(); - readString will indeed read (and decrease the number in the buffer) however it just reads until it times out, so it could read more, or less even, than is required.
    – Nick Gammon
    Commented Aug 9, 2015 at 0:36

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.