I try to send pairs of numbers over the serial port from a linux pc to the arduino. By now I did so always by piecing together the ASCII chars from the serial line and using Serial.read()
. Now I need this same thing to run at 100-200Hz, and it turns out, that the Arduino does not receive all the data (even at 1Hz).
As turned out, my problem was in another portion of the code and all below code works perfectly as is!
How do I make sure, that every char (or better even byte without ASCII encoding) is received? I am aware, that each connection has errors, but this is a systematical error! I guess, that the bytes are lost during the processing within the main loop, or because of other callbacks I use (got some ISR running). I am not looking for error detection, but rather a concise way of reading the serial line, without loosing 70% of the data validity.
If important: the pairs i need to send are unsigned, 1 byte and another one with 2 bytes. The values are in the range of [0;7]
and [1000;2000]
. Thanks for any advice. Disclaimer, I read a lot, including this, but most questions go into detail about the packaging format, whereas I am concerned about the reading method.
Code I use at the moment:
// read numbers from the char representation!
// on the serial connection
// each DIGIT is sent separately
String inputString = ""; // a string to hold incoming data
boolean stringComplete = false; // whether the string is complete
uint8_t channel; // holding the number of the channel to modify
uint16_t signal; // ppm signal length in us (1000-2000)
void setup() {
Serial.begin(115200);
inputString.reserve(200);
pinMode(9,OUTPUT); // fixed pin 9 for PPM output
}
void loop() {
if (stringComplete) {
channel = uint8_t(inputString[0])-48;
signal = 0;
for (uint8_t n=1; n<5; n++) {
signal = (signal*10) + uint8_t(inputString[n])-48;
}
Serial.print(F("ch: ")); Serial.print(channel);
Serial.print(F(" signal: ")); Serial.print(signal);
Serial.print(F("\n"));
//PROCESS Data
inputString = "";
stringComplete = false;
}
}
void serialEvent() {
while (Serial.available()) {
char inChar = (char)Serial.read();
inputString += inChar;
// if the incoming character is a
// newline (10) or carriage return (13)
// set a flag for a complete transmission
if (inChar == 10 | inChar == 13) {
stringComplete = true;
}
}
}
Another version I tried, without strings is:
// channel and pulse-length
uint16_t numbers[2] = {0, 0}; // channel,value
uint8_t buf[5];
void setup() {
Serial.begin(115200);
}
void loop() {
if (serial_input(numbers,buf)) {
numbers[0] = buf[0]-48;
numbers[1] = (buf[1]-48)*10+(buf[2]-48);
Serial.print(F("channel: ")); Serial.println(numbers[0]);
Serial.print(F(" signal: ")); Serial.println(numbers[1]);
// Do Stuff
}
}
uint8_t serial_input(uint16_t* num, uint8_t* buf) {
uint8_t newData = 0;
uint8_t k = 0;
num[0] = 0; num[1] = 0;
while(Serial.available()) {
delayMicroseconds(2100);
char ser = Serial.read();
if (isDigit(ser)) {
buf[k] = uint8_t(ser);
k++;
newData = 1;
} else {break;}
}
return newData;
}
Another try inspired by an answer here, where I send "3 1500\n" over the serial line:
void serialEvent() {
if (!newdata) {
channel = Serial.parseInt();
signal = Serial.parseInt();
newdata = true;
}
}
Turns out all of those methods work (more or less elegantly), but having a "delay" within an ISR isn't such a good idea - even intermediately...