0

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...

asked Mar 21, 2016 at 20:38

2 Answers 2

0

there is a great function: https://www.arduino.cc/en/Serial/ParseInt

I have tested the following script up 2kHz incoming, but it should go even faster. Do something like this

int number = 0;
int number2 = 0;
bool get_data = false;
void setup() {
 Serial.begin(115200);
 inputString.reserve(200);
 pinMode(9,OUTPUT); // fixed pin 9 for PPM output
}
void loop() {
 if(get_data == true){
 Serial.print(number);
 Serial.print(number2);
 get_data = false;
 }
}
 void SerialEvent()
 if(!get_data){
 number = Serial.parseInt();
 number2 = Serial.parseInt();
 get_data = true;
 }
}
mike
3476 silver badges20 bronze badges
answered Mar 21, 2016 at 22:28
0
1

115200 baud at 200Hz means you should be able to transfer in excess of 50 characters between each "trigger". That is more than enough time to send the data even as ASCII encoded text.

serialEvent() is no better than manually looking at things like serialAvailable() in your loop since that is all it does anyway. It just runs after each iteration of your loop.

One problem that you have is that you're appending each character to a String object. This is very inefficient and should be avoided. Instead you should really use a character array:

// Input text in the form "123 456\n"
char instring[20]; // Room for 19 characters plus terminating null
int spos = 0; // Location in the array
int val1 = 0;
int val2 = 0;
void loop() {
 if (Serial.available()) {
 char *bita, *bitb;
 int c = Serial.read();
 switch (c) {
 case '\r': break; // ignore
 case '\n': 
 spos = 0;
 bita = strtok(instring, " ");
 bitb = strtok(NULL, " ");
 if (bita && bitb) {
 val1 = atoi(bita);
 val2 = atoi(bitb);
 }
 // You now have two new values in val1 and val2
 break;
 default:
 if (spos < 18) {
 instring[spos++] = c;
 instring[spos] = 0;
 }
 }
 }
} 
answered Mar 21, 2016 at 22:33
0

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.