Skip to content

Commit

Permalink
PoC implementation for the PCA9685
Browse files Browse the repository at this point in the history
  • Loading branch information
mzanetti committed Oct 4, 2021
1 parent 7f6ceb4 commit 8e043fb
Show file tree
Hide file tree
Showing 6 changed files with 234 additions and 0 deletions.
2 changes: 2 additions & 0 deletions i2cdevices/i2cdevices.pro
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ include(../plugins.pri)
HEADERS += \
integrationplugini2cdevices.h \
ads1115channel.h \
pca9685.h \
pi16adcchannel.h


SOURCES += \
integrationplugini2cdevices.cpp \
ads1115channel.cpp \
pca9685.cpp \
pi16adcchannel.cpp
22 changes: 22 additions & 0 deletions i2cdevices/integrationplugini2cdevices.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@

#include "pi16adcchannel.h"
#include "ads1115channel.h"
#include "pca9685.h"

#include <hardware/i2c/i2cmanager.h>

Expand Down Expand Up @@ -122,6 +123,17 @@ void IntegrationPluginI2CDevices::discoverThings(ThingDiscoveryInfo *info)
info->finish(Thing::ThingErrorNoError);
}

void IntegrationPluginI2CDevices::executeAction(ThingActionInfo *info)
{
if (info->thing()->thingClassId() == pca9685ThingClassId) {
I2CDevice *pca = m_i2cDevices.key(info->thing());
if (info->action().actionTypeId() == pca9685FrequencyActionTypeId) {
QByteArray command = "FREQ:" + info->action().paramValue(pca9685FrequencyActionFrequencyParamTypeId).toByteArray();
hardwareManager()->i2cManager()->writeData(pca, command);
}
}
}

void IntegrationPluginI2CDevices::setupThing(ThingSetupInfo *info)
{
if (info->thing()->thingClassId() == pi16ADCThingClassId) {
Expand Down Expand Up @@ -200,6 +212,16 @@ void IntegrationPluginI2CDevices::setupThing(ThingSetupInfo *info)
}
info->finish(Thing::ThingErrorNoError);
}

if (info->thing()->thingClassId() == pca9685ThingClassId) {
QString i2cPortName = info->thing()->paramValue(ads1115ThingI2cPortParamTypeId).toString();
int i2cAddress = info->thing()->paramValue(ads1115ThingI2cAddressParamTypeId).toInt();
Pca9685 *pca = new Pca9685(i2cPortName, i2cAddress, this);
hardwareManager()->i2cManager()->writeData(pca, "init");
m_i2cDevices.insert(pca, info->thing());
info->finish(Thing::ThingErrorNoError);
return;
}
}

void IntegrationPluginI2CDevices::thingRemoved(Thing *thing)
Expand Down
2 changes: 2 additions & 0 deletions i2cdevices/integrationplugini2cdevices.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ class IntegrationPluginI2CDevices: public IntegrationPlugin
void init() override;
void discoverThings(ThingDiscoveryInfo *info) override;

void executeAction(ThingActionInfo *info) override;

void setupThing(ThingSetupInfo *info) override;
void thingRemoved(Thing *thing) override;

Expand Down
59 changes: 59 additions & 0 deletions i2cdevices/integrationplugini2cdevices.json
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,65 @@
]
}
]
},
{
"id": "a17363f3-4e2a-4bfa-a937-bd7ef894b9d8",
"name": "pca9685",
"displayName": "PCA9685",
"thingClasses": [
{
"id": "ef5e814e-9078-4e46-8528-685f72239278",
"name": "pca9685",
"displayName": "PCA9685",
"createMethods": ["user"],
"paramTypes": [
{
"id": "9a8341a4-4d18-4832-a22b-9026136a6be5",
"name": "i2cPort",
"displayName": "I2C port",
"type": "QString"
},
{
"id": "7e26abbb-b5c9-4761-85f1-f86eb7a86312",
"name": "i2cAddress",
"displayName": "I2C address",
"type": "int"
},
{
"id": "e0fb6e69-b8b7-4c20-98fd-5352e5b85792",
"name": "channel",
"displayName": "Channel",
"type": "uint",
"minValue": 1,
"maxValue": 4
}
],
"stateTypes": [
{
"id": "8ae3dada-efcc-446f-bb37-8554c32324dc",
"name": "power",
"displayName": "Power",
"displayNameEvent": "Power changed",
"displayNameAction": "Set power",
"type": "bool",
"defaultValue": false,
"writable": true
},
{
"id": "a4a2de25-c0a9-43f3-8b86-d9f9cfd64b8d",
"name": "frequency",
"displayName": "Frequency",
"displayNameEvent": "Frequency changed",
"displayNameAction": "Set frequency",
"type": "uint",
"minValue": 1,
"maxValue": 1000,
"writable": true,
"defaultValue": 1
}
]
}
]
}
]
}
92 changes: 92 additions & 0 deletions i2cdevices/pca9685.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
#include "pca9685.h"

#include <unistd.h>

#include "extern-plugininfo.h"

#include <QThread>
#include <qmath.h>

Pca9685::Pca9685(const QString &portName, int address, QObject *parent) : I2CDevice(portName, address, parent)
{

}

bool Pca9685::writeData(int fd, const QByteArray &command)
{
if (command == "init") {
writeByte8(fd, MODE2, OUTDRV);
writeByte8(fd, MODE1, ALLCALL);
QThread::msleep(5000);
quint8 mode1 = readByte8(fd, MODE1);
mode1 = mode1 & ~SLEEP;
writeByte8(fd, MODE1, mode1);
QThread::msleep(5000);
qCInfo(dcI2cDevices())<<"PCA9685 Initialization: SUCCESS";
return true;
}

if (command.startsWith("PWM")) {
QList<QByteArray> parts = command.split(':');
quint8 channel = parts.at(1).toUInt();
quint16 on = parts.at(2).toUInt();
quint16 off = parts.at(3).toUInt();
writeByte8(fd, LED0_ON_L+4*channel, on & 0xFF);
writeByte8(fd, LED0_ON_H+4*channel, on >> 8);
writeByte8(fd, LED0_OFF_L+4*channel, off & 0xFF);
writeByte8(fd, LED0_OFF_H+4*channel, off >> 8);
return true;
}

if (command.startsWith("FREQ")) {
QList<QByteArray> parts = command.split(':');
quint16 frequency = parts.at(1).toUInt();
float prescaleval = 25000000.0; //25MHz
prescaleval /= 4096.0; //12-bit
prescaleval /= float(frequency);
prescaleval -= 1.0;
qInfo()<<"Setting PWM frequency to "<<frequency<<" hz";
qInfo()<<"Estimated pre-scale: "<<prescaleval;
int prescale = (int)qFloor(prescaleval + 0.5);
qInfo()<<"Final pre-scale: "<<prescale;
uint8_t oldmode = readByte8(fd, MODE1);
uint8_t newmode = (oldmode & 0x7F) | 0x10; // sleep
writeByte8(fd, MODE1, newmode); //go to sleep
writeByte8(fd, PRESCALE, prescale);
writeByte8(fd, MODE1, oldmode);
//time.sleep(0.005) # wait
QThread::msleep(5000);
writeByte8(fd, MODE1, oldmode | 0x80);
return true;
}

return false;
}

bool Pca9685::writeByte8(int fd, uint8_t registerAddress, uint8_t data)
{
uint8_t buffer[2] = {0};
buffer[0] = registerAddress & 0x00FF;
buffer[1] = data;

if (write(fd, buffer, 2) != 2){
qCDebug(dcI2cDevices())<<"I2C Transaction failed!";
return false;
}
return true;
}

uint8_t Pca9685::readByte8(int fd, uint8_t registerAddress)
{
uint8_t buffer[1] = {0};
buffer[0] = registerAddress & 0x00FF;
write(fd, buffer, 1);

uint8_t result_buffer[1] = {0};
if (read(fd, result_buffer, 1) != 1){
qDebug()<<"I2C Transaction failed!";
return 0;
}

return result_buffer[0];
}
57 changes: 57 additions & 0 deletions i2cdevices/pca9685.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#ifndef PCA9685_H
#define PCA9685_H

#include <QObject>

#include <hardware/i2c/i2cdevice.h>

#define MODE1 0x00
#define MODE2 0x01
#define SUBADR1 0x02
#define SUBADR2 0x03
#define SUBADR3 0x04
#define PRESCALE 0xFE
#define LED0_ON_L 0x06
#define LED0_ON_H 0x07
#define LED0_OFF_L 0x08
#define LED0_OFF_H 0x09
#define ALL_LED_ON_L 0xFA
#define ALL_LED_ON_H 0xFB
#define ALL_LED_OFF_L 0xFC
#define ALL_LED_OFF_H 0xFD
// Bits
#define RESTART 0x80
#define SLEEP 0x10
#define ALLCALL 0x01
#define INVRT 0x10
#define OUTDRV 0x04

#define PAN 0
#define TILT 1
#define FREQUENCY 50
#define CLOCKFREQ 25000000
#define PANOFFSET 1
#define PANSCALE 1.4
#define TILTOFFSET 30
#define TILTSCALE 1.43
#define PANMAX 270
#define PANMIN 90
#define TILTMAX 90
#define TILTMIN -45

class Pca9685 : public I2CDevice
{
Q_OBJECT
public:
explicit Pca9685(const QString &portName, int address, QObject *parent = nullptr);


bool writeData(int fd, const QByteArray &data) override;

private:
bool writeByte8(int fd, uint8_t registerAddress, uint8_t data);
uint8_t readByte8(int fd, uint8_t registerAddress);

};

#endif // PCA9685_H

0 comments on commit 8e043fb

Please sign in to comment.