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

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;
   }
}
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;
                }
        }
    }
}            
0

Your Answer

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

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