Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: PoC implementation for the PCA9685 #485

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
51 changes: 51 additions & 0 deletions i2cdevices/integrationplugini2cdevices.json
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,57 @@
]
}
]
},
{
"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"
}
],
"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(5);
quint8 mode1 = readByte8(fd, MODE1);
mode1 = mode1 & ~SLEEP;
writeByte8(fd, MODE1, mode1);
QThread::msleep(5);
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(5);
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