I encountered very interesting problem while using driver for servo of my robot chasis. As TensorFlow requires python 3.7 I had to rewrite our steering robot program to use python3.7. It seemed to be easy as peace of cake, but then to my surprise I discovered that motor doesn’t work at all in python3.7 and in python2.6 works.
The trick was that somehow python 2.6 wrongly calculates this equation:
int(pulse * (4096 / 100))
if pulse=4096 then it gives output 4000. Python3.7 calculates it correctly, but correct value 4096 provided to chosen channel on our servo is too high.
Below I attach proper driver for PCA9685 in python3.7:
#!/usr/bin/python3.7
import time
import math
import smbus
# ============================================================================
# Raspi PCA9685 16-Channel PWM Servo Driver
# ============================================================================
class PCA9685:
# Registers/etc.
__SUBADR1 = 0x02
__SUBADR2 = 0x03
__SUBADR3 = 0x04
__MODE1 = 0x00
__PRESCALE = 0xFE
__LED0_ON_L = 0x06
__LED0_ON_H = 0x07
__LED0_OFF_L = 0x08
__LED0_OFF_H = 0x09
__ALLLED_ON_L = 0xFA
__ALLLED_ON_H = 0xFB
__ALLLED_OFF_L = 0xFC
__ALLLED_OFF_H = 0xFD
def __init__(self, address, debug=False):
self.bus = smbus.SMBus(1)
self.address = address
self.debug = debug
if (self.debug):
print("Reseting PCA9685")
self.write(self.__MODE1, 0x00)
def write(self, reg, value):
"Writes an 8-bit value to the specified register/address"
self.bus.write_byte_data(self.address, reg, value)
if (self.debug):
print("I2C: Write 0x%02X to register 0x%02X" % (value, reg))
def read(self, reg):
"Read an unsigned byte from the I2C device"
result = self.bus.read_byte_data(self.address, reg)
if (self.debug):
print("I2C: Device 0x%02X returned 0x%02X from reg 0x%02X" % (self.address, result & 0xFF, reg))
return result
def setPWMFreq(self, freq):
"Sets the PWM frequency"
prescaleval = 25000000.0 # 25MHz
prescaleval /= 4096.0 # 12-bit
prescaleval /= float(freq)
prescaleval -= 1.0
if (self.debug):
print("Setting PWM frequency to %d Hz" % freq)
print("Estimated pre-scale: %d" % prescaleval)
prescale = math.floor(prescaleval + 0.5)
if (self.debug):
print("Final pre-scale: %d" % prescale)
oldmode = self.read(self.__MODE1)
newmode = (oldmode & 0x7F) | 0x10 # sleep
self.write(self.__MODE1, newmode) # go to sleep
self.write(self.__PRESCALE, int(math.floor(prescale)))
self.write(self.__MODE1, oldmode)
time.sleep(0.005)
self.write(self.__MODE1, oldmode | 0x80)
def setPWM(self, channel, on, off):
"Sets a single PWM channel"
self.write(self.__LED0_ON_L + 4*channel, on & 0xFF)
self.write(self.__LED0_ON_H + 4*channel, on >> 8)
self.write(self.__LED0_OFF_L + 4*channel, off & 0xFF)
self.write(self.__LED0_OFF_H + 4*channel, off >> 8)
if (self.debug):
print("channel: %d LED_ON: %d LED_OFF: %d" % (channel,on,off))
def setDutycycle(self, channel, pulse):
#self.setPWM(channel, 0, int(pulse * (4096 / 100)))
if pulse==100:
self.setPWM(channel, 0,4000)
else:
self.setPWM(channel, 0,0)
def setLevel(self, channel, value):
if (value == 1):
self.setPWM(channel, 0, 4095)
else:
self.setPWM(channel, 0, 0)
#pwm = PCA9685(0x40, debug=True)
#pwm.setPWMFreq(50)
#pwm.setDutycycle(0,100)
#pwm.setLevel(1,0)
#pwm.setLevel(2,1)