1

I have been tasked with a little RC demo at our trade counter, and I need to control a few servo's and a ESC to run a motor.

I have been trying to fumble my way around to no real avail, I have used the Adafruit servo kit but i'm still trying to learn python myself.

The below code does work and initialized the ESC and moves the servos.... but I can't seem to find the correct part of the code that works and then using the same code after initilization to allow the speed to be controlled there after.

The hardware is a Pi 3+ using a Adafruit PCA9685 Hat

#!/usr/bin/env python

# PCA9685.py
# 2016-01-31
# Public Domain

import time

import pigpio

class PWM:

   """
   This class provides an interface to the I2C PCA9685 PWM chip.

   The chip provides 16 PWM channels.

   All channels use the same frequency which may be set in the
   range 24 to 1526 Hz.

   If used to drive servos the frequency should normally be set
   in the range 50 to 60 Hz.

   The duty cycle for each channel may be independently set
   between 0 and 100%.

   It is also possible to specify the desired pulse width in
   microseconds rather than the duty cycle.  This may be more
   convenient when the chip is used to drive servos.

   The chip has 12 bit resolution, i.e. there are 4096 steps
   between off and full on.
   """

   _MODE1         = 0x00
   _MODE2         = 0x01
   _SUBADR1       = 0x02
   _SUBADR2       = 0x03
   _SUBADR3       = 0x04
   _PRESCALE      = 0xFE
   _LED0_ON_L     = 0x06
   _LED0_ON_H     = 0x07
   _LED0_OFF_L    = 0x08
   _LED0_OFF_H    = 0x09
   _ALL_LED_ON_L  = 0xFA
   _ALL_LED_ON_H  = 0xFB
   _ALL_LED_OFF_L = 0xFC
   _ALL_LED_OFF_H = 0xFD

   _RESTART = 1<<7
   _AI      = 1<<5
   _SLEEP   = 1<<4
   _ALLCALL = 1<<0

   _OCH    = 1<<3
   _OUTDRV = 1<<2

   def __init__(self, pi, bus=1, address=0x40):

      self.pi = pi
      self.bus = bus
      self.address = address

      self.h = pi.i2c_open(bus, address)

      self._write_reg(self._MODE1, self._AI | self._ALLCALL)
      self._write_reg(self._MODE2, self._OCH | self._OUTDRV)

      time.sleep(0.0005)

      mode = self._read_reg(self._MODE1)
      self._write_reg(self._MODE1, mode & ~self._SLEEP)

      time.sleep(0.0005)

      self.set_duty_cycle(-1, 0)
      self.set_frequency(200)

   def get_frequency(self):

      "Returns the PWM frequency."

      return self._frequency

   def set_frequency(self, frequency):

      "Sets the PWM frequency."

      prescale = int(round(25000000.0 / (4096.0 * frequency)) - 1)

      if prescale < 3:
         prescale = 3
      elif prescale > 255:
         prescale = 255

      mode = self._read_reg(self._MODE1);
      self._write_reg(self._MODE1, (mode & ~self._SLEEP) | self._SLEEP)
      self._write_reg(self._PRESCALE, prescale)
      self._write_reg(self._MODE1, mode)

      time.sleep(0.0005)

      self._write_reg(self._MODE1, mode | self._RESTART)

      self._frequency = (25000000.0 / 4096.0) / (prescale + 1)
      self._pulse_width = (1000000.0 / self._frequency)

   def set_duty_cycle(self, channel, percent):

      "Sets the duty cycle for a channel.  Use -1 for all channels."

      steps = int(round(percent * (4096.0 / 100.0)))

      if steps < 0:
         on = 0
         off = 4096
      elif steps > 4095:
         on = 4096
         off = 0
      else:
         on = 0
         off = steps

      if (channel >= 0) and (channel <= 15):
         self.pi.i2c_write_i2c_block_data(self.h, self._LED0_ON_L+4*channel,
            [on & 0xFF, on >> 8, off & 0xFF, off >> 8])

      else:
         self.pi.i2c_write_i2c_block_data(self.h, self._ALL_LED_ON_L,
            [on & 0xFF, on >> 8, off & 0xFF, off >> 8])

   def set_pulse_width(self, channel, width):

      "Sets the pulse width for a channel.  Use -1 for all channels."

      self.set_duty_cycle(channel, (float(width) / self._pulse_width) * 100.0)

   def cancel(self):

      "Switches all PWM channels off and releases resources."

      self.set_duty_cycle(-1, 0)
      self.pi.i2c_close(self.h)

   def _write_reg(self, reg, byte):
      self.pi.i2c_write_byte_data(self.h, reg, byte)

   def _read_reg(self, reg):
      return self.pi.i2c_read_byte_data(self.h, reg)

if __name__ == "__main__":

   import time

   import PCA9685
   import pigpio

   pi = pigpio.pi()

   if not pi.connected:
      exit(0)

   pwm = PCA9685.PWM(pi) # defaults to bus 1, address 0x40

   pwm.set_frequency(50) # suitable for servos

   for dc in range(5, 11):
       pwm.set_duty_cycle(-1, dc) # -1 for all channels
       time.sleep(1)

   for pw in range(1000, 2050, 50):
       pwm.set_pulse_width(-1, pw) # -1 for all channels
       time.sleep(1)

   pwm.cancel()

   pi.stop()
  • What part of code are you looking for, exactly? "the correct part of the code that works" applies to all of it really. – Dmitry Grigoryev May 13 at 8:33
  • Have you first used i2cdetect command to make sure Rpi can detect the Hat? If yes, you can try the AdaFruit demo programs to first (1) dim a LED, (2) move 1 motor. – tlfong01 May 13 at 9:37
1

Preferably you should not change that code at all.

It is intended that you import the module into your own script.

If you see something like

if __name__ == "__main__": in a module that sort of implies the module should be imported and the author has added some example code in the module (after those lines).

I suggest you create a new file called my-test.py and add the example code, e.g.

#!/usr/bin/env python

import time

import PCA9685
import pigpio

pi = pigpio.pi()

if not pi.connected:
   exit(0)

pwm = PCA9685.PWM(pi) # defaults to bus 1, address 0x40

pwm.set_frequency(50) # suitable for servos

for dc in range(5, 11):
    pwm.set_duty_cycle(-1, dc) # -1 for all channels
    time.sleep(1)

for pw in range(1000, 2050, 50):
    pwm.set_pulse_width(-1, pw) # -1 for all channels
    time.sleep(1)

pwm.cancel()

pi.stop()

Use that as your template.

-1

Update 2019jun21hkt1040

The OP commented that he hopes to use PCA9685 to move servo and also ESC. There are many kinds of ESC, one popular kine is BLDC. For BLDC, we cannot use PCA9685 which is for weak servos. For strong BLDC motors, one suggestion is to use BLDC drive modules. I have been using the following for my 4WD:

bldc motor driver

AliExpress BLDC Driver Modules

How to Power and Control Brushless DC [BLDC] Motors - Digi-Key 2016-12-07

My BLDC Motor

bldc motor

Question

Pi3+ using Adafruit PCA9685 Hat and Adafruit Servo Kit to control a few servos, ...

Code does not work. How to fix?

Answer

I skimmed AdaFruit's tutorial and found that they suggest to do 3 things:

  1. Use i2cdetect -y 1 command to make sure Rpi can find the Hat.

  2. Run python example dim a LED.

  3. Rung python program example to move a servo.

i2cdetect

/ to continue, ...

References

AdaFruit Dimming LED using CircuitPython and the PWM/Servo HAT and Bonnet!

import board
import busio
import adafruit_pca9685
i2c = busio.I2C(board.SCL, board.SDA)
hat = adafruit_pca9685.PCA9685(i2c)

hat.frequency = 60
led_channel = hat.channels[0]
led_channel.duty_cycle = 0xffff
led_channel.duty_cycle = 1000
for i in range(0xffff):
    led_channel.duty_cycle = i
for i in range(0xffff, 0, -1):
    led_channel.duty_cycle = i

AdaFruit Python Program Standard Servo

import time
from adafruit_servokit import ServoKit

# Set channels to the number of servo channels on your kit.
# 8 for FeatherWing, 16 for Shield/HAT/Bonnet.
kit = ServoKit(channels=8)

kit.servo[0].angle = 180
kit.continuous_servo[1].throttle = 1
time.sleep(1)
kit.continuous_servo[1].throttle = -1
time.sleep(1)
kit.servo[0].angle = 0
kit.continuous_servo[1].throttle = 0

References

PCA9685 Datasheet

Arduino PCA9685 example

Adafruit 16-Channel PWM / Servo HAT for Raspberry Pi - Mini Kit US$17.50

Adafruit 16-Channel PWM / Servo HAT for Raspberry Pi Tutorial

How to Use 16 Channel PWM Controller With Raspberry Pi

AdaFruit I2C Tutorial

Adafruit_CircuitPython_PCA9685

AdaFruit CircuitPython helper library for the PWM/Servo FeatherWing, Shield and Pi HAT kits.

Adafruit 16-Channel PWM / Servo HAT for Raspberry Pi Schematics

Adafruit HAT for Raspberry Pi Documantation

Adafruit_pca9685 Python Library

AdaFruit PWM Library Reference

Dimming LEDs using CircuitPython and the PWM/Servo HAT and Bonnet!

  • esc's are not programmed like standard servos – Wolfgang Fahl Jun 20 at 19:33
  • @Wolfgang Fahl, My apologies. I must confess I did not read your question carefully. Let me see if I should improve my answer or put a warning at the beginning, or simply remove my answer. Many thanks for pointing out my silly mistake. – tlfong01 Jun 21 at 1:19
  • @Wolfgang Fahl, so I have updated my answer, at the top, for using BLDC driver as ESC. – tlfong01 Jun 21 at 2:50
  • The BLDC motors (with two power wires, three control wires) I have been playing might not be the same as your RC motors, though also BLDC, might behave same as brush DC motors, with only two connecting wires. So my BLDC motor controller might not be the same as yours. I apologize for an confusing. In case you have any reference circuits, I am happy to give more detailed explanations. – tlfong01 Jun 21 at 7:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.