I am using 2 adafruit PWM servo shield in order to generate 25 PWM signals. In particular I am using this shield: https://www.adafruit.com/products/1411
A python script sends the commands to the Arduino (Mega 2560) through pyserial. The commands are like "A0 P1 S500\n". Each argument separated by a space. The first chooses the shield, the second one the pin, and the third one the speed.
The code running in the Arduino is as follows:
#include <Adafruit_PWMServoDriver.h>
#define BUFFER_SIZE 200
// Init both adafruit boards
Adafruit_PWMServoDriver pwm0 = Adafruit_PWMServoDriver(0x40);
Adafruit_PWMServoDriver pwm1 = Adafruit_PWMServoDriver(0x41);
int read_command(char *buffer)
{
static int pos = 0;
int rpos;
if (Serial.available() > 0) {
Serial.println(Serial.available());
char readch = Serial.read();
switch (readch) {
case '\n':
rpos = pos;
pos = 0; // Reset position index ready for next time
Serial.println(buffer);
return rpos;
default:
if (pos < BUFFER_SIZE -1) {
buffer[pos++] = readch;
buffer[pos] = '\0';
} else {
Serial.println("666"); //buffer overflow
}
}
}
// no end line or char found, return -1
return -1;
}
void parse_command(char* command)
{
char* parameter;
parameter = strtok(command, " ");
long shield, pin, speed;
while (parameter != NULL) {
switch(parameter[0]) {
case 'A':
shield = strtol(parameter+1, NULL, 10);
break;
case 'P':
pin = strtol(parameter+1, NULL, 10);
break;
case 'S':
speed = strtol(parameter+1, NULL, 10);
break;
}
parameter = strtok(NULL, " ");
}
for (int x=0; x < BUFFER_SIZE; x++)
command[x] = '\0';
if (shield == 0)
pwm0.setPWM(pin, 0, speed);
if (shield == 1)
pwm1.setPWM(pin, 0, speed);
}
void setup()
{
Serial.begin(9600);
Serial.flush();
pwm0.begin();
pwm0.setPWMFreq(100); //Max is 1600, Kevin was using 1000
pwm1.begin();
pwm1.setPWMFreq(100);
}
void loop()
{
static char buffer[BUFFER_SIZE];
if (read_command(buffer) > 0)
parse_command(buffer);
}
As said, I am sending the commands from a Python script, where for example I have a function called "activate all", that will send 25 commands like "A0 P1 S500\n" (obviously with different values after the A and the P) to the serial port. I also have a function called "disable all", which is very similar to the previous command, but setting the S to 0 so like "A0 P1 S0\n".
So, as I start the serial communication, if I execute from Python "activate_all", I can how all the PWM signals are generated at the same time. I know it is actually 1 by 1, but it is fast enough to seem simultaneously to the human eye. If I then send a disable_all, I can see how all the PWM signals are set to 0 at the same time.
This works as expected for a while, but after some point, when I send an activate_all, it will indeed activate all of them, but it activates each PWM signal one by one, with a delay of around 1 second between activations (so you can clearly see them going one by one), and a very similar effect when disabling them all. From this point on, which seems slightly different each time, every time I want to activate more than one pwm signal at the same time, it will add this 1s delay between activations.
The only way I have to fix this is to send from the python a "flush()". which seems to be fix it. Doing flushes from the Arduino code, like when for example serial.available is 0, doesnt seem to fix it.
I don't know what exactly I am doing wrong. I guess the way I am handling the communication buffer is wrong, but I haven't seen where my mistake is.
A very similar code to the one pasted here was used in another project, in that case it was interfaced with Labview, and in that case everything seem fine.
In case you wonder about the code running in the Python script, it is like this:
import serial, time, json
class BZBoard:
def __init__(self, port):
self.ser = serial.Serial(port, 9600, timeout=120)
time.sleep(2) # serial docs recommend a wait just after connection
self.ser.flush(); self.ser.flushInput(); self.ser.flushOutput();
self.motors = {
just naming the pins
}
def __del__(self):
self.disable_all()
self.ser.close()
del self.ser
def close(self):
self.disable_all()
self.ser.close()
del self.ser
def activate_motor(self, motor_code, speed=300):
''' code as in A1 or C2 as marked in the actual board. See the dict motors'''
shield, pin = self.motors[motor_code]
command = "A%d P%d S%d\n" % ( shield, pin, speed )
self.ser.write(command.encode())
def activate_all(self, speed=300):
for key in self.motors.keys():
self.activate_motor(key, speed)
def disable_motor(self, motor_code):
shield, pin = self.motors[motor_code]
command = "A%d P%d S0\n" % ( shield, pin )
self.ser.write(command.encode())
def disable_all(self):
for key in self.motors.keys():
self.disable_motor(key)
if __name__ == "__main__":
board = BZBoard("/dev/ttyACM0")
So the user will do for example a lopp that repeats itself 20 times, where it will activate_all, wait 1 second, disable_all, wait 1 second. The first 10 iterations or so, everything will be fine and the PWM are generated all of them at the same time (with respect to the human eye), but after that, the PWM signals are generated 1 by 1, with a delay of 1s or so between them.
Serial.print()
orSerial.println()
) does the problem still occur? – James Waldby - jwpat7 Mar 3 '17 at 21:05