Quadruped and the ServoCape...

Hello,

Here:

rmmod: ERROR: Module pca9685 is not currently loaded is the output of rmmod pca9685.

So, it seems that it was not loaded after all.

Seth

Hello Sir,

@benedict.hewson , okay about these ideas and me trying to get them put into source to handle the ServoCape. Thank you!

Outside of that idea:

Traceback (most recent call last):
  File "/home/debian/servo/./PCA_First.py", line 8, in <module>
    pwm_controller = Pca9685(i2c_bus, 0b111111)
  File "/home/debian/servo/PCA.py", line 20, in __init__
    self.write_reg( MODE1, 1 << 5 )  # initialize MODE1 register
  File "/home/debian/servo/PCA.py", line 29, in write_reg
    return self.write_regs( reg, [ value ] )
  File "/home/debian/servo/PCA.py", line 38, in write_regs
    return self.bus.write_i2c_block_data( self.addr, reg, values )
  File "/usr/local/lib/python3.9/dist-packages/smbus2/smbus2.py", line 643, in write_i2c_block_data
    ioctl(self.fd, I2C_SMBUS, msg)
OSError: [Errno 16] Device or resource busy

So far, this is the error encountered from my specific source:

#!/usr/bin/python3

from smbus2 import SMBus, i2c_msg
from time import sleep
from PCA import *

i2c_bus = SMBus('/dev/bone/i2c/2')
pwm_controller = Pca9685(i2c_bus, 0b111111)

x = pwm_controller

class Pca9685:
  def servo():
        angle = int(input("Please type a numerical value between 0 and 180: "))
        if angle >= 0 or angle <= 180:
            angle = x
        print ("Your angle, sir: %d " % x)

Pca9685.servo()

So far, even w/ all my changes to smbus2.py from their source and the current driver, I cannot get past errors. If you see an error that is not listed in my source, please let me know.

Also, here is the updated driver in python3 for handling random class-function-definitions.

from smbus2 import SMBus
from time import sleep
from pathlib import Path

# relevant registers
MODE1       = 0x00
MODE2       = 0x01
LED         = 0x06
ALL_LED     = 0xFA
PRE_SCALE   = 0xFE

set_pin = Path("/sys/class/gpio/gpio68/direction")
set_pin.write_text("low")

class Pca9685:
    def __init__( self, bus, addr ):
        self.addr = 0b111111 | addr
        self.bus = bus
        self.write_reg( MODE1, 1 << 5 )  # initialize MODE1 register
        sleep( 500e-6 )  # wait 500us to allow oscillator to power up
        set_pin.write_text("high")
        sleep(1)

    def read_reg( self, reg ):
        return self.read_regs( reg, 1 )[0]

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

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

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

    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 )

    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 ] )

Also…

@amf99 , thank you for your time in helping. Just a reminder, I have no PCA9685 driver installed on my machine at the time outside of the written/typed up driver by some nice person from IRC.

Seth

P.S. Also:

  1. I tried unlink /dev/bone/i2c/2 to test if the Compatibility Layer from GSoC was getting the way. This is not the issue so far.
  2. I found this page: linux/dev-interface.rst at 5.10 · beagleboard/linux · GitHub
  3. I will attempt to try this in C for testing.

As far as I can tell, smbus2.py does not play friendly so far w/ the Specification/Compatibility Layer.

This too!

lrwxrwxrwx 1 root root 10 Oct 10 01:46 /dev/bone/i2c/2 -> /dev/i2c-2

Would I need a udev rule to handle /dev/bone/i2c/2 or would I need to make a new user so that /dev/bone/i2c/2 can be in a new group?

Hello,

Okay…

So:

  1. It seems that ls -l /dev/bone/i2c/0 reports back root root as user and group. This does not matter.
  2. I have been using the wrong i2c address and file all along.
  3. Things work and the servo does NOT move.

So, I will keep trying. Thank you for the readers and participants in my short on this ServoCape.

Seth

Hi @silver2row

Are you using this cape ?

BeagleBoard.org BeagleBone Servo Cape 1 up from the bottom ?

I have just hooked up a Adafruit clone PCA9685 board to my BBB using the same I2C bus.

I initially set the address to the address for the above board using your code, but this didn’t work. Checking the signals I could see that the Python code was indeed sending data, but there was no ACK back. Wrong address.

Checking the schematic for my board, the default address is 64. Using this and everything seems to work just fine, although I haven’t tried setting a PWM, but I expect it to work.

I have run a few simple tests, see below.

On my BBB /dev/i2c-x are owned by root in group i2c

the file /lib/udev/rules.d/60-i2c-tools.rules sets this.

This is running Debian 10.3
Kernel is:
Linux beaglebone 4.19.94-ti-r42 #1buster SMP PREEMPT Tue Mar 31 19:38:29 UTC 2020 armv7l GNU/Linux

Python code

PCA96xx.py

from smbus2 import SMBus
from time import sleep

# registers!

Mode1        = 0x00
Mode2        = 0x01
LED          = 0x06
All_LED      = 0xFA
PRE_SCALE    = 0xFE

class PCA9685:
  def __init__(self, bus, addr):
    self.addr = addr
    self.bus = bus
    self.write_reg(Mode1, 1 << 5)
    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 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)

  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])

-----------------------------------------------------------------------

#!/usr/bin/python3

from PCA96xx import *
from time import sleep

i2c_bus = SMBus(2)
pwm_controller = PCA9685(i2c_bus, 64)

AUTO_INC    = (1 << 4)
SLEEP       = (1 << 4)

print( pwm_controller.read_regs(0,2))
print( pwm_controller.read_reg(254))

# can only change frequency in sleep mode
pwm_controller.write_reg(0, AUTO_INC | SLEEP)

# set 50Hz
pwm_controller.write_reg(254,121)

pwm_controller.write_reg(0, AUTO_INC )


print( pwm_controller.read_reg(254))

-------------------------------------------------

output from above

[32, 4]
30
121

Hope that helps.

1 Like

Hello Sir,

@benedict.hewson , I will test this later today. Thank you!

Seth

P.S. It seems my address is 34 on the BBB I have currently. I am using 5.10.x kernels from the newer images. And yes, one up from the bottom is the ServoCape in question.

I thought these files were located in /etc/udev/rules.d/? I will check /lib/.

Hello @benedict.hewson ,

Sir, I tried it and got returns on output. So, whatever you did, works. Still, w/ my current source, I am not making it work all that well.

Seth

P.S. I had to change some source in your specific entries in the code but for the most part, it works. Here is my output:

[9, 9]
0
0

I am not completely sure I understand what this means, though. So, off to learn more!

Hi @silver2row

It is impossible for the PCA9685 chip to be at address 34. I2C usually has a 7 bit address (can be 10 bits but not in this case) Address bit A6 if physycally set to a 1 in the chip, with bits A0-A5 being set externally. The address of the chip can therefore be anything from 64-127 (0x40 - 0x7f) with the default for the board at 127 (0x7f)

This can be changed by soldering across the pads on the pcb. No join and the address bit is a 1, shorted and the address bit becomes 0. This assume that the board matches the schematic.

The only thing in the above code you would need to change would be the address.

Hello,

Sorry. You were right. I changed 2 in the line, i2c_bus = SMBus(2) , to “/dev/i2c-0” and then pwm_controller = PCA9685(I2c_bus, 64) to pwm_controller = PCA9685(i2c_bus, 0x34).

Seth

P.S. I changed the address and the i2c_bus issue. I cannot get it to work unless I am using 0 and not 2 in the address space.

Hi @silver2row

According to the schematic, the cape is on I2C-2. Unless the naming of the I2C ports has changed I2C-0 can not work. Let me find a 5.1x BBB image and see if this is the case.

You do have this connected to a beaglebone black yes ? I know you are also playing around with the BBA1 64.

Again the address of the chip can not possibly be 0x34.
Are any of the address pads shorted out on your cape ?

I2C-0 is connected to the eeprom on the beaglebone board. No idea what address that is on. Also to the PMIC and HDMI chip. It is not connected to the expansion headers.

If you unplug the cape and run the program, do you still get the same results ?

There is of course an eeprom on the cape you are using, at address 0x54 according to the schematic.

Yes,

I am using the BBB for this effort w/ kernel 5.10.x. I have nothing shorted on the Cape.

I will test to see if using the board, this BBB, reports the same output w/out the Cape.

Seth

With nothing shorted on the cape, the address should be 0x7f as the resistors pull the address lines high… Might be worth checking with a DVM.

@benedict.hewson ,

I know you are right. I know that the EEPROM is for /dev/i2c-0, P9.19/20.

W/out using /dev/i2c-0, I am useless so far, i.e. as I cannot configure the board w/ the Cape to do anything that resembles movement. To this day, I cannot attest to using it w/ it working.

But…I believe it worked once upon a time w/ an older model Compatibility Layer from the .org.

Seth

P.S. I will test more and return service. I understand that /dev/i2c-0 is the connection from BBB to ServoCape but why do I receive output when using it?

Am I basically just outputting the EEPROM on the Cape w/ /dev/i2c-0 as my fd?

Using bone-debian-11.3-iot-armhf-2022-05-10-4gb.img
Linux BeagleBone 5.10.109-ti-r45 #1bullseye

Everything still works. Device is still on I2C-2 and the output from the program is as expected.

Are you using a .dtbo or a specific .dtb?

I could not get i2c-2 to work w/out error so far. I will try again. Maybe I broke my system somehow.

Seth

I just flashed the image to an sdcard. I have no cape fitted, just wiring the I2C-2 lines to the PCA9685 board.

It is using
am335x-boneblack-uboot-univ.dtb
BB-ADC-00A0.dtbo
BB-BONE-eMMC1-01-00A0.dtbo
BB-HDMI-TDA998x-00A0.dtbo

Have you got a serial console on the board.
U-Boot probes the cape e2prom addresses, maybe it is loading something.

No serial console from UART0 debug header.

I still have the Cape on it. I have a ssh terminal so far only.

Seth

But… sudo beagle-version reported in output this idea:

uboot_detected_capes=BBORG_SERVO

if you do
ls /proc/device-tree/chosen/overlays/

does it list any other overlays ?

Yes,

BB-ADC-00A0.kernel BB-BONE-eMMC1-01-00A0.kernel BB-HDMI-TDA998x-00A0.kernel BBORG_SERVO-00A2 name

Those are my files.

Seth

ok BBORG_SERVO-00A2 should be loading the pca9685 driver for address 0x7f

That is almost certainly why your code is not working as the address will be claimed by the driver.

I am not sure how the driver appears to the system.
You should have something in /sys/class/pwms for that chip

try

ls -l /sys/class/pwm/

lrwxrwxrwx 1 root root 0 Oct 11 09:06 pwmchip0 -> ../../devices/platform/ocp/48000000.interconnect/48000000.interconnect:segment@300000/48300000.target-module/48300000.epwmss/48300100.pwm/pwm/pwmchip0
lrwxrwxrwx 1 root root 0 Oct 11 09:06 pwmchip1 -> ../../devices/platform/ocp/48000000.interconnect/48000000.interconnect:segment@300000/48302000.target-module/48302000.epwmss/48302100.pwm/pwm/pwmchip1
lrwxrwxrwx 1 root root 0 Oct 11 09:06 pwmchip2 -> ../../devices/platform/ocp/48000000.interconnect/48000000.interconnect:segment@300000/48304000.target-module/48304000.epwmss/48304100.pwm/pwm/pwmchip2
lrwxrwxrwx 1 root root 0 Oct 11 09:06 pwmchip3 -> ../../devices/platform/ocp/48000000.interconnect/48000000.interconnect:segment@300000/48300000.target-module/48300000.epwmss/48300200.pwm/pwm/pwmchip3
lrwxrwxrwx 1 root root 0 Oct 11 09:06 pwmchip5 -> ../../devices/platform/ocp/48000000.interconnect/48000000.interconnect:segment@300000/48302000.target-module/48302000.epwmss/48302200.pwm/pwm/pwmchip5
lrwxrwxrwx 1 root root 0 Oct 11 09:06 pwmchip7 -> ../../devices/platform/ocp/48000000.interconnect/48000000.interconnect:segment@300000/48304000.target-module/48304000.epwmss/48304200.pwm/pwm/pwmchip7

That is that and this is ls -l /dev/bone/pwm/*

0:
total 0
lrwxrwxrwx 1 root root 29 Oct 11 09:06 a -> /sys/class/pwm/pwmchip3/pwm0/
lrwxrwxrwx 1 root root 29 Oct 11 09:06 b -> /sys/class/pwm/pwmchip3/pwm1/

1:
total 0
lrwxrwxrwx 1 root root 29 Oct 11 09:06 a -> /sys/class/pwm/pwmchip5/pwm0/
lrwxrwxrwx 1 root root 29 Oct 11 09:06 b -> /sys/class/pwm/pwmchip5/pwm1/

2:
total 0
lrwxrwxrwx 1 root root 29 Oct 11 09:06 a -> /sys/class/pwm/pwmchip7/pwm0/
lrwxrwxrwx 1 root root 29 Oct 11 09:06 b -> /sys/class/pwm/pwmchip7/pwm1/

Seth

P.S. I will test these instances and see if I can make them move w/ duty_cycle and period w/ enable.

Oh! I have no pwm group. Should this matter?