Quadruped and the ServoCape...

It doesn’t look like you have any extra PWM’s in there. I have 0,1,2,3,5 & 7 on my bare BBB

You could always try disabling the autoloading on the cape.

if you un-comment the following line from uEnv.txt
#disable_uboot_overlay_addr0=1

That should stop the overlay from loading I think. Then the program should work on addres 0x7f

1 Like

I am rebooting now. Pray for rain!

Seth

Okay There,

So…the address 0x7f w/ i2c-2 works now.

My source does nothing but it works. I will attempt to use some more functions from the driver now.

Seth

can you print out the register contents. If you run the same lines that I had, you should get the same outputs if you are talkiing with the chip.

I am not sure where the PCA9685 drivers puts it’s control points. The only reference I can find is for the Pi where it should show up in /sys/class/pwm

Ah you did ls /dev/bone/pwm

that probably won’t show up the extra PWMs
with the driver loaded you need to check /sys/class/pwm

Testing now…

[32, 4]
30
121

That is the output.

Seth

that is correct. You are talking to the chip correclty

I would say, if you are going to be controlling quite a few motors, it will probably be cleaner and easier to use the SMBUS driver and your class rather than the PCA9685 driver and modify lots of /sys/class/pwm/X duty cycle settings.

Okay,

Yep, 12 motors.

Seth

P.S. So, handling the files at /sys/class/pwm/X would be a better way to provide source than to use the driver. Okay.

No, I think your code is better than using the pca9685 driver. Your code is not using /sys/class/pwm

Also although it may not be important. Using your class, it would be possible to update consecutive motors in 1 write, assuming auto increment is set. You can configure the chip to only update the registers on the I2C stop, so all pwms update at the same time.

You can’t do that if using /sys/class/pwm interface.

1 Like

I got what you are saying.

First off, I did not write this driver. This is due to another human in life.

Secondly, I cannot get past this effort: TypeError: get_pwm() missing 1 required positional argument: 'output'

That error is due to not having output in the PCA9685.get_pwm/set_pwm(angle) source.

I put in output to that mix of source, and I receive an error about this: NameError: name 'output' is not defined.

I will keep testing.

Seth

Ok, I am not getting an error. Check you don’t have a typo in there

  def get_pwm(self, output):
    assert output in range(0, 16)
    reg = LED + 4 * output

    [on_l, on_h, off_l, off_h] = self.read_regs(reg, 4)
    on  = on_l | on_h << 8
    off = off_l | off_h << 8

    phase = on
    duty = (off - on) & 0xfff
    if off & 0x1000:
      duty = 0
    elif on & 0x1000:
      duty = 4096
    return (duty, phase)


running

for pwm in range (0,16):
    print("pwm {}:{}".format(pwm, pwm_controller.get_pwm(pwm) ) )

pwm 0:(0, 0)
pwm 1:(0, 0)
pwm 2:(0, 0)
pwm 3:(0, 0)
pwm 4:(0, 0)
pwm 5:(0, 0)
pwm 6:(0, 0)
pwm 7:(0, 0)
pwm 8:(0, 0)
pwm 9:(0, 0)
pwm 10:(0, 0)
pwm 11:(0, 0)
pwm 12:(0, 0)
pwm 13:(0, 0)
pwm 14:(0, 0)
pwm 15:(0, 0)



1 Like

You are right,

That is the exact same output I received. It took me a while to get it, i.e. as I made a typo.

I will try (0, 0) when putting in pwm_set and pwm_get and see if calling the class before these functions gets me anywhere.

Seth

@silver2row

I have been playing around with the python and hooked up a servo I found.
I have added a couple of functions ( set_pwm_frequency & set_servo_angle)
The code assumes that a 2ms pulse is 180 degrees. You would need to check what servos you are using to see what angle of rotation they have. My servos are only 90 degrees at 2ms.

Don’t forget gpio68 needs to be low for the PWM’s to work.

from smbus2 import SMBus
from time import sleep

# registers!

Mode1               = 0x00
Mode2               = 0x01
FIRST_SERVO         = 0x06
All_LED             = 0xFA
PRE_SCALE           = 0xFE
DEFAULT_FREQUENCY   = 200
AUTO_INC            = (1 << 5)
SLEEP               = (1 << 4)

class PCA9685:
  def __init__(self, bus, addr):
    self.addr = addr
    self.bus = bus
    self.write_reg(Mode1, AUTO_INC)
    self.frequency = DEFAULT_FREQUENCY
    self.bit_time = 1.0/ ( self.frequency*4096.0)
    sleep(500e-6)

  def read_reg(self, reg):
    assert reg in range(0, 256)
    return self.read_regs(reg, 1)[0]

  def write_reg(self, reg, value):
    return self.write_regs(reg, [value])

  def read_regs(self, reg, value):
    assert reg in range(0, 256)
    assert value in range(1, 257 - reg)
    return self.bus.read_i2c_block_data(self.addr, reg, value)

  def write_regs(self, reg, values):
    assert reg in range(0, 256)
    return self.bus.write_i2c_block_data(self.addr, reg, values)

  def set_pwm_frequency(self, frequency):
# the +9 is a bit of a hack to get very nearly 50Hz. For other frequencies this would be wrong
# at 60Hz it gives around 58Hz

    val = int( round( 25000000/( 4096*frequency ))-1)+9
    self.frequency = frequency
    self.bit_time = 1.0/ ( self.frequency*4096.0)
    self.write_reg(0, AUTO_INC | SLEEP)
    self.write_reg(254,val)
    self.write_reg(0, AUTO_INC )


  def set_servo_angle(self,servo,angle):
# offset stagers the PWMs so each channel starts at a different time
# you could just leave this at 0 if you wish

    offset = int(servo *200)
    pwm_high = int( (( (angle/180)*0.001 + 0.001 )/ self.bit_time))
    pwm_start = offset
    pwm_end = int( offset + pwm_high ) % 4096

    on_l  = pwm_start & 0xff
    on_h  = pwm_start >> 8
    off_l = pwm_end & 0xff
    off_h = pwm_end >> 8

    values=[on_l, on_h, off_l, off_h]
    reg = FIRST_SERVO + 4 * servo
    self.write_regs(reg, values)

  def get_pwm(self, output):
    assert output in range(0, 16)
    reg = FIRST_SERVO + 4 * output

    [on_l, on_h, off_l, off_h] = self.read_regs(reg, 4)
    on  = on_l | on_h << 8
    off = off_l | off_h << 8

    phase = on
    duty = (off - on) & 0xfff
    if off & 0x1000:
      duty = 0
    elif on & 0x1000:
      duty = 4096
    return (duty, phase)

  def set_pwm(self, output, duty, phase = 0):
    assert duty in range(0, 4097)
    assert phase in range(0, 4096)

    if output == "all":
      reg = All_LED
    else:
      assert output in range(0, 16)
      reg = LED +4 * output

    on  = phase
    off = (duty + phase) & 0xfff
    if duty == 0:
      off |= 0x1000
    elif duty == 4096:
      on |= 0x1000

    on_l  = on & 0xff
    on_h  = on >> 8
    off_l = off & 0xff
    off_h = off >> 8
    self.write_regs(reg, [on_l, on_h, off_l, off_h])


and some test code,
pwm_controller.set_pwm_frequency(50)

for l in range(0,5):
    pwm_controller.set_servo_angle(0,0)
    pwm_controller.set_servo_angle(15,0)
    sleep(1)
    pwm_controller.set_servo_angle(0,90)
    pwm_controller.set_servo_angle(15,90)
    sleep(1)
    pwm_controller.set_servo_angle(0,180)
    pwm_controller.set_servo_angle(15,180)
    sleep(1)

1 Like

Hello Sir,

@benedict.hewson , you fully outdid (yes, this is a word) yourself. The reason I state my last statement is b/c there was no way I could have known what to do w/out your ideas and knowledge. I am short term learner in this field, i.e. I do not have formal education in this dept.

Seth

P.S. I will test the source and report back. I looked over it and found that what you were posting earlier was an indication of things to come, e.g. either from you or my own doing. Thank you again! Testing will ensue! I had no ideas about needing to have to have the freq. and angle. I mean, I knew but I did not know, i.e. as my formal edu. was dedicated to junk. They fed me garbage and laughed and then when I did not die, they took my credits in a good curriculum and pooped on them. So, I was left fighting for credits that the transfer college deemed unworthy of their higher edu. scholastics. BoO!

Anyway, another story for another time. I will try to keep it professional (if possible). Oh! I moved dirt today w/ a shovel. School to work. Blah. I guess I need to piss someone off at times?

@benedict.hewson ,

So, I just reviewed the source, i.e. sort of earlier. Just sounds right.

Anyway, I see the frequency and angle. Neat!

Seth

P.S. I should have kept it short and sweet instead of garbage mongering. my bad.

@benedict.hewson ,

So…the source is not faulty like previously thought. I am just not getting output from the servo so far.

Power is on via barrel jack and the Cape is powered via the PWR/GND connector from the BBB.

Seth

P.S. I will keep on it, i.e. as I am trying here.

@benedict.hewson ,

Okay sir, I got the source to work. You were right about the source working but the servo does not move.

I have a BBB, ServoCape, barrel jack connector, and 5v from the BBB powering the ServoCape at its +/-, respectively.

Seth

P.S. I will keep trying other ideas. Sorry to harass you about this issue for so long.

So, I checked the power connector on the ServoCape and it is showing 5.10v and I think that is all it can take currently. Hmm. I will keep trying other ideas.

Is this right for i2c-2? https://docs.google.com/spreadsheets/d/1j134bDRMX4vjsWzh6Wi3Zsfh8llCCMIZowDYTpoRYvI/edit?pli=1#gid=1775283126

are you remembering to do.

echo out > /sys/class/gpio/gpio68/direction
echo 0 > /sys/class/gpio/gpio68/value

or put it in your code. By default the pin is an input and and it needs to be low for the servos to work.

Oh!

Okay. I will test it. I think I was setting direction and then just putting the gpio high for the time being.

Seth


#!/usr/bin/python3

from PCA_1 import *
from time import sleep
from pathlib import Path

i2c_bus = SMBus("/dev/bone/i2c/2a")
pwm_controller = Pca9685(i2c_bus, 0x7f)

B_PWM = Path("/sys/class/gpio/gpio68/direction")
B_PWM.write_text("out")
B_PWM = Path("/sys/class/gpio/gpio68/value")
B_PWM.write_text("0")

pwm_controller.set_pwm_frequency(50)
pwm_controller.set_servo_angle(2, 90)

def cort():
    for l in range(0, 180, 4):
        pwm_controller.set_servo_angle(2, 0)
        #pwm_controller.set_servo_angle(15, 0)
        sleep(2)

        pwm_controller.set_servo_angle(2, 90)
        #pwm_controller.set_servo_angle(15, 90)
        sleep(2)

        pwm_controller.set_servo_angle(2, 180)
        #pwm_controller.set_servo_angle(15, 180)
        sleep(2)
cort()

I keep waiting and nothing happens. I wrote to the gpio68 in sysfs. Should I use gpiod?

Seth

P.S. It seems that root and root are the user and group. See here:

lrwxrwxrwx 1 root root 0 Jan 1 2000 /sys/class/gpio/gpio68 -> ../../devices/platform/ocp/48000000.interconnect/48000000.interconnect:segment@100000/481ac000.target-module/481ac000.gpio/gpiochip2/gpio/gpio68

  1. I use config-pin on i2c-2 and I receive a Remote Error.
    a. OSError: [Errno 121] Remote I/O error
  2. I put config-pin back to default and nothing happens.
    a. blank output for eternity

Sir,

@benedict.hewson , would it matter if the smbus2 lib. was located in /usr/local/lib/ instead of the original location of .local/lib/?

Seth