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 eachvoid loop()
. - Checking
peek()
,available()
, andreadString()
- after I send it one instruction, they return the same values every single time they loop.
3 Answers 3
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();
}
-
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– sscirrus2015年08月07日 22:33:30 +00:00Commented 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– Majenko2015年08月07日 22:35:21 +00:00Commented Aug 7, 2015 at 22:35
-
I've posted the code.sscirrus– sscirrus2015年08月07日 23:05:12 +00:00Commented 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 wantreadString('\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– Majenko2015年08月07日 23:28:42 +00:00Commented 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– sscirrus2015年08月07日 23:35:42 +00:00Commented Aug 7, 2015 at 23:35
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.
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();
}
}
}
-
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.2015年08月09日 00:36:01 +00:00Commented Aug 9, 2015 at 0:36