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