Reading Integers from Serial Communication

Hi All,

I am attempting to send motor position values from my computer to an Arduino over serial communication.

So far, I have been able to encode the position values for a single motor into a bytearray on Python and then grab those integers on the Arduino using the code below (not my code; found on another post):

char buf[80];

int readline(int readch, char *buffer, int len) {
  static int pos = 0;
  int rpos;

  if (readch > 0) {
    switch (readch) {
      case '\r':  // Ignore CR
        break;
      case '\n':  // Return on new-line
        rpos = pos;
        pos = 0;  // Reset position index ready for next time
        return rpos;
      default:
        if (pos < len - 1) {
          buffer[pos++] = readch;
          buffer[pos] = 0;
        }
    }
  }
  return 0;
}

// Call later on...
readline(Serial.read(), buf,80)
int stepcount = atoi(buf)                       // Use stepcount to drive motors

This works great for a single set of positions (i.e., a single motor's position). However, I'm not sure how to send positions for different motors at the same time. For example:

Left Motor Position : 1000 steps
Right Motor Position : -500 steps

If my single motor position is encoded in the format of '\r1000\n', I would like to find a way to encode both left and right positions into a single string or have some way of differentiating a right motor position from a left motor position.

Part of my issue is not fully understanding how the above snippet of code actually works and then be able to modify it with this added functionality. By my understanding, the above code will grab the byte coming in on Serial.read(), check the "case," and then add that byte to the buffer until the '\n' byte is received and return the completed buffer as the output.

Have you seen the serial input basics tutorial? It, I think, will help you.

1 Like

That's really close. The only thing you missed is the return value. This code returns the number of bytes it put in the buffer. You are passing a pointer to your buffer in the arguments and it's putting the string in that. So the caller owns the buffer.

That's what you have to do. Have a look at the Serial Input Basics thread on this site. There's a lot of good techniques in there.

Basically you need to construct some sort of message. It can be however you want since you will have to write code on both ends.

I have a robot that gets control codes in brackets like these < >. When it receives a command, the first byte is always an M so it knows it's a motor speed. The second byte is either a L or a R to let it know which motor and then the last byte is the motor speed as a raw byte.

Whatever you come up with, you have to be able to parse that on the other end. One trick that will help is to understand that if buf is an array then buf without [] or a * is a pointer to it and you can use pointer math.

So if my received line in buf looks like:

<MR127>

then
int stepcount = atoi(buf+3);

will get the 127 into an int. The +3 on the pointer lets it know to move three characters into the buffer to start.

2 Likes

It is equivalent to:


int stepcount = atoi( &buf[3] );

Which to use is a matter of taste...

look this over

input

a123,b  345,c,d 999

output

 cmd a 123
 cmd a 345
 unknown cmd 512
 unknown cmd 999

void
process (
    char *t)
{
    char c;
    int  val;
    sscanf (t, "%c%d", &c, &val);

    switch (c) {
    case 'a':
        Serial.print (" cmd a ");
        Serial.println (val);
        break;

    case 'b':
        Serial.print (" cmd a ");
        Serial.println (val);
        break;

    default:
        Serial.print (" unknown cmd ");
        Serial.println (val);
        break;
    }
}

void
loop (void)
{
    if (Serial.available ()) {
        char buf [80];
        int n = Serial.readBytesUntil ('\n', buf, sizeof(buf));
        buf [n] = '\0';     // terminate with null

        char *t = strtok (buf, ",");
        process (t);
        while ((t = strtok (NULL, ",")))
            process (t);
    }
}

void
setup (void)
{
    Serial.begin (9600);
}
1 Like

Thank you, all, for the replies! I will take a look at your suggestions.

Hi gcjr,

Thank you for the reply.

This works pretty much exactly how I envisioned. One issue I am finding, however, is that it appears to require a long time to process the information coming in on serial. Is there anything I can do to speed this up? For my application, I really need near instantaneous conversion from a string like "a123" to an integer "123" in order to have precise control of the motors.

void process(
  char *t) {
  char c;
  int val;
  sscanf(t, "%c%d", &c, &val);

  switch (c) {
    case 'a':
      rad = val;
      break;

    case 'b':
      theta = val;
      break;

    default:
      break;
  }
}

void loop() {
  // put your main code here, to run repeatedly:
  if (Serial.available()) {
    char buf[80];
    int n = Serial.readBytesUntil('\n', buf, sizeof(buf));
    buf[n] = '\0';  // terminate with null

    char *t = strtok(buf, ",");
    process(t);
    while ((t = strtok(NULL, ",")))
      process(t);
   
  stepper1.moveTo(rad);
  stepper1.runToPosition();
}

I'd like to be able to send a new motor position every 15 milliseconds (100 ms would be the maximum interval for updating position). Then have the motor move before reading the next command coming in on serial. Currently, it appears that the motor movement is being interrupted by the if(Serial.available()) segment.

Actually, I may have just found the issue!

I noticed that the line "int n = Serial.readBytesUntil('\n', buf, sizeof(buf));" was looking for the '\n' to terminate the reading of serial.

I was sending my strings in the format 'a123.' By adding in '\n, e.g., 'a123\n,' the code is much more responsive!

Thanks!

Why not like this?

void setup ()
{
    Serial.begin (9600);
}

1.

In the above parsing begins at three items deep from the beginning of buf[] array.

2.

In the above, parsing begins from item three of buf[] array.

If I take 1., then I have to remember that array name (the buf) bears the base address of the array.

If I take 2., then I have to remember that buf[3] is a value.

Indeed - so, as I said, it's just a matter of taste which one you use.

They are equivalent, and will generate the same code.

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.