I wrote to Mats earlier, so he has some background on this already :)
I think I may have uncovered a small bug in the Adafruit16CServoDriver MRL service.
See the corection that I've made in bold below in my local Adafruit16CServoDriver java build for details
The bug becomes apparent when using the servo slider in the MRL servo GUI
Try it and see...before and after the fix below
public void setServo(Integer pin, Integer pulseWidthOff) {
// since pulseWidthOff can be larger than > 256 it needs to be
// sent as 2 bytes
/*
* log.debug(
* String.format("setServo %s deviceAddress %s pin %s pulse %s", pin,
* deviceAddress, pin, pulseWidthOff)); byte[] buffer = { (byte)
* (PCA9685_LED0_OFF_L + (pin * 4)), (byte) (pulseWidthOff & 0xff),
* (byte) (pulseWidthOff >> 8) }; controller.i2cWrite(this,
* Integer.parseInt(deviceBus), Integer.decode(deviceAddress), buffer,
* buffer.length);
*/
//setPWM(pin, 4095, pulseWidthOff); edited out by Acapulco Rolf 26th May 2017, should be as below
// setPWM(pin, 4095, pulseWidthOff) // no worky code
setPWM(pin, 0, pulseWidthOff); // This is worky code
}
Making the change shown and commented above corrects the issue
I've also "reworked" the calculations for the PCA9685 pre-scale calculation in the Adafruit16CServoDriver as below.
I've posted a spreadsheet here to show the full pre-scale range across all achievable PCA9685 frequency points
Note that to get close to 330Hz (MG PDI 6221MG servo working frequency), set the frequency of the PCA9685 driver to 320Hz in order for the frequency pre-scale value to come in as close as possible under 330Hz.
The PCA9685 will hit 320Hz, given the precision of the pre-scale value (one byte)
I've measured this on an oscilloscope to prove the calculation
This is the calculation I now have as worky in my local PCA9685 MRL service Java code:
public void setPWM(Integer pin, Integer pulseWidthOn, Integer pulseWidthOff) {
.
.
.
.
// Acapulco Rolf pre-scale calculation
prescale_value = Math.round(1.00663996 * osc_clock / precision / hz) - 1;
// Acapulco Rolf correction for PCA9685 frequency overshoot
// See issue #11 here: https://github.com/adafruit/Adafruit-PWM-Servo-Driver-Library/issues/11
// through the develper used a different correction different factor in his case
// and also see here: https://forums.adafruit.com/viewtopic.php?f=19&t=72554
prescale_value = (float) (Math.round(prescale_value * 1.06));
prescale_value = (int) prescale_value;
Yes, I could do this in one line, but I broke out the calculation to help me debug for now :)
// example PCA9685 pre-scale values and target frequencies
// prescale_value = 106; // for PCA9685 @ 60Hz
// prescale_value = 17; // for PCA9685 @356Hz
// prescale_value = 18; // for PCA9685 @337Hz
// prescale_value = 19; // for PCA9685 @320Hz
# See Acapulco Rolf PCA9685 spreadsheet for details
# https://docs.google.com/ spreadsheets/d/1caMWj5LFj_ 2AWXzwLSECo4na8hXtDhHOYy0aozw5 AzM/edit?usp=sharing
This following is worky for me now with my PDI 6221 MG servos running at their stated frequency of 330Hz:
servo2.moveTo(50)
time.sleep(1)
servo2.moveTo(55)
time.sleep(1)
servo2.moveTo(60)
time.sleep(1)
servo2.moveTo(65)
time.sleep(1)
servo2.moveTo(70)
time.sleep(1)
servo2.moveTo(75)
Of course, all this still requires hard coding servo max/min (SERVOMAX and SERVOMIN) in the MRL Adafruit16CServoDriver service, because of the logic below in servoMoveTo()
public void servoMoveTo(ServoControl servo) {
ServoData servoData = servoMap.get(servo.getName());
if (!pwmFreqSet) {
setPWMFreq(servoData.pin, defaultPwmFreq);
}
if (servoData.isEnergized) {
// Move at max speed
if (servoData.velocity == -1) {
log.debug("Ada move at max speed");
servoData.currentOutput = servo.getTargetOutput();
servoData.targetOutput = servo.getTargetOutput();
log.debug(String.format(" servoWrite %s deviceAddress %s targetOutput %f", servo.getName(), deviceAddress, servo.getTargetOutput()));
int pulseWidthOff = SERVOMIN + (int) (servo.getTargetOutput() * (int) ((float) SERVOMAX - (float) SERVOMIN) / (float) (180));
So.....I've now removed the hardcoded servo constants in my local MRL dev build
I now pass in servo frequency, maximum rotation angle, and minimum and maximum servo pulse width as parameters of the servo.attach() method, in order to define these servo attributes at run time
edit:
I note that the microsecondsToDegree method also hardcodes servo end points (eek)
public double microsecondsToDegree(int microseconds) {
if (microseconds <= 180)
return microseconds;
return (double) (microseconds - 544) * 180 / (2400 - 544);
}
With my code changes of the servo class, these endpoints are now user definable at runtime :)
@Kwatters/@Grog/Mats and the gang, any thoughts...?
By the way, I'd like to display these new servo attributes in the MRL servo GUI.
Are you able point me at where I can get to that specific code in the mrl servo GUI source code
I've got the MRL development branch compiled locally in Eclipse
JX PDI 6221 MG servos worky in MRL @ 330Mhz
Hi acapulco Rolf I don't
Hi acapulco Rolf
I don't think the method microsecondsToDegree(int) is used anymore. I think it's a fossil from my first implementation to use microseconds for servo position. Now the conversion to microsecond happen in the Arduino class and better that way, and should allow you to use other value for other servo controller.
PCA9685 driver
Hey calamity
Good to know :)
@Mats, a further update for you
Hi I made the suggested
Hi
I made the suggested change from
There is another possible bug
I've noted that when shutting down MRL on a Raspberry Pi, servos controlled by the AdafruitPCA9685 service that were not detached manually first will still be driven until the power is cycled or the MRL program is run again and the servos attached and then detached.
There does not appear to be detach sequence built into MRL when shutting down.
The arduinos apeare to detach themselves when they loose coms with the MRL service.
This is just an observation.
Ray
Servo detach
Yes. It's a problem. I think MRL has methods to make a graceful shutdown, but them are not implementera in all Services.