Servos & PWM RX/TX issue

Hi everyone,

First post! I am migrating an Arduino project that runs 7 servos. My photon is set up and everything is almost perfect.

It seems that servos on Photon require PWM. Does anyone know why this is? I was running them fine over digital pins on the arduino.

I found the following about which pins are PWM ...

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.

So my real question - I am trying to get 7 PWM outputs through D0, D1, D2, D3, WKP, RX and TX. According to the above tech spec, this set up should be fine but RX and TX aren't working the same as the other pins.

I did find the following issue that RX and TX can be randomly inverted but this seems to have been fixed since firmware 0.4.4 which I am running.

Any help would be SUPER appreciated.

Can you define what “aren’t working as the other pins” is?

All servos require PWM, your arduino created PWM signal on the pins as well.
The way an arduino drives the non-PWM pins is by using software.

PWM signal is basically a quickly on and off switching signal, its best to use hardware PWM to generate it because it frees up CPU time.
If you want to drive your servos on non PWM pins I recommend that you to look into how a servo PWM works and then use hardware timers to make your own software PWM signal.

Regards

It’s not easy to tell without seeing your code, but could it be that you are using Serial.begin() Serial1.begin() anywhere in your code (including libs)?

What exact behaviour do you see on the “odd” pins?

Cheers @Sleziak for the suggestion. I have no doubt this is probably where I will end up going at some point.

@ScruffR I never knew that Serial.begin would interfere with TX / RX PWM but that makes total sense thanks for that. I did try taking out the Serial.begin though and I am still having issues. I can at least see the servos are trying to move… but not going anywhere. See my code below… The TX / RX servos buzz as if they want to move but dont. A bit strange considering all others work fine.

//servo setups
Servo serv1;
int serv1pos = 0;
int serv1pin = TX;
int serv1tuning = 62;

Servo serv2;
int serv2pos = 0;
int serv2pin = RX;
int serv2tuning = 59;

Servo serv3;
int serv3pos = 0;
int serv3pin = WKP;
int serv3tuning = 48;

Servo serv4;
int serv4pos = 0;
int serv4pin = D3;
int serv4tuning = 51;

Servo serv5;
int serv5pos = 0;
int serv5pin = D2;
int serv5tuning = 53;

Servo serv6;
int serv6pos = 0;
int serv6pin = D1;
int serv6tuning = 40;

Servo serv7;
int serv7pos = 0;
int serv7pin = D0;
int serv7tuning = 48;

//vars 
int swingangle = 100;

//-------------------------------------------------------------

void setup() {
 
  testsong(); 
  
}

void loop() {
}

//-------------------------------------------------------------

void testsong(){
  
    note1(); 
    delay(1000);
    note2(); 
    delay(1000);
    note3();
    delay(1000);
    note4();
    delay(100);
    note5();
    delay(100);
    note6();
    delay(100);
    note7();
    delay(100);
  
    testsong();
  
}


//-------------------------------------------------------------

void note1(){

  serv1.attach(serv1pin);
  
  for(serv1pos = 103; serv1pos >= serv1tuning; serv1pos -= 1){                                
    serv1.write(serv1pos);             
    delay(3);        
  }

  for(serv1pos = serv1tuning; serv1pos <= 103; serv1pos += 1){                                
    serv1.write(serv1pos);             
    delay(3);        
  }
  
  
  serv1.detach();
  
}

//-------------------------------------------------------------



void note2(){
  
  serv2.attach(serv2pin);
  
  for(serv2pos = swingangle; serv2pos >= serv2tuning; serv2pos -= 1){                                
    serv2.write(serv2pos);             
    delay(3);        
  }

  for(serv2pos = serv2tuning; serv2pos <= swingangle; serv2pos += 1){                                
    serv2.write(serv2pos);             
    delay(3);        
  }
  
  serv2.detach();
  
}



//-------------------------------------------------------------

void note3(){

  serv3.attach(serv3pin);
  
  for(serv3pos = 96; serv3pos >= serv3tuning; serv3pos -= 1){                                
    serv3.write(serv3pos);             
    delay(3);        
  }
  
  for(serv3pos = serv3tuning; serv3pos < 96; serv3pos += 1){                                
    serv3.write(serv3pos);             
    delay(3);        
  }
  
  serv3.detach();

}



//-------------------------------------------------------------

void note4(){
  
  serv4.attach(serv4pin);
  
  for(serv4pos = swingangle; serv4pos >= serv4tuning; serv4pos -= 1){                                
    serv4.write(serv4pos);             
    delay(3);        
  }
  
  for(serv4pos = serv4tuning; serv4pos < swingangle; serv4pos += 1){                                
    serv4.write(serv4pos);             
    delay(3);        
  }
  
  serv4.detach();

}



//-------------------------------------------------------------

void note5(){
  
  serv5.attach(serv5pin);
  
  for(serv5pos = 96; serv5pos >= serv5tuning; serv5pos -= 1){                                
    serv5.write(serv5pos);             
    delay(3);        
  }
  
  for(serv5pos = serv5tuning; serv5pos < 96; serv5pos += 1){                                
    serv5.write(serv5pos);             
    delay(3);        
  }
  
  serv5.detach();

}

//-------------------------------------------------------------

void note6(){     
  
  serv6.attach(serv6pin);
  
  for(serv6pos = 90; serv6pos >= serv6tuning; serv6pos -= 1){                                
    serv6.write(serv6pos);             
    delay(3);        
  }
  
  for(serv6pos = serv6tuning; serv6pos < 90; serv6pos += 1){                                
    serv6.write(serv6pos);             
    delay(3);        
  }
  
  serv6.detach();

}

//-------------------------------------------------------------

void note7(){
  
  serv7.attach(serv7pin);
  
  for(serv7pos = 96; serv7pos >= serv7tuning; serv7pos -= 1){                                
    serv7.write(serv7pos);             
    delay(3);        
  }
  
  for(serv7pos = serv7tuning; serv7pos < 96; serv7pos += 1){                                
    serv7.write(serv7pos);             
    delay(3);        
  }
  
  serv7.detach();

}


//-------------------------------------------------------------
1 Like

Oops, did I write Serial.begin()? I meant Serial1.begin().

USB serial (Serial) should not interfere :blush:

(I corrected the post accordingly)


But some general suggestions for your code:
You are doing pretty much the same thing in all your noteX() functions.
You might want to pull all of that together into one function (e.g. note(int nr)) and just use the nr parameter inside that one function (that also makes it easier to play your “songs” via a loop iterating over an array).
I haven’t done anything with the Servo lib so far, but the way I see you use it inside your functions (explicit attach() and detach() each time) you may only need one instance of servo.

And since you are calling your testsong() recursively, you will sooner or later run into a stackoverflow condition.
Maybe this is your actual problem.

@barry, I have to ask. How are you driving the servos - directly from the Photon pins or through a driver board/transistor of some kind?

Thanks @ScruffR . I hear you on the single note function I will do that for sure. As for the looping song, that was only to test but I will try one instance of Servo. Again everything was running fine on Arduino before porting it over.

@peekay123 Yes I am driving the servos directly off the Photon pins. Bad idea?

Thanks for your help everyone.

@barry, the Photon GPIO is only design to sink up to 20ma at 3.3v and with no limiting resistor, you could damage your device. Arduino pins can drive 30ma at 5v which probably makes the difference. You may want to think about getting a servo driver board.

Thank you @peekay123 I will definitely look into this!

@barry, also make sure you are running the lastest 0.4.4 firmware as I believe there were issues with PWM on the RX and TX pins in previous versions. :smile:

This is still happening on the latest firmware (0.4.7). Running this simple sketch:

Servo servo_RX;

void setup()
{
  servo_RX.attach(RX);    
}


void loop()
{
  for (int i i = 180; i <= 180; i++) {
    servo_RX.write(i);
    delay(20);
  }
}

And hooking the RX pin up to my scope demonstrates that the servo waveform on the RX pin is inverted. The same happens with TX; other pins operate as expected.

I’m assuming this is probably a simple fix; any timeline on it? I made a PCB with servo headers on RX and TX pins, which are useless as long as this issue persists!

To follow up, I can confirm that analogWrite works as expected; analogWrite(TX, 64); generates a 25% duty cycle square wave, not a 75% one.

As a workaround for the servo issue, you can do servo.attach(TX, 17600, 19456, 0, 180); and negate all your angles; it’s far from ideal, however!

Edit: Setting some pins to output causes this to start working non-inverted - for instance, pinMode(D7, OUTPUT); in setUp magically causes the waveform to uninvert itself.

Can you open a GitHub issue with a case that works, a case that doesn’t work and your workaround with pinMode? This is the best way get this fixed.

Maybe this issue needs reopening then?

Sounds like the fix for this issue will be in firmware 0.4.8 which has not been released yet. @nickjohnson hopefully the pinMode workaround works well enough that you can wait for the next firmware version release.

Thanks for spotting this. Based on the bug, though I assume that it's pure coincidence that pinMode fixes it; it's simply setting some memory on the stack into a different state that happens to be the one we need - so I wouldn't expect it to be at all reliable as a workaround. :confused:

Excellent that it'll be fixed in the next release, however.