├── imgs └── pca9685_leds.jpg ├── README.md ├── examples ├── servo.go └── demo.go └── device ├── pwm.go └── pca9685.go /imgs/pca9685_leds.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sromerotech/pca9685-golang/HEAD/imgs/pca9685_leds.jpg -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PCA9685-golang 2 | A Pca9685 board controller library made with Golang. 3 | 4 | ![PCA9685][image] 5 | 6 | ## Credits 7 | * Inspired by [Adafruit Python PCA9685 library][adafruit]. 8 | * With the help of: 9 | * [flipthatbit.net][i2c]: Interfacing I2C the easy way. 10 | * [www.raspberrypi-spy.co.uk][raspberrypi-spy]: How To Use A MCP23017... 11 | 12 | ## Collaborators 13 | People how has contributed to this repo: 14 | * [Ataboo][ataboo-link] 15 | 16 | 17 | [adafruit]: https://github.com/adafruit/Adafruit_Python_PCA9685 18 | [i2c]: http://flipthatbit.net/2011/04/interfacing-i2c-the-easy-way/ 19 | [image]: https://github.com/sergiorb/pca9685-golang/blob/master/imgs/pca9685_leds.jpg?raw=true 20 | [ataboo-link]: https://github.com/ataboo 21 | [raspberrypi-spy]: https://www.raspberrypi-spy.co.uk/2013/07/how-to-use-a-mcp23017-i2c-port-expander-with-the-raspberry-pi-part-1/ 22 | -------------------------------------------------------------------------------- /examples/servo.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/op/go-logging" 5 | "golang.org/x/exp/io/i2c" 6 | "log" 7 | "github.com/sergiorb/pca9685-golang/device" 8 | "time" 9 | ) 10 | 11 | const ( 12 | I2C_ADDR = "/dev/i2c-1" 13 | ADDR_01 = 0x40 14 | 15 | SERVO_CHANNEL = 15 16 | MIN_PULSE = 150 17 | MAX_PULSE = 650 18 | ) 19 | 20 | func main() { 21 | logger := logging.Logger{} 22 | dev, err := i2c.Open(&i2c.Devfs{Dev: I2C_ADDR}, ADDR_01) 23 | if err != nil { 24 | log.Fatal(err) 25 | } 26 | 27 | pca := device.NewPCA9685(dev, "Servo Controller", MIN_PULSE, MAX_PULSE, &logger) 28 | pca.Frequency = 60.0 29 | pca.Init() 30 | 31 | servo := pca.NewPwm(SERVO_CHANNEL) 32 | 33 | setPercentage(servo, 100.0) 34 | time.Sleep(2 * time.Second) 35 | setPercentage(servo, 0.0) 36 | time.Sleep(2 * time.Second) 37 | 38 | for i:=0; i<1000; i++ { 39 | setPercentage(servo, float32(i) / 10) 40 | time.Sleep(time.Millisecond * 10) 41 | } 42 | 43 | pca.SetAllPwm(0, 0) 44 | } 45 | 46 | func setPercentage(p *device.Pwm, percent float32) { 47 | pulseLength := int((MAX_PULSE - MIN_PULSE) * percent / 100 + MIN_PULSE) 48 | 49 | p.SetPulse(0, pulseLength) 50 | } 51 | -------------------------------------------------------------------------------- /device/pwm.go: -------------------------------------------------------------------------------- 1 | package device 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | ) 7 | 8 | type Pwm struct { 9 | pca *PCA9685 10 | pin int 11 | last_value float32 12 | } 13 | 14 | func (p *PCA9685) NewPwm(pin int) *Pwm { 15 | 16 | p.log.Info(fmt.Sprintf("Creating a new Pwm controller at pin %v from %v", pin, p.name)) 17 | 18 | return &Pwm{ 19 | pca: p, 20 | pin: pin, 21 | last_value: 0.0, 22 | } 23 | } 24 | 25 | func (pwm *Pwm) SetPercentage(percentage float32) error { 26 | 27 | if percentage < 0.0 || percentage > 100.0 { 28 | return errors.New(fmt.Sprintf("Percentage must be between 0.0 and 100.0. Got %v.", percentage)) 29 | } 30 | 31 | pwm.pca.log.Info(fmt.Sprintf("Setting pwm #%v to %v%% at \"%v\" device.", pwm.pin, percentage, pwm.pca.name)) 32 | 33 | pulseRange := pwm.pca.maxPulse - pwm.pca.minPulse 34 | 35 | value := ((float32(percentage) * float32(pulseRange)) / 100.0) + float32(pwm.pca.minPulse) 36 | 37 | 38 | pwm.pca.setServoPulse(pwm.pin, int(value)) 39 | 40 | return nil 41 | } 42 | 43 | func (pwm *Pwm) SetPulse(on int, off int) error { 44 | if on < 0 || on > off || off > 4096 { 45 | return errors.New(fmt.Sprintf( 46 | "On/Off (%d/%d) must be between 0 and %d", 47 | on, 48 | off, 49 | STEP_COUNT, 50 | )) 51 | } 52 | 53 | pwm.pca.setPwm(pwm.pin, on, off) 54 | 55 | return nil 56 | } 57 | -------------------------------------------------------------------------------- /examples/demo.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/op/go-logging" 5 | "github.com/sergiorb/pca9685-golang/device" 6 | "golang.org/x/exp/io/i2c" 7 | "os" 8 | "time" 9 | ) 10 | 11 | const ( 12 | I2C_ADDR = "/dev/i2c-1" 13 | ADDR_01 = 0x40 14 | MIN_PULSE = 0 15 | MAX_PULSE = 1000 16 | ) 17 | 18 | func init() { 19 | 20 | stderrorLog := logging.NewLogBackend(os.Stderr, "", 0) 21 | 22 | stderrorLogLeveled := logging.AddModuleLevel(stderrorLog) 23 | stderrorLogLeveled.SetLevel(logging.INFO, "") 24 | 25 | logging.SetBackend(stderrorLogLeveled) 26 | } 27 | 28 | func main() { 29 | 30 | var mainLog = logging.MustGetLogger("PCA9685 Demo") 31 | 32 | i2cDevice, err := i2c.Open(&i2c.Devfs{Dev: I2C_ADDR}, ADDR_01) 33 | defer i2cDevice.Close() 34 | 35 | if err != nil { 36 | 37 | mainLog.Error(err) 38 | 39 | } else { 40 | 41 | var deviceLog = logging.MustGetLogger("PCA9685") 42 | 43 | pca9685 := device.NewPCA9685(i2cDevice, "PWM Controller", MIN_PULSE, MAX_PULSE, deviceLog) 44 | 45 | pca9685.Init() 46 | 47 | pca9685.Demo([]int{0, 1, 2}) 48 | 49 | pwm00 := pca9685.NewPwm(0) 50 | pwm01 := pca9685.NewPwm(1) 51 | pwm02 := pca9685.NewPwm(2) 52 | 53 | _ = pwm00.SetPercentage(15.0) 54 | 55 | _ = pwm01.SetPercentage(50.0) 56 | 57 | _ = pwm02.SetPercentage(100.0) 58 | 59 | time.Sleep(2 * time.Second) 60 | 61 | pca9685.SwitchOff([]int{0, 1, 2}) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /device/pca9685.go: -------------------------------------------------------------------------------- 1 | package device 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "github.com/op/go-logging" 7 | "golang.org/x/exp/io/i2c" 8 | "math" 9 | "time" 10 | ) 11 | 12 | const ( 13 | MODE1 byte = 0x00 14 | MODE2 byte = 0x01 15 | PRESCALE byte = 0xFE 16 | LED0_ON_L byte = 0x06 17 | LED0_ON_H byte = 0x07 18 | LED0_OFF_L byte = 0x08 19 | LED0_OFF_H byte = 0x09 20 | ALL_LED_ON_L byte = 0xFA 21 | ALL_LED_ON_H byte = 0xFB 22 | ALL_LED_OFF_L byte = 0xFC 23 | ALL_LED_OFF_H byte = 0xFD 24 | OUTDRV byte = 0x04 25 | ALLCALL byte = 0x01 26 | SLEEP byte = 0x10 27 | BYTE byte = 0xFF 28 | 29 | DEFAULT_FREQ float32 = 1000.0 30 | OSC_FREQ float32 = 25000000.0 31 | STEP_COUNT float32 = 4096.0 32 | ) 33 | 34 | type PCA9685 struct { 35 | i2cBus *i2c.Device 36 | name string 37 | initiated bool 38 | minPulse int 39 | maxPulse int 40 | log *logging.Logger 41 | Frequency float32 42 | } 43 | 44 | func NewPCA9685(i2cDevice *i2c.Device, name string, minPulse int, maxPulse int, log *logging.Logger) *PCA9685 { 45 | 46 | log.Info(fmt.Sprintf("Creating a new PCA9685 device. Alias: %v", name)) 47 | 48 | return &PCA9685{ 49 | i2cBus: i2cDevice, 50 | name: name, 51 | initiated: false, 52 | minPulse: minPulse, 53 | maxPulse: maxPulse, 54 | log: log, 55 | Frequency: DEFAULT_FREQ, 56 | } 57 | } 58 | 59 | func (p *PCA9685) Init() { 60 | 61 | if p.initiated { 62 | 63 | p.log.Warning(fmt.Sprintf("Device \"%v\" already initiated!", p.name)) 64 | 65 | } else { 66 | p.log.Info(fmt.Sprintf("Initiating \"%v\" PCA9685 device", p.name)) 67 | 68 | p.SetAllPwm(0, 0) 69 | p.writeByte(MODE2, OUTDRV) 70 | p.writeByte(MODE1, ALLCALL) 71 | 72 | time.Sleep(5 * time.Millisecond) 73 | 74 | mode := p.readByte(MODE1) 75 | mode = mode & ^SLEEP 76 | p.writeByte(MODE1, mode) 77 | 78 | time.Sleep(5 * time.Millisecond) 79 | 80 | p.setPwmFrequency(p.Frequency) 81 | 82 | p.initiated = true 83 | } 84 | } 85 | 86 | func (p *PCA9685) SwitchOn(pwm []int) error { 87 | 88 | if !p.initiated { 89 | 90 | return errors.New(fmt.Sprintf("Device \"%v\"is not initiated!", p.name)) 91 | } 92 | 93 | for i := 0; i < len(pwm); i++ { 94 | 95 | p.log.Info(fmt.Sprintf("Switching on pwm #%v", pwm[i])) 96 | 97 | p.setServoPulse(pwm[i], p.maxPulse) 98 | } 99 | 100 | return nil 101 | } 102 | 103 | func (p *PCA9685) SwitchOff(pwm []int) error { 104 | 105 | if !p.initiated { 106 | 107 | return errors.New(fmt.Sprintf("Device \"%v\"is not initiated!", p.name)) 108 | } 109 | 110 | for i := 0; i < len(pwm); i++ { 111 | 112 | p.log.Info(fmt.Sprintf("Switching off pwm #%v", pwm[i])) 113 | 114 | p.setServoPulse(pwm[i], p.minPulse) 115 | } 116 | 117 | return nil 118 | } 119 | 120 | func (p *PCA9685) FadeInOut(pwmNumber int) error { 121 | 122 | if !p.initiated { 123 | 124 | return errors.New(fmt.Sprintf("Device \"%v\"is not initiated!", p.name)) 125 | } 126 | 127 | p.log.Info(fmt.Sprintf("Fading pwm #%v from %v to %v pulse...", pwmNumber, p.minPulse, p.maxPulse)) 128 | 129 | for i := p.minPulse; i < p.maxPulse; i++ { 130 | 131 | p.setServoPulse(pwmNumber, i) 132 | } 133 | 134 | for i := p.maxPulse; i > p.minPulse; i-- { 135 | 136 | p.setServoPulse(pwmNumber, i) 137 | } 138 | 139 | return nil 140 | } 141 | 142 | func (p *PCA9685) Wink(pwm []int, times int, speed int) { 143 | 144 | p.log.Info(fmt.Sprintf("Winking pwm's: %v, %v times at speed %vms", pwm, times, speed)) 145 | 146 | for i := 0; i < times; i++ { 147 | 148 | p.SwitchOn(pwm) 149 | 150 | var halfSpeed time.Duration = time.Duration(speed/2) * time.Millisecond 151 | 152 | time.Sleep(halfSpeed) 153 | 154 | p.SwitchOff(pwm) 155 | 156 | time.Sleep(halfSpeed) 157 | } 158 | } 159 | 160 | func (p *PCA9685) Demo(pwm []int) { 161 | 162 | for i := 0; i < len(pwm); i++ { 163 | 164 | p.FadeInOut(pwm[i]) 165 | } 166 | 167 | p.Wink(pwm, 4, 800) 168 | } 169 | 170 | func (p *PCA9685) setPwmFrequency(freqHz float32) { 171 | 172 | preScaleValue := OSC_FREQ // 25MHz 173 | preScaleValue /= STEP_COUNT 174 | preScaleValue /= freqHz 175 | preScaleValue -= 1.0 176 | 177 | p.log.Debug(fmt.Sprintf("Setting PWM frequency to %v Hz", freqHz)) 178 | p.log.Debug(fmt.Sprintf("Estimated pre-scale: %v", preScaleValue)) 179 | 180 | preScale := int(math.Floor(float64(preScaleValue + 0.5))) 181 | 182 | p.log.Debug(fmt.Sprintf("Final pre-scale: %v", preScale)) 183 | 184 | oldMode := p.read8(MODE1) 185 | 186 | newMode := (oldMode & 0x7F) | 0x10 187 | 188 | p.write8(MODE1, newMode) 189 | p.write8(PRESCALE, preScale) 190 | p.write8(MODE1, oldMode) 191 | 192 | time.Sleep(5 * time.Millisecond) 193 | 194 | p.write8(MODE1, oldMode| 0x80) 195 | } 196 | 197 | func (p *PCA9685) setServoPulse(pwmNumber int, pulse int) { 198 | 199 | var pulseLength float32 = 1000000 200 | 201 | pulseLength /= float32(60) 202 | 203 | //p.log.Debug(fmt.Sprintf("%vus per period", pulseLength)) 204 | 205 | pulseLength /= STEP_COUNT 206 | 207 | //p.log.Debug(fmt.Sprintf("%vus per bit", pulseLength)) 208 | 209 | pulseF := float32(pulse) 210 | 211 | pulseF /= pulseLength 212 | 213 | p.log.Debug(fmt.Sprintf("Setting pwm #%v to pulse %v", pwmNumber, pulseF)) 214 | 215 | p.setPwm(pwmNumber, 0, int(pulseF)) 216 | } 217 | 218 | func (p *PCA9685) SetAllPwm(on int, off int) { 219 | p.write8(ALL_LED_ON_L, on) 220 | p.write8(ALL_LED_ON_H, on >> 8) 221 | p.write8(ALL_LED_OFF_L, off) 222 | p.write8(ALL_LED_OFF_H, off >> 8) 223 | } 224 | 225 | func (p *PCA9685) setPwm(pwm int, on int, off int) { 226 | p.write8(LED0_ON_L+byte(4)*byte(pwm), on) 227 | p.write8(LED0_ON_H+byte(4)*byte(pwm), on >> 8) 228 | p.write8(LED0_OFF_L+byte(4)*byte(pwm), off) 229 | p.write8(LED0_OFF_H+byte(4)*byte(pwm), off >> 8) 230 | } 231 | 232 | func (p *PCA9685) write8(reg byte, intVal int) { 233 | byteVal := byte(intVal) & BYTE 234 | 235 | p.writeByte(reg, byteVal) 236 | } 237 | 238 | func (p *PCA9685) writeByte(reg byte, byteVal byte) { 239 | err := p.i2cBus.WriteReg(reg, []byte{byteVal}) 240 | if err != nil { 241 | p.log.Error(fmt.Sprintf("Failed to read from register %#x.", reg)) 242 | p.log.Error(err.Error()) 243 | } 244 | 245 | p.log.Debug(fmt.Sprintf("Wrote %#x to register %#x.", byteVal, reg)) 246 | } 247 | 248 | func (p *PCA9685) read8(reg byte) int { 249 | byteVal := p.readByte(reg) 250 | 251 | return int(byteVal) 252 | } 253 | 254 | func (p *PCA9685) readByte(reg byte) byte { 255 | buf := make([]byte, 1) 256 | err := p.i2cBus.ReadReg(reg, buf) 257 | if err != nil { 258 | p.log.Error(fmt.Sprintf("Failed to read from register %#x.", reg)) 259 | p.log.Error(err.Error()) 260 | } 261 | 262 | p.log.Debug(fmt.Sprintf("Read %#x from register %#x.", buf[0], reg)) 263 | 264 | return buf[0] 265 | } 266 | --------------------------------------------------------------------------------