Issue controlling 5 servos with a single Photon


#1

Hello. I am trying to control 5 analog servos (A4, WKP, D0, D1, D2) using a single photon. Photon is powered via USB and I am drawing power for the servos from VIN/GND. For now, I’ve only connected 3 servos (D2, A4 & WKP) but I have the code already written to rotate all 5 servos in this order -> D0, D1, D2, A4, WKP. I use the servo.attach(), servo.write(angle), delay() and servo.detach() functions.
The issue is that the servo connected to the D2 pin rotates 3 times ( I believe the code for D0 and D1 are also sending PWM signal to the D2 pin). Is this a known issue? I couldn’t find any documentation about PWM being duplicated on these pins.
Any help is appreciated. Thanks!


#2

@krazyvshank the only duplication for PWM that I could find in the current documentation was A5 & D2 and A4 & D3 .

“On the Photon and Electron, this function works on pins D0, D1, D2, D3, A4, A5, WKP, RX and TX with a caveat: PWM timer peripheral is duplicated on two pins (A5/D2) and (A4/D3) for 7 total independent PWM outputs. For example: PWM may be used on A5 while D2 is used as a GPIO, or D2 as a PWM while A5 is used as an analog input. However A5 and D2 cannot be used as independently controlled PWM outputs at the same time.”

There were several changes made in firmware 6.0 that effect the PWM analogWriteMaxFrequency and analogWriteResolution.


#3

There are two duplicates, but D0 should not be affected by D0 or D1 (the dups are A5/D2 and A4/D3). I think you need to post your code.


#4

Thanks @carbuthn and @Ric. I did read about the A5/D2 and A4/D3 duplications and that is why I chose the pins I am using.
@carbuthn - Does the photon system firmware get updated automatically? Did they roll out the 6.0 recently? How do I get back to an older version to see if that might fix my issue?

Here is the code -

//Automated Blinds Opening and Closing using Particle Photon
// a maximum of eight servo objects can be created 

 
Servo myservo;

int val;

int ledControl(String command);


void setup() 
{

myservo.attach(D0); 
myservo.attach(D1); 
myservo.attach(D2); 
myservo.attach(A4);
myservo.attach(WKP);

Particle.function("ledstate", ledControl);
Particle.variable("checkStatus1", "off");

}

void loop()

{
    
}

int ledControl(String command)


{  //if and else will set on off functionality
    if (command == "1") {   
    myservo.attach(D0);
    delay(100);
    myservo.write(160); 
    delay(1000);
    myservo.detach();
    delay(100);
    Particle.variable("checkStatus1", "on");
    myservo.attach(D1);
    delay(100);
    myservo.write(160); 
    delay(1000);
    myservo.detach();
    delay(100);
    Particle.variable("checkStatus1", "on");
    myservo.attach(D2);
    delay(100);
    myservo.write(160); 
    delay(1000);
    myservo.detach();
    delay(100);
	Particle.variable("checkStatus1", "on");
    myservo.attach(A4);
    delay(500);
    myservo.write(135); 
    delay(1000);
    myservo.detach();
    delay(100);
	Particle.variable("checkStatus1", "on");
    myservo.attach(WKP);
    delay(500);
    myservo.write(140); 
    delay(1000);
    myservo.detach();    
    Particle.variable("checkStatus1", "on");

  } else if(command == "0"){
    myservo.attach(D0);
    delay(500);
    myservo.write(0);  
    delay(1500);
    myservo.detach();
    delay(100);
    Particle.variable("checkStatus1", "off");
    myservo.attach(D1);
    delay(500);
    myservo.write(0);  
    delay(1500);
    myservo.detach();
    delay(100);
    Particle.variable("checkStatus1", "off");
    myservo.attach(D2);
    delay(500);
    myservo.write(0);  
    delay(1500);
    myservo.detach();
    delay(100);
    Particle.variable("checkStatus1", "off");
	myservo.attach(A4);
    delay(500);
    myservo.write(0);  
    delay(1500);
    myservo.detach();
    delay(100);
    Particle.variable("checkStatus1", "off");
	myservo.attach(WKP);
    delay(1000);
    myservo.write(14);  
    delay(1000);
    myservo.detach();
    Particle.variable("checkStatus1", "off");
 }else if(command == "3"){
    myservo.attach(D2);
    delay(500);
    myservo.write(180);  
    Particle.variable("checkStatus1", "on");
    delay(1000);
    myservo.detach();
 } else if(command == "4"){
    myservo.attach(D2);
    delay(500);
    myservo.write(0);  
    Particle.variable("checkStatus1", "off");
    delay(1000);
    myservo.detach();
 }
     
    else{
    myservo.attach(D0);
    delay(500);
    val = command.toInt(); //converts command to integer to be used for positional arrangement
    val = map (val, 0, 99, 2, 180);
    myservo.write(val);
    delay(1200);
    myservo.detach();
	Particle.variable("checkStatus1", "on");

    myservo.attach(D1);
    delay(500);
    val = command.toInt(); //converts command to integer to be used for positional arrangement
    val = map (val, 0, 99, 2, 180);
    myservo.write(val);
    delay(1200);
    myservo.detach();
    myservo.attach(D2);
    delay(500);
    val = command.toInt(); //converts command to integer to be used for positional arrangement
    val = map (val, 0, 99, 2, 180);
    myservo.write(val);
    delay(1200);
    myservo.detach();
    myservo.attach(A4);
    delay(500);
    val = command.toInt(); //converts command to integer to be used for positional arrangement
    val = map (val, 0, 99, 2, 180);
    myservo.write(val);
    delay(1200);
    myservo.detach();
	myservo.attach(WKP);
    delay(500);
    val = command.toInt(); //converts command to integer to be used for positional arrangement
    val = map (val, 0, 99, 2, 180);
    myservo.write(val);
    delay(1200);
    myservo.detach();

    }
}

#5

My photons updated to the new default (6.0) automatically when I used the online Web IDE to build. You can select the device, click on the > to the right of the device and then pick a different system firmware.

Firmware 0.6.0 is out and default as of 4 days ago. 0.6.0 at the end of the message .


#6

Having lots of delay in a Particle function is a bad idea–the cloud will timeout before your function returns. A better way is to treat the Particle function like an interrupt service routine and set flags for the main loop to take the actions you want, and then it can clear the flags.

It is also not clear to me what you are trying to do with all the servo detach and attach statements. It looks like you are trying to use one single servo object to service all those pins when you could have multiple servo objects. When you detach the servo object from the pin, that pin will stop outputting servo control signals and your servo will do something strange. Cheap servos will go to one extreme or the other while expensive servos can be programmed to return to a home position when the control signal is lost, but either way, that would not seem to be what you wanted. Normally you only detach when you want to use the same pin for some other kind of I/O.


#7

Thanks @bko. To answer your question on what I am trying to do -> Automate Window Blinds. So, I am just opening / closing the blinds one at a time in a sequence. It works perfectly when I control up to 3 blinds (A4, WKP and D0 or D1 or D2). Since I have 5 windows next to each other, I would like to have one photon as the control point.
However, now that I have added code for all 5 pins, somehow the servo on D2 is responding to D0, then D1 and then D2. One more strange thing is that, the servo on D2 goes to 160 degrees and goes back to 0 when command = ‘1’ (please see code). I am beginning to wonder now if the photon is defective.
Thanks again.


#8

I’m not sure what you’re trying to do in setup. You can only attach myServo to one pin, so only the last call to attach will have any effect. You should try what @bko suggested; create 5 Servo objects so you don’t need to be attaching and detaching all the time. See if that fixes your problem.

Also, you are using Particle.variable incorrectly. It should be called once in setup with a variable as the second argument.

String status1;

void loop() {
    Particle.variable("checkStatus1", status1);
}

In your function handler (or better yet in loop where you should be handling servo calls), you set the value of that variable.

status1 = "on";

#9

Thanks @Ric, @bko and @carbuthn. The issue is now resolved. I used different servo objects as per your suggestion. I HD to detach the servos because they were not able to hold the blinds all the way closed without buzzing. But what resolved the issue was to attach, rotate, detach and then re attach.
Thanks for all your help!


#10

@krazyvshank I thought that a analog servo needed to be connected to an analog pins? They would work fine connecting to a digital pin too? I am trying to do the same thing as you five servos one Photon for blind control but I was going to use the A4, A5, WKP, TX and RX. I might have burnt out my Photon’s resistor so I am a little worried about doing it again, I feel I am doing something wrong. Should I be using the digital ports instead of the A5, TX and RX. Didn’t have servos connect to those pins when the issue happened but I did have it setup in my code for it. Any help would be appreciated.

Thanks.


#11

What a servo needs is pulse width modulation (PWM). There are nine pins on the Photon that have that capability, though some can’t be used independently. This is from the Photon data sheet:

PWM is available on D0, D1, D2, D3, A4, A5, WKP, RX, TX with a caveat: PWM timer peripheral is duplicated on two pins (A5/D2) and (A4/D3) for 7 total independent PWM outputs. For example: PWM may be used on A5 while D2 is used as a GPIO, or D2 as a PWM while A5 is used as an analog input. However A5 and D2 cannot be used as independently controlled PWM outputs at the same time.


#12

For others who happen upon this topic, another solution is to use an I2C PWM driver like the PCA 9685. I’ve used this board from Adafruit with the Photon and it works great. 16 channels on each board. And theoretically you can connect 62 of them, just in case you want to drive 922 servos. I did not attempt that.


#13

Thank you @ric not sure why I assumed analog only.