From dbea7acbb23c727f0c31c75349b8cc0c11428e38 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Tue, 28 Apr 2020 17:17:28 +0200 Subject: [PATCH] Add an I2C hardware resource --- .../hardware/i2c/i2cmanagerimplementation.cpp | 278 ++++++++++++++++++ .../hardware/i2c/i2cmanagerimplementation.h | 95 ++++++ .../hardwaremanagerimplementation.cpp | 9 + libnymea-core/hardwaremanagerimplementation.h | 10 +- libnymea-core/libnymea-core.pro | 2 + libnymea/hardware/i2c/i2cdevice.cpp | 118 ++++++++ libnymea/hardware/i2c/i2cdevice.h | 61 ++++ libnymea/hardware/i2c/i2cmanager.cpp | 78 +++++ libnymea/hardware/i2c/i2cmanager.h | 60 ++++ libnymea/hardwaremanager.h | 2 + libnymea/libnymea.pro | 4 + libnymea/loggingcategories.cpp | 1 + libnymea/loggingcategories.h | 1 + 13 files changed, 711 insertions(+), 8 deletions(-) create mode 100644 libnymea-core/hardware/i2c/i2cmanagerimplementation.cpp create mode 100644 libnymea-core/hardware/i2c/i2cmanagerimplementation.h create mode 100644 libnymea/hardware/i2c/i2cdevice.cpp create mode 100644 libnymea/hardware/i2c/i2cdevice.h create mode 100644 libnymea/hardware/i2c/i2cmanager.cpp create mode 100644 libnymea/hardware/i2c/i2cmanager.h diff --git a/libnymea-core/hardware/i2c/i2cmanagerimplementation.cpp b/libnymea-core/hardware/i2c/i2cmanagerimplementation.cpp new file mode 100644 index 00000000..5b029f52 --- /dev/null +++ b/libnymea-core/hardware/i2c/i2cmanagerimplementation.cpp @@ -0,0 +1,278 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2020, nymea GmbH +* Contact: contact@nymea.io +* +* This file is part of nymea. +* This project including source code and documentation is protected by +* copyright law, and remains the property of nymea GmbH. All rights, including +* reproduction, publication, editing and translation, are reserved. The use of +* this project is subject to the terms of a license agreement to be concluded +* with nymea GmbH in accordance with the terms of use of nymea GmbH, available +* under https://nymea.io/license +* +* GNU Lesser General Public License Usage +* Alternatively, this project may be redistributed and/or modified under the +* terms of the GNU Lesser General Public License as published by the Free +* Software Foundation; version 3. This project is distributed in the hope that +* it will be useful, but WITHOUT ANY WARRANTY; without even the implied +* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this project. If not, see . +* +* For any further details and any questions please contact us under +* contact@nymea.io or see our FAQ/Licensing Information on +* https://nymea.io/license/faq +* +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "i2cmanagerimplementation.h" + +#include "hardware/i2c/i2cdevice.h" +#include "loggingcategories.h" + +#include +#include + +#include +#include +#include + +namespace nymeaserver { + +I2CManagerImplementation::I2CManagerImplementation(QObject *parent) : I2CManager(parent) +{ + m_pollTimer.setInterval(200); + m_pollTimer.setSingleShot(true); + connect(&m_pollTimer, &QTimer::timeout, this, &I2CManagerImplementation::nextCycle); +} + +I2CManagerImplementation::~I2CManagerImplementation() +{ + m_watcher.waitForFinished(); +} + +QStringList nymeaserver::I2CManagerImplementation::availablePorts() const +{ + return QDir("/sys/class/i2c-adapter/").entryList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name); +} + +QList nymeaserver::I2CManagerImplementation::scanRegisters(const QString &portName) +{ + QList ret; + + QList portsToBeScanned = {portName}; + if (portName.isEmpty()) { + portsToBeScanned = availablePorts(); + } + + m_mutex.lock(); + + foreach (const QString &p, portsToBeScanned) { + QFile f("/dev/" + p); + if (!f.open(QFile::ReadWrite)) { + qCWarning(dcI2C()) << "Failed to open I2C port" << p << "for scanning"; + continue; + } + + for (int address = 0x03; address <= 0x77; address++) { + // First check if selecting the slave address is possible at all + if (ioctl(f.handle(), I2C_SLAVE, address) >= 0) { + char probe = 0x00; + long res = 0; + // This is how the kernels i2cdetect scans: + // Try to read from address 0x30 - 0x35 and 0x50 to 0x5F and write to the others. + if ((address >= 0x30 && address <= 0x37) + || (address >= 0x50 && address <= 0x5F)) { + res = read(f.handle(), &probe, 1); + } else { + res = write(f.handle(), &probe, 1); + } + if (res == 1) { + qCDebug(dcI2C()) << QString("Found slave device at address 0x%1").arg(address, 0, 16); + I2CScanResult result; + result.portName = p; + result.address = address; + ret.append(result); + } + } + } + } + m_mutex.unlock(); + return ret; +} + +bool I2CManagerImplementation::open(I2CDevice *i2cDevice) +{ + if (m_openFiles.contains(i2cDevice)) { + qCWarning(dcI2C()) << "I2C device" << i2cDevice << "already opened."; + return false; + } + + QString fileName = "/dev/" + i2cDevice->portName(); + + foreach (I2CDevice *d, m_openFiles.keys()) { + if (d->portName() == i2cDevice->portName()) { + // Another I2CDevice opened this file already. We'll hook into that. + m_mutex.lock(); + m_openFiles.insert(i2cDevice, m_openFiles.value(d)); + m_mutex.unlock(); + return true; + } + } + + if (!QFile::exists(fileName)) { + qCWarning(dcI2C()) << "The I2C port does not exist:" << i2cDevice->portName(); + return false; + } + + QFile *file = new QFile("/dev/" + i2cDevice->portName(), this); + if (!file->open(QFile::ReadWrite)) { + qCWarning(dcI2C()) << "Error opening I2C port" << i2cDevice << "Error:" << file->errorString(); + delete file; + return false; + } + + m_mutex.lock(); + m_openFiles.insert(i2cDevice, file); + m_mutex.unlock(); + return true; +} + +bool I2CManagerImplementation::startReading(I2CDevice *i2cDevice, int interval) +{ + QMutexLocker locker(&m_mutex); + + if (!m_openFiles.contains(i2cDevice)) { + qCWarning(dcI2C()) << "I2CDevice not open. Cannot start reading."; + return false; + } + qCDebug(dcI2C()) << "Starting to poll I2C device" << i2cDevice; + ReadingInfo readingInfo; + readingInfo.interval = interval; + m_readers.insert(i2cDevice, readingInfo); + + if (!m_pollTimer.isActive()) { + m_pollTimer.start(); + } + return true; +} + + +void I2CManagerImplementation::stopReading(I2CDevice *i2cDevice) +{ + QMutexLocker locker(&m_mutex); + m_readers.remove(i2cDevice); + + if (m_readers.count() == 0) { + m_pollTimer.stop(); + } +} + +bool I2CManagerImplementation::writeData(I2CDevice *i2cDevice, const QByteArray &data) +{ + m_writeQueueMutex.lock(); + WritingInfo info; + info.device = i2cDevice; + info.data = data; + m_writeQueue.append(info); + m_writeQueueMutex.unlock(); + return true; +} + +void I2CManagerImplementation::close(I2CDevice *i2cDevice) +{ + bool isInUse = false; + m_mutex.lock(); + if (m_readers.contains(i2cDevice)) { + isInUse = true; + } + m_mutex.unlock(); + + if (isInUse) { + stopReading(i2cDevice); + } + + int refCount = 0; + foreach (I2CDevice* d, m_openFiles.keys()) { + if (d->portName() == i2cDevice->portName()) { + refCount++; + } + } + if (refCount == 0) { + + m_mutex.lock(); + QFile *f = m_openFiles.take(i2cDevice); + m_mutex.unlock(); + + f->close(); + f->deleteLater(); + } +} + +void I2CManagerImplementation::nextCycle() +{ + QFuture future = QtConcurrent::run([this](){ + // Copy the write queue to open it up as fast as possible for others to append new entries + m_writeQueueMutex.lock(); + QList writeQueue = m_writeQueue; + m_writeQueue.clear(); + m_writeQueueMutex.unlock(); + + m_mutex.lock(); + + foreach (const WritingInfo &info, writeQueue) { + I2CDevice *i2cDevice = info.device; + + int fd = m_openFiles.value(i2cDevice)->handle(); + if (fd == -1) { + qCWarning(dcI2C()) << "I2C device" << i2cDevice << "not opened. Cannot write to it."; + continue; + } + + if (ioctl(fd, I2C_SLAVE, i2cDevice->address()) < 0) { + qCWarning(dcI2C()) << "Cannot select I2C slave address for I2C device" << i2cDevice; + continue; + } + + qCDebug(dcI2C()) << "Writing to I2C device" << i2cDevice; + bool success = i2cDevice->writeData(fd, info.data); + + QMetaObject::invokeMethod(i2cDevice, "dataWritten", Qt::QueuedConnection, Q_ARG(bool, success)); + + } + + foreach (I2CDevice *i2cDevice, m_readers.keys()) { + ReadingInfo readingInfo = m_readers.value(i2cDevice); + if (readingInfo.lastReading.addMSecs(readingInfo.interval) > QDateTime::currentDateTime()) { + continue; + } + int fd = m_openFiles.value(i2cDevice)->handle(); + if (fd == -1) { + qCWarning(dcI2C()) << "I2C device" << i2cDevice << "not opened. Cannot read."; + continue; + } + + if (ioctl(fd, I2C_SLAVE, i2cDevice->address()) < 0) { + qCWarning(dcI2C()) << "Cannot select I2C slave address for I2C device" << i2cDevice; + continue; + } + + qCDebug(dcI2C()) << "Reading I2C device" << i2cDevice; + QByteArray data = i2cDevice->readData(fd); + + m_readers[i2cDevice].lastReading = QDateTime::currentDateTime(); + + QMetaObject::invokeMethod(i2cDevice, "readingAvailable", Qt::QueuedConnection, Q_ARG(QByteArray, data)); + } + + m_mutex.unlock(); + }); + + m_watcher.setFuture(future); + connect(&m_watcher, SIGNAL(finished()), &m_pollTimer, SLOT(start())); +} + +} diff --git a/libnymea-core/hardware/i2c/i2cmanagerimplementation.h b/libnymea-core/hardware/i2c/i2cmanagerimplementation.h new file mode 100644 index 00000000..3280ee6a --- /dev/null +++ b/libnymea-core/hardware/i2c/i2cmanagerimplementation.h @@ -0,0 +1,95 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2020, nymea GmbH +* Contact: contact@nymea.io +* +* This file is part of nymea. +* This project including source code and documentation is protected by +* copyright law, and remains the property of nymea GmbH. All rights, including +* reproduction, publication, editing and translation, are reserved. The use of +* this project is subject to the terms of a license agreement to be concluded +* with nymea GmbH in accordance with the terms of use of nymea GmbH, available +* under https://nymea.io/license +* +* GNU Lesser General Public License Usage +* Alternatively, this project may be redistributed and/or modified under the +* terms of the GNU Lesser General Public License as published by the Free +* Software Foundation; version 3. This project is distributed in the hope that +* it will be useful, but WITHOUT ANY WARRANTY; without even the implied +* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this project. If not, see . +* +* For any further details and any questions please contact us under +* contact@nymea.io or see our FAQ/Licensing Information on +* https://nymea.io/license/faq +* +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef I2CMANAGERIMPLEMENTATION_H +#define I2CMANAGERIMPLEMENTATION_H + +#include "hardware/i2c/i2cmanager.h" + +#include +#include +#include +#include +#include +#include +#include + +class QFile; + +namespace nymeaserver { + +class I2CManagerImplementation : public I2CManager +{ + Q_OBJECT +public: + + explicit I2CManagerImplementation(QObject *parent = nullptr); + ~I2CManagerImplementation(); + + QStringList availablePorts() const override; + QList scanRegisters(const QString &portName) override; + + bool open(I2CDevice *i2cDevice) override; + bool startReading(I2CDevice *i2cDevice, int interval = 1000) override; + void stopReading(I2CDevice *i2cDevice) override; + bool writeData(I2CDevice *i2cDevice, const QByteArray &data) override; + void close(I2CDevice *i2cDevice) override; + +private slots: + void nextCycle(); + +private: + class ReadingInfo { + public: + int interval; + QDateTime lastReading; + }; + class WritingInfo { + public: + QByteArray data; + I2CDevice *device; + }; + + QMutex m_mutex; + QHash m_readers; + QHash m_openFiles; + + QMutex m_writeQueueMutex; + QList m_writeQueue; + + QFutureWatcher m_watcher; + + QTimer m_pollTimer; + +}; + +} + +#endif // I2CMANAGERIMPLEMENTATION_H diff --git a/libnymea-core/hardwaremanagerimplementation.cpp b/libnymea-core/hardwaremanagerimplementation.cpp index 82d582bc..7e057cf0 100644 --- a/libnymea-core/hardwaremanagerimplementation.cpp +++ b/libnymea-core/hardwaremanagerimplementation.cpp @@ -41,6 +41,7 @@ #include "hardware/radio433/radio433brennenstuhl.h" #include "hardware/bluetoothlowenergy/bluetoothlowenergymanagerimplementation.h" #include "hardware/network/mqtt/mqttproviderimplementation.h" +#include "hardware/i2c/i2cmanagerimplementation.h" namespace nymeaserver { @@ -67,8 +68,11 @@ HardwareManagerImplementation::HardwareManagerImplementation(Platform *platform, // Bluetooth LE m_bluetoothLowEnergyManager = new BluetoothLowEnergyManagerImplementation(m_pluginTimerManager->registerTimer(10), this); + m_i2cManager = new I2CManagerImplementation(this); + qCDebug(dcHardware()) << "Hardware manager initialized successfully"; + // Enable all the resources setResourceEnabled(m_pluginTimerManager, true); setResourceEnabled(m_radio433, true); @@ -127,4 +131,9 @@ MqttProvider *HardwareManagerImplementation::mqttProvider() return m_mqttProvider; } +I2CManager *HardwareManagerImplementation::i2cManager() +{ + return m_i2cManager; +} + } diff --git a/libnymea-core/hardwaremanagerimplementation.h b/libnymea-core/hardwaremanagerimplementation.h index 404fceb8..0c5899eb 100644 --- a/libnymea-core/hardwaremanagerimplementation.h +++ b/libnymea-core/hardwaremanagerimplementation.h @@ -37,14 +37,6 @@ #include "hardwaremanager.h" -class Radio433; -class UpnpDiscovery; -class PluginTimerManager; -class NetworkAccessManager; -class UpnpDeviceDescriptor; -class PlatformZeroConfController; -class BluetoothLowEnergyManager; - namespace nymeaserver { class Platform; @@ -65,6 +57,7 @@ public: PlatformZeroConfController *zeroConfController() override; BluetoothLowEnergyManager *bluetoothLowEnergyManager() override; MqttProvider *mqttProvider() override; + I2CManager * i2cManager() override; private: QNetworkAccessManager *m_networkAccessManager = nullptr; @@ -78,6 +71,7 @@ private: UpnpDiscovery *m_upnpDiscovery = nullptr; BluetoothLowEnergyManager *m_bluetoothLowEnergyManager = nullptr; MqttProvider *m_mqttProvider = nullptr; + I2CManager *m_i2cManager = nullptr; }; } diff --git a/libnymea-core/libnymea-core.pro b/libnymea-core/libnymea-core.pro index b0a85f77..ea33d384 100644 --- a/libnymea-core/libnymea-core.pro +++ b/libnymea-core/libnymea-core.pro @@ -93,6 +93,7 @@ HEADERS += nymeacore.h \ hardware/network/upnp/upnpdiscoveryreplyimplementation.h \ hardware/network/mqtt/mqttproviderimplementation.h \ hardware/network/mqtt/mqttchannelimplementation.h \ + hardware/i2c/i2cmanagerimplementation.h \ debugserverhandler.h \ tagging/tagsstorage.h \ tagging/tag.h \ @@ -177,6 +178,7 @@ SOURCES += nymeacore.cpp \ hardware/network/upnp/upnpdiscoveryreplyimplementation.cpp \ hardware/network/mqtt/mqttproviderimplementation.cpp \ hardware/network/mqtt/mqttchannelimplementation.cpp \ + hardware/i2c/i2cmanagerimplementation.cpp \ debugserverhandler.cpp \ tagging/tagsstorage.cpp \ tagging/tag.cpp \ diff --git a/libnymea/hardware/i2c/i2cdevice.cpp b/libnymea/hardware/i2c/i2cdevice.cpp new file mode 100644 index 00000000..f68fd7c7 --- /dev/null +++ b/libnymea/hardware/i2c/i2cdevice.cpp @@ -0,0 +1,118 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2020, nymea GmbH +* Contact: contact@nymea.io +* +* This file is part of nymea. +* This project including source code and documentation is protected by +* copyright law, and remains the property of nymea GmbH. All rights, including +* reproduction, publication, editing and translation, are reserved. The use of +* this project is subject to the terms of a license agreement to be concluded +* with nymea GmbH in accordance with the terms of use of nymea GmbH, available +* under https://nymea.io/license +* +* GNU Lesser General Public License Usage +* Alternatively, this project may be redistributed and/or modified under the +* terms of the GNU Lesser General Public License as published by the Free +* Software Foundation; version 3. This project is distributed in the hope that +* it will be useful, but WITHOUT ANY WARRANTY; without even the implied +* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this project. If not, see . +* +* For any further details and any questions please contact us under +* contact@nymea.io or see our FAQ/Licensing Information on +* https://nymea.io/license/faq +* +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "i2cdevice.h" +#include "i2cmanager.h" + +#include + +/*! \fn QByteArray I2CDevice::readData(int fileDescriptor); + Reimplement this when implementing reading communication with an I2C device. + This method will be called repeatedly as soon as the I2CManager is instructed to start + reading from this device. Read the current value from the device, e.g. a sensor reading + and return the value. + + The given file descriptor will already be opened, the I2C slave address already be + selected. The only task is to read the current value. Often that consists of a + write operation to configure registers on the device followed by a read operation. + + IMPORTANT: This method will be called from a different thread. This means that you + are free to perform blocking operations, including calling QThread::msleep() but it + also implies that you must not access other members of the class if they may be + accessed from code outside of this method. If you absolutely must do so, make sure + to use mutexes (e.g. QMutex) accordingly. +*/ + +/*! \fn bool I2CDevice::writeData(int fileDescriptor, const QByteArray &data); + Reimplement this when implementing writing communication with an I2C device. + This method will be called when I2CManager::writeData(I2CDevice *device, const QByteArray &data) + is called. The data to be written is copied and passed on to this method. + + The given file descriptor will already be opened, the I2C slave address already be + selected. The only task is to write the data to it. Often that consists of a + write operation to configure registers on the device followed by another write operation + to write the actual data. + + IMPORTANT: This method will be called from a different thread. This means that you + are free to perform blocking operations, including calling QThread::msleep() but it + also implies that you must not access other members of the class if they may be + accessed from code outside of this method. If you absolutely must do so, make sure + to use mutexes (e.g. QMutex) accordingly. +*/ + +/*! Constructs an I2CDevice with the given portName and address. The \a portName + must match the file name of /dev, for example "i2c-0" for /dev/i2c-0. The + \a address describes the I2C slave address for this device. + + I2CManager::scanRegisters() can be used to scan for available I2C devices connected + to the system. +*/ +I2CDevice::I2CDevice(const QString &portName, int address, QObject *parent): + QObject(parent), + m_portName(portName), + m_address(address) +{ + +} + +I2CDevice::~I2CDevice() +{ +} + +/*! Returns the port name of this I2C device. */ +QString I2CDevice::portName() const +{ + return m_portName; +} + +/*! Returns the address of this I2C device. */ +int I2CDevice::address() const +{ + return m_address; +} + +QByteArray I2CDevice::readData(int fileDescriptor) +{ + Q_UNUSED(fileDescriptor) + return QByteArray(); +} + +bool I2CDevice::writeData(int fileDescriptor, const QByteArray &data) +{ + Q_UNUSED(fileDescriptor) + Q_UNUSED(data) + return false; +} + +QDebug operator<<(QDebug debug, const I2CDevice *i2cDevice) +{ + debug.nospace() << "I2CDevice(Port: " << i2cDevice->portName() << ", Address: 0x" << QString::number(i2cDevice->address(), 16) << ")"; + return debug.space(); +} diff --git a/libnymea/hardware/i2c/i2cdevice.h b/libnymea/hardware/i2c/i2cdevice.h new file mode 100644 index 00000000..6ec3986c --- /dev/null +++ b/libnymea/hardware/i2c/i2cdevice.h @@ -0,0 +1,61 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2020, nymea GmbH +* Contact: contact@nymea.io +* +* This file is part of nymea. +* This project including source code and documentation is protected by +* copyright law, and remains the property of nymea GmbH. All rights, including +* reproduction, publication, editing and translation, are reserved. The use of +* this project is subject to the terms of a license agreement to be concluded +* with nymea GmbH in accordance with the terms of use of nymea GmbH, available +* under https://nymea.io/license +* +* GNU Lesser General Public License Usage +* Alternatively, this project may be redistributed and/or modified under the +* terms of the GNU Lesser General Public License as published by the Free +* Software Foundation; version 3. This project is distributed in the hope that +* it will be useful, but WITHOUT ANY WARRANTY; without even the implied +* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this project. If not, see . +* +* For any further details and any questions please contact us under +* contact@nymea.io or see our FAQ/Licensing Information on +* https://nymea.io/license/faq +* +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef I2CDEVICE_H +#define I2CDEVICE_H + +#include + +class I2CDevice : public QObject +{ + Q_OBJECT +public: + explicit I2CDevice(const QString &portName, int address, QObject *parent = nullptr); + virtual ~I2CDevice(); + + QString portName() const; + int address() const; + + virtual QByteArray readData(int fileDescriptor); + virtual bool writeData(int fileDescriptor, const QByteArray &data); + +signals: + void readingAvailable(const QByteArray &data); + void dataWritten(bool success); + +private: + QString m_portName; + int m_address; +}; + +QDebug operator<<(QDebug debug, const I2CDevice *i2cDevice); + + +#endif // I2CDEVICE_H diff --git a/libnymea/hardware/i2c/i2cmanager.cpp b/libnymea/hardware/i2c/i2cmanager.cpp new file mode 100644 index 00000000..36669451 --- /dev/null +++ b/libnymea/hardware/i2c/i2cmanager.cpp @@ -0,0 +1,78 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2020, nymea GmbH +* Contact: contact@nymea.io +* +* This file is part of nymea. +* This project including source code and documentation is protected by +* copyright law, and remains the property of nymea GmbH. All rights, including +* reproduction, publication, editing and translation, are reserved. The use of +* this project is subject to the terms of a license agreement to be concluded +* with nymea GmbH in accordance with the terms of use of nymea GmbH, available +* under https://nymea.io/license +* +* GNU Lesser General Public License Usage +* Alternatively, this project may be redistributed and/or modified under the +* terms of the GNU Lesser General Public License as published by the Free +* Software Foundation; version 3. This project is distributed in the hope that +* it will be useful, but WITHOUT ANY WARRANTY; without even the implied +* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this project. If not, see . +* +* For any further details and any questions please contact us under +* contact@nymea.io or see our FAQ/Licensing Information on +* https://nymea.io/license/faq +* +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "i2cmanager.h" + +/*! \fn QStringList I2CManager::availablePorts() const + Lists the available I2C ports in the system. + */ + +/*! \fn QList I2CManager::scanRegisters(const QString &portName = QString()) + Scans the I2C systems for connected devices. If \a portName is given, only this port + will be scanned. If \a portName is empty, all available ports in the system will be + scanned. + */ + +/*! \fn bool I2CManager::open(I2CDevice *i2cDevice) + Open the given I2CDevice. After reimplementing an I2CDevice, this method can be used + to open the device. Returns false if opening fails, for example because of missing + permissions or if the given \ai2cDevice is not valid, for example because of a bad + port name or slave address. +*/ + +/*! \fn bool I2CManager::startReading(I2CDevice *i2cDevice, int interval = 1000) + Start reading from the given \a i2cDevice. When calling this, the I2CManager will start + polling the given \a i2cDevice. Optionally, the interface can be given. + Note that the interval might not be met, for example if the device is busy by other + readers. + The given \a i2cDevice is required to be opened first. + */ + +/*! \fn void I2CManager::stopReading(I2CDevice *i2cDevice) + Stops reading from the given \a i2cDevice. + */ + +/*! \fn bool I2CManager::writeData(I2CDevice *i2cDevice, const QByteArray &data) + Write the given \a data to the given \a i2cDevice. + The data will be put into a write buffer and will be written to the device + in a different thread when the i2c device is available. + The given \a i2cDevice is required to be opened first. + */ + +/*! \fn void I2CManager::close(I2CDevice *i2cDevice) + Closes the giben \a i2cDevice. If reading are still active for this device, + stopReading() will be called implicitly. + */ + +I2CManager::I2CManager(QObject *parent): + QObject(parent) +{ + +} diff --git a/libnymea/hardware/i2c/i2cmanager.h b/libnymea/hardware/i2c/i2cmanager.h new file mode 100644 index 00000000..3aefbce4 --- /dev/null +++ b/libnymea/hardware/i2c/i2cmanager.h @@ -0,0 +1,60 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2020, nymea GmbH +* Contact: contact@nymea.io +* +* This file is part of nymea. +* This project including source code and documentation is protected by +* copyright law, and remains the property of nymea GmbH. All rights, including +* reproduction, publication, editing and translation, are reserved. The use of +* this project is subject to the terms of a license agreement to be concluded +* with nymea GmbH in accordance with the terms of use of nymea GmbH, available +* under https://nymea.io/license +* +* GNU Lesser General Public License Usage +* Alternatively, this project may be redistributed and/or modified under the +* terms of the GNU Lesser General Public License as published by the Free +* Software Foundation; version 3. This project is distributed in the hope that +* it will be useful, but WITHOUT ANY WARRANTY; without even the implied +* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this project. If not, see . +* +* For any further details and any questions please contact us under +* contact@nymea.io or see our FAQ/Licensing Information on +* https://nymea.io/license/faq +* +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef I2CMANAGER_H +#define I2CMANAGER_H + +#include + +class I2CDevice; + +struct I2CScanResult { + QString portName; + int address; +}; + +class I2CManager : public QObject +{ + Q_OBJECT +public: + I2CManager(QObject *parent = nullptr); + virtual ~I2CManager() = default; + + virtual QStringList availablePorts() const = 0; + virtual QList scanRegisters(const QString &portName = QString()) = 0; + + virtual bool open(I2CDevice *i2cDevice) = 0; + virtual bool startReading(I2CDevice *i2cDevice, int interval = 1000) = 0; + virtual void stopReading(I2CDevice *i2cDevice) = 0; + virtual bool writeData(I2CDevice *i2cDevice, const QByteArray &data) = 0; + virtual void close(I2CDevice *i2cDevice) = 0; +}; + +#endif // I2CMANAGER_H diff --git a/libnymea/hardwaremanager.h b/libnymea/hardwaremanager.h index d351ef38..d5ed2c45 100644 --- a/libnymea/hardwaremanager.h +++ b/libnymea/hardwaremanager.h @@ -41,6 +41,7 @@ class UpnpDeviceDescriptor; class PlatformZeroConfController; class BluetoothLowEnergyManager; class MqttProvider; +class I2CManager; class HardwareResource; class HardwareManager : public QObject @@ -59,6 +60,7 @@ public: virtual PlatformZeroConfController *zeroConfController() = 0; virtual BluetoothLowEnergyManager *bluetoothLowEnergyManager() = 0; virtual MqttProvider *mqttProvider() = 0; + virtual I2CManager *i2cManager() = 0; protected: void setResourceEnabled(HardwareResource* resource, bool enabled); diff --git a/libnymea/libnymea.pro b/libnymea/libnymea.pro index 6654c457..f090daf4 100644 --- a/libnymea/libnymea.pro +++ b/libnymea/libnymea.pro @@ -55,6 +55,8 @@ HEADERS += \ hardware/bluetoothlowenergy/bluetoothlowenergydevice.h \ hardware/bluetoothlowenergy/bluetoothdiscoveryreply.h \ hardware/bluetoothlowenergy/bluetoothlowenergymanager.h \ + hardware/i2c/i2cmanager.h \ + hardware/i2c/i2cdevice.h \ coap/coap.h \ coap/coappdu.h \ coap/coapoption.h \ @@ -131,6 +133,8 @@ SOURCES += \ hardware/bluetoothlowenergy/bluetoothlowenergymanager.cpp \ hardware/bluetoothlowenergy/bluetoothlowenergydevice.cpp \ hardware/bluetoothlowenergy/bluetoothdiscoveryreply.cpp \ + hardware/i2c/i2cmanager.cpp \ + hardware/i2c/i2cdevice.cpp \ coap/coap.cpp \ coap/coappdu.cpp \ coap/coapoption.cpp \ diff --git a/libnymea/loggingcategories.cpp b/libnymea/loggingcategories.cpp index ee549c92..86b41c30 100644 --- a/libnymea/loggingcategories.cpp +++ b/libnymea/loggingcategories.cpp @@ -72,6 +72,7 @@ Q_LOGGING_CATEGORY(dcBluetoothServer, "BluetoothServer") Q_LOGGING_CATEGORY(dcBluetoothServerTraffic, "BluetoothServerTraffic") Q_LOGGING_CATEGORY(dcMqtt, "Mqtt") Q_LOGGING_CATEGORY(dcTranslations, "Translations") +Q_LOGGING_CATEGORY(dcI2C, "I2C") static QFile s_logFile; diff --git a/libnymea/loggingcategories.h b/libnymea/loggingcategories.h index 5faca21b..7ace86f3 100644 --- a/libnymea/loggingcategories.h +++ b/libnymea/loggingcategories.h @@ -75,6 +75,7 @@ Q_DECLARE_LOGGING_CATEGORY(dcBluetoothServerTraffic) Q_DECLARE_LOGGING_CATEGORY(dcMqtt) Q_DECLARE_LOGGING_CATEGORY(dcTranslations) Q_DECLARE_LOGGING_CATEGORY(dcCoap) +Q_DECLARE_LOGGING_CATEGORY(dcI2C) /* Installs a nymea log message handler in the system.