diff --git a/debian/control b/debian/control index d17e3e89..219a4b5c 100644 --- a/debian/control +++ b/debian/control @@ -632,6 +632,21 @@ Description: nymea.io plugin for remote ssh connection . This package will install the nymea.io plugin for remote ssh connection +Package: nymea-plugin-pianohat +Architecture: any +Depends: ${shlibs:Depends}, + ${misc:Depends}, + nymea-plugins-translations, +Description: nymea.io plugin for the Raspberry Pi Pianohat + The nymea daemon is a plugin based IoT (Internet of Things) server. The + server works like a translator for devices, things and services and + allows them to interact. + With the powerful rule engine you are able to connect any device available + in the system and create individual scenes and behaviors for your environment. + . + This package will install the nymea.io plugin for the Raspberry Pi Pianohat + + Package: nymea-plugins-translations Section: misc @@ -670,6 +685,7 @@ Depends: nymea-plugin-awattar, nymea-plugin-elgato, nymea-plugin-senic, nymea-plugin-keba, + nymea-plugin-pianohat, Replaces: guh-plugins Description: Plugins for nymea IoT server - the default plugin collection The nymea daemon is a plugin based IoT (Internet of Things) server. The diff --git a/debian/nymea-plugin-pianohat.install.in b/debian/nymea-plugin-pianohat.install.in new file mode 100644 index 00000000..2192e4d5 --- /dev/null +++ b/debian/nymea-plugin-pianohat.install.in @@ -0,0 +1 @@ +usr/lib/@DEB_HOST_MULTIARCH@/nymea/plugins/libnymea_devicepluginpianohat.so diff --git a/nymea-plugins.pro b/nymea-plugins.pro index d418c08f..e5567989 100644 --- a/nymea-plugins.pro +++ b/nymea-plugins.pro @@ -40,6 +40,7 @@ PLUGIN_DIRS = \ keba \ remotessh \ dweetio \ + pianohat \ CONFIG+=all diff --git a/pianohat/devicepluginpianohat.cpp b/pianohat/devicepluginpianohat.cpp new file mode 100644 index 00000000..ef4adc81 --- /dev/null +++ b/pianohat/devicepluginpianohat.cpp @@ -0,0 +1,166 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2018 Simon Stürz * + * * + * This file is part of nymea. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * This library 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 library; If not, see * + * . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "plugininfo.h" +#include "devicepluginpianohat.h" +#include "types/param.h" + +DevicePluginPianohat::DevicePluginPianohat() +{ + +} + +void DevicePluginPianohat::init() +{ + // Initialize/create objects +} + +void DevicePluginPianohat::startMonitoringAutoDevices() +{ + // Start seaching for devices which can be discovered and added automatically +} + +void DevicePluginPianohat::postSetupDevice(Device *device) +{ + qCDebug(dcPianohat()) << "Post setup device" << device->name() << device->params(); + + m_pianohat->enable(); +} + +void DevicePluginPianohat::deviceRemoved(Device *device) +{ + qCDebug(dcPianohat()) << "Remove device" << device->name() << device->params(); + + // Clean up all data related to this device + if (m_pianohat) { + m_pianohat->disable(); + m_pianohat->deleteLater(); + m_pianohat = nullptr; + } + + if (m_device) { + // Note: the DeviceManager will take care about the device Object + m_device = nullptr; + } +} + +DeviceManager::DeviceSetupStatus DevicePluginPianohat::setupDevice(Device *device) +{ + qCDebug(dcPianohat()) << "Setup device" << device->name() << device->params(); + + // Make sure there is not already a pianohat added, since only one device is supported + if (m_pianohat) { + qCWarning(dcPianohat()) << "There has already been setup a PianoHat. Only one instance of this device is allowed."; + return DeviceManager::DeviceSetupStatusFailure; + } + + // Create the PianoHat object and store the device pointer (there can always be only one pianohat) + m_device = device; + m_pianohat = new PianoHat(this); + connect(m_pianohat, &PianoHat::keyPressed, this, &DevicePluginPianohat::onPianohatKeyPressed); + + return DeviceManager::DeviceSetupStatusSuccess; +} + +DeviceManager::DeviceError DevicePluginPianohat::executeAction(Device *device, const Action &action) +{ + qCDebug(dcPianohat()) << "Executing action for device" << device->name() << action.actionTypeId().toString() << action.params(); + + return DeviceManager::DeviceErrorNoError; +} + +void DevicePluginPianohat::onPianohatKeyPressed(const PianoHat::Key &key, bool pressed) +{ + qCDebug(dcPianohat()) << key << (pressed ? "pressed" : "released"); + + // Emit the key pressed event on pressed and set the bool states + ParamList eventParams; + switch (key) { + case PianoHat::KeyC: + eventParams.append(Param(pianohatButtonNameParamTypeId, "C")); + m_device->setStateValue(pianohatCStateTypeId, pressed); + break; + case PianoHat::KeyCSharp: + eventParams.append(Param(pianohatButtonNameParamTypeId, "C#")); + m_device->setStateValue(pianohatCSharpStateTypeId, pressed); + break; + case PianoHat::KeyD: + eventParams.append(Param(pianohatButtonNameParamTypeId, "D")); + m_device->setStateValue(pianohatDStateTypeId, pressed); + break; + case PianoHat::KeyDSharp: + eventParams.append(Param(pianohatButtonNameParamTypeId, "D#")); + m_device->setStateValue(pianohatDSharpStateTypeId, pressed); + break; + case PianoHat::KeyE: + eventParams.append(Param(pianohatButtonNameParamTypeId, "E")); + m_device->setStateValue(pianohatEStateTypeId, pressed); + break; + case PianoHat::KeyF: + eventParams.append(Param(pianohatButtonNameParamTypeId, "F#")); + m_device->setStateValue(pianohatFStateTypeId, pressed); + break; + case PianoHat::KeyG: + eventParams.append(Param(pianohatButtonNameParamTypeId, "G")); + m_device->setStateValue(pianohatGSharpStateTypeId, pressed); + break; + case PianoHat::KeyGSharp: + eventParams.append(Param(pianohatButtonNameParamTypeId, "G#")); + m_device->setStateValue(pianohatGSharpStateTypeId, pressed); + break; + case PianoHat::KeyA: + eventParams.append(Param(pianohatButtonNameParamTypeId, "A")); + m_device->setStateValue(pianohatAStateTypeId, pressed); + break; + case PianoHat::KeyASharp: + eventParams.append(Param(pianohatButtonNameParamTypeId, "A#")); + m_device->setStateValue(pianohatASharpStateTypeId, pressed); + break; + case PianoHat::KeyB: + eventParams.append(Param(pianohatButtonNameParamTypeId, "B")); + m_device->setStateValue(pianohatBStateTypeId, pressed); + break; + case PianoHat::KeyHighC: + eventParams.append(Param(pianohatButtonNameParamTypeId, "C (high)")); + m_device->setStateValue(pianohatCHighStateTypeId, pressed); + break; + case PianoHat::KeyOctaveDown: + eventParams.append(Param(pianohatButtonNameParamTypeId, "Octave down")); + m_device->setStateValue(pianohatOctaveDownStateTypeId, pressed); + break; + case PianoHat::KeyOctaveUp: + eventParams.append(Param(pianohatButtonNameParamTypeId, "Octave up")); + m_device->setStateValue(pianohatOctaveUpStateTypeId, pressed); + break; + case PianoHat::KeyInstrument: + eventParams.append(Param(pianohatButtonNameParamTypeId, "Instrument")); + m_device->setStateValue(pianohatInstrumentStateTypeId, pressed); + break; + default: + break; + } + + // Only emit the pressed event if the button was touched + if (!eventParams.isEmpty() && pressed) + emitEvent(Event(pianohatPressedEventTypeId, m_device->id(), eventParams)); + +} diff --git a/pianohat/devicepluginpianohat.h b/pianohat/devicepluginpianohat.h new file mode 100644 index 00000000..c2d3d226 --- /dev/null +++ b/pianohat/devicepluginpianohat.h @@ -0,0 +1,58 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2018 Simon Stürz * + * * + * This file is part of nymea. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * This library 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 library; If not, see * + * . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef DEVICEPLUGINPIANOHAT_H +#define DEVICEPLUGINPIANOHAT_H + +#include "pianohat.h" +#include "devicemanager.h" +#include "plugin/deviceplugin.h" + +class DevicePluginPianohat: public DevicePlugin +{ + Q_OBJECT + + Q_PLUGIN_METADATA(IID "io.nymea.DevicePlugin" FILE "devicepluginpianohat.json") + Q_INTERFACES(DevicePlugin) + + +public: + explicit DevicePluginPianohat(); + + void init() override; + void startMonitoringAutoDevices() override; + void postSetupDevice(Device *device) override; + void deviceRemoved(Device *device) override; + + DeviceManager::DeviceSetupStatus setupDevice(Device *device) override; + DeviceManager::DeviceError executeAction(Device *device, const Action &action) override; + +private: + Device *m_device = nullptr; + PianoHat *m_pianohat = nullptr; + +private slots: + void onPianohatKeyPressed(const PianoHat::Key &key, bool pressed); + +}; + +#endif // DEVICEPLUGINPIANOHAT_H diff --git a/pianohat/devicepluginpianohat.json b/pianohat/devicepluginpianohat.json new file mode 100644 index 00000000..1ab94268 --- /dev/null +++ b/pianohat/devicepluginpianohat.json @@ -0,0 +1,190 @@ +{ + "name": "Pianohat", + "displayName": "Pianohat", + "id": "d2cbfd1e-2c4b-4ddd-8a37-21f4c07b9ab3", + "vendors": [ + { + "name": "pimoroni", + "displayName": "Pimoroni", + "id": "3d1adc76-2867-4cbd-bdae-e2cf754869eb", + "deviceClasses": [ + { + "name": "pianohat", + "displayName": "PianoHat", + "id": "8c0001dd-0cb5-4239-af0d-d2c3cb32a803", + "deviceIcon": "None", + "setupMethod": "JustAdd", + "createMethods": ["User"], + "interfaces": [ "simplemultibutton" ], + "stateTypes": [ + { + "id": "b55ae3da-74b5-4419-b00c-296ed4d1be3b", + "name": "c", + "displayName": "C pressed", + "displayNameEvent": "C pressed changed", + "type": "bool", + "defaultValue": false + }, + { + "id": "6ad62591-8f0f-4579-8185-92c29e5bb627", + "name": "cSharp", + "displayName": "C# pressed", + "displayNameEvent": "C# pressed changed", + "type": "bool", + "defaultValue": false + }, + { + "id": "7713a612-5e85-4d56-84b4-33d528e39c99", + "name": "d", + "displayName": "D pressed", + "displayNameEvent": "D pressed changed", + "type": "bool", + "defaultValue": false + }, + { + "id": "77b4628e-3965-4125-a927-80ddff868cab", + "name": "dSharp", + "displayName": "D# pressed", + "displayNameEvent": "D# pressed changed", + "type": "bool", + "defaultValue": false + }, + { + "id": "5b350f53-cb79-4bb4-9355-8407634b88dc", + "name": "e", + "displayName": "E pressed", + "displayNameEvent": "E pressed changed", + "type": "bool", + "defaultValue": false + }, + { + "id": "80432960-2fe8-423b-ab9b-a2b5dc71356c", + "name": "f", + "displayName": "F pressed", + "displayNameEvent": "F pressed changed", + "type": "bool", + "defaultValue": false + }, + { + "id": "b0c8c1af-a311-4f37-a1a8-bd734e540a6b", + "name": "fSharp", + "displayName": "F# pressed", + "displayNameEvent": "F# pressed changed", + "type": "bool", + "defaultValue": false + }, + { + "id": "2eb0a67f-d370-4b79-9c84-48b544c77004", + "name": "g", + "displayName": "G pressed", + "displayNameEvent": "G pressed changed", + "type": "bool", + "defaultValue": false + }, + { + "id": "1897f604-cb26-4d66-b292-251f59b27f56", + "name": "gSharp", + "displayName": "G# pressed", + "displayNameEvent": "G# pressed changed", + "type": "bool", + "defaultValue": false + }, + { + "id": "5ff7377c-95f2-4080-be0c-145b94437ee6", + "name": "a", + "displayName": "A pressed", + "displayNameEvent": "A pressed changed", + "type": "bool", + "defaultValue": false + }, + { + "id": "b221e069-4cbd-4dcd-adba-97beb6630211", + "name": "aSharp", + "displayName": "A# pressed", + "displayNameEvent": "A# pressed changed", + "type": "bool", + "defaultValue": false + }, + { + "id": "99324edb-e88e-4f1d-96c7-49caf861cd4b", + "name": "b", + "displayName": "B pressed", + "displayNameEvent": "B pressed changed", + "type": "bool", + "defaultValue": false + }, + { + "id": "3bc73c73-81ec-41d5-ae77-471d3399740b", + "name": "cHigh", + "displayName": "C (high) pressed", + "displayNameEvent": "C (high) pressed changed", + "type": "bool", + "defaultValue": false + }, + { + "id": "b890db7f-9c60-4288-8911-160d4f4ea508", + "name": "octaveDown", + "displayName": "Octave down pressed", + "displayNameEvent": "Octave down pressed changed", + "type": "bool", + "defaultValue": false + }, + { + "id": "a32bf226-ca2f-48cf-9834-e55b33fd0e58", + "name": "octaveUp", + "displayName": "Octave up pressed", + "displayNameEvent": "Octave up pressed changed", + "type": "bool", + "defaultValue": false + }, + { + "id": "77e47139-445c-4a88-a266-ddefd22997c2", + "name": "instrument", + "displayName": "Instrument pressed", + "displayNameEvent": "Octave down pressed changed", + "type": "bool", + "defaultValue": false + } + ], + "eventTypes": [ + { + "id": "4877964c-951f-42d7-9775-bf28615700b1", + "name": "pressed", + "displayName": "Key pressed", + "paramTypes": [ + { + "id": "74bf5392-d71e-45a0-a96b-8b4ec405d3dd", + "name": "buttonName", + "displayName": "Key", + "type": "QString", + "allowedValues": [ + "C", + "C#", + "D", + "D#", + "E", + "F", + "F#", + "G", + "G#", + "A", + "A#", + "B", + "C (high)", + "Octave down", + "Octave up", + "Instrument" + ], + "defaultValue": "C" + } + ] + } + ] + } + ] + } + ] +} + + + diff --git a/pianohat/docs/CAP1188-datasheet.pdf b/pianohat/docs/CAP1188-datasheet.pdf new file mode 100644 index 00000000..699a82d4 Binary files /dev/null and b/pianohat/docs/CAP1188-datasheet.pdf differ diff --git a/pianohat/pianohat.cpp b/pianohat/pianohat.cpp new file mode 100644 index 00000000..849c58b1 --- /dev/null +++ b/pianohat/pianohat.cpp @@ -0,0 +1,74 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2018 Simon Stürz * + * * + * This file is part of nymea. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * This library 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 library; If not, see * + * . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "pianohat.h" +#include "extern-plugininfo.h" + +PianoHat::PianoHat(QObject *parent) : + QObject(parent) +{ + qCDebug(dcPianohat()) << "Create touch sensor from main trhead" << QThread::currentThreadId(); + + /* Note: the pianohat has 2 CAP1188 chips on following Addresses + * https://pinout.xyz/pinout/piano_hat# + * - Register 0x28 + * - Alert GPIO: 4 + * - Reset GPIO: 17 + * - Register 0x2b + * - Alert GPIO: 27 + * - Reset GPIO: 22 + */ + + m_sensorOne = new TouchSensor(0x28, 4, 17, this); + connect(m_sensorOne, &TouchSensor::keyPressedChanged, this, &PianoHat::onSensorOneKeyPressedChanged); + + m_sensorTwo = new TouchSensor(0x2b, 27, 22, this); + connect(m_sensorTwo, &TouchSensor::keyPressedChanged, this, &PianoHat::onSensorTwoKeyPressedChanged); +} + +void PianoHat::enable() +{ + // Enable the sensors + m_sensorOne->enableSensor(); + m_sensorTwo->enableSensor(); +} + +void PianoHat::disable() +{ + // Enable the sensors + m_sensorOne->disableSensor(); + m_sensorTwo->disableSensor(); +} + +void PianoHat::onSensorOneKeyPressedChanged(quint8 key, bool pressed) +{ + // Map the bit number to the actual pianohat key + Key pianoHatKey = static_cast(key); + emit keyPressed(pianoHatKey, pressed); +} + +void PianoHat::onSensorTwoKeyPressedChanged(quint8 key, bool pressed) +{ + // Map the bit number to the actual pianohat key (bit + 8) + Key pianoHatKey = static_cast(key + 8); + emit keyPressed(pianoHatKey, pressed); +} diff --git a/pianohat/pianohat.h b/pianohat/pianohat.h new file mode 100644 index 00000000..b2c38d03 --- /dev/null +++ b/pianohat/pianohat.h @@ -0,0 +1,76 @@ +#ifndef PIANOHAT_H +#define PIANOHAT_H + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2018 Simon Stürz * + * * + * This file is part of nymea. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * This library 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 library; If not, see * + * . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include + +#include "touchsensor.h" +#include "plugin/device.h" + + +class PianoHat : public QObject +{ + Q_OBJECT +public: + enum Key { + // Sensor one 0x28 + KeyC = 0, + KeyCSharp = 1, + KeyD = 2, + KeyDSharp = 3, + KeyE = 4, + KeyF = 5, + KeyFSharp = 6, + KeyG = 7, + // Sensor two 0x2b + KeyGSharp = 8, + KeyA = 9, + KeyASharp = 10, + KeyB = 11, + KeyHighC = 12, + KeyOctaveDown = 13, + KeyOctaveUp = 14, + KeyInstrument = 15 + }; + Q_ENUM(Key) + + explicit PianoHat(QObject *parent = nullptr); + + void enable(); + void disable(); + +private: + TouchSensor *m_sensorOne = nullptr; + TouchSensor *m_sensorTwo = nullptr; + +signals: + void keyPressed(const Key &key, bool pressed); + +private slots: + void onSensorOneKeyPressedChanged(quint8 key, bool pressed); + void onSensorTwoKeyPressedChanged(quint8 key, bool pressed); + +}; + +#endif // PIANOHAT_H diff --git a/pianohat/pianohat.pro b/pianohat/pianohat.pro new file mode 100644 index 00000000..ee5b1e47 --- /dev/null +++ b/pianohat/pianohat.pro @@ -0,0 +1,14 @@ +include(../plugins.pri) + +TARGET = $$qtLibraryTarget(nymea_devicepluginpianohat) + +SOURCES += \ + devicepluginpianohat.cpp \ + pianohat.cpp \ + touchsensor.cpp + +HEADERS += \ + devicepluginpianohat.h \ + pianohat.h \ + touchsensor.h + diff --git a/pianohat/touchsensor.cpp b/pianohat/touchsensor.cpp new file mode 100644 index 00000000..53b0c6d7 --- /dev/null +++ b/pianohat/touchsensor.cpp @@ -0,0 +1,221 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2018 Simon Stürz * + * * + * This file is part of nymea. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * This library 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 library; If not, see * + * . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "touchsensor.h" +#include "extern-plugininfo.h" + +#include +#include +#include "linux/i2c-dev.h" +#include +#include +#include +#include + + +TouchSensor::TouchSensor(quint8 i2cRegister, int interruptGpio, int resetGpio, QObject *parent) : + QThread(parent), + m_i2cRegister(i2cRegister), + m_interruptGpioPin(interruptGpio), + m_resetGpioPin(resetGpio) +{ + +} + +TouchSensor::~TouchSensor() +{ + disableSensor(); + wait(); +} + +void TouchSensor::setupGpios() +{ + qCDebug(dcPianohat()) << "Setup GPIOs for sensor" << QString().number(m_i2cRegister, 16).prepend("0x"); + + // Init reset gpio + m_resetGpio = new Gpio(m_resetGpioPin); + if (!m_resetGpio->exportGpio()) { + qCWarning(dcPianohat()) << "Could not export reset GPIO" << m_resetGpioPin; + } + + msleep(500); + + if (!m_resetGpio->setDirection(Gpio::DirectionOutput)) { + qCWarning(dcPianohat()) << "Could not set direction of reset GPIO" << m_resetGpioPin; + } + + m_resetGpio->moveToThread(this); +} + +void TouchSensor::initializeSensor() +{ + qCDebug(dcPianohat()) << "Start initializing sensor" << QString().number(m_i2cRegister, 16).prepend("0x"); + + // Reset the controller + qCDebug(dcPianohat()) << "Reset sensor" << QString().number(m_i2cRegister, 16).prepend("0x"); + m_resetGpio->setValue(Gpio::ValueLow); + msleep(100); + m_resetGpio->setValue(Gpio::ValueHigh); + msleep(100); + m_resetGpio->setValue(Gpio::ValueLow); + msleep(100); + + // Configure sensor + qCDebug(dcPianohat()) << "Setup sensor" << QString().number(m_i2cRegister, 16).prepend("0x"); + + m_i2cDevice = open(m_deviceFile.toLocal8Bit(), O_RDWR); + if(m_i2cDevice < 0){ + qCWarning(dcPianohat()) << "Could not open I2C device" << m_deviceFile; + return; + } + + int ret = ioctl(m_i2cDevice, I2C_SLAVE, m_i2cRegister); + if (ret < 0) { + qCWarning(dcPianohat()) << "Failed to aquire slave mode access to I2C device"; + return; + } + + qCDebug(dcPianohat()) << "--> Product ID:" << QString().number(readRegister(CAP1188_PROUCT_ID), 16).prepend("0x"); + qCDebug(dcPianohat()) << "--> Manufacturer ID:" << QString().number(readRegister(CAP1188_MANUFACTURER_ID), 16).prepend("0x"); + qCDebug(dcPianohat()) << "--> Revision:" << QString().number(readRegister(CAP1188_REVISION), 16).prepend("0x"); + + // Enable all inputs + writeRegister(CAP1188_INPUT_ENABLE, 0xFF); + + // Enable interrupts + writeRegister(0x27, 0xFF); + + // Disable repeat + writeRegister(0x28, 0x00); + + // Enable multitouch + quint8 multitouchConfig = readRegister(CAP1188_MULTITOUCH_CONFIG); + writeRegister(CAP1188_MULTITOUCH_CONFIG, multitouchConfig & ~0x80); + + // Sampling config + // Note: configurations from: https://github.com/pimoroni/cap1xxx/blob/master/library/cap1xxx.py#L305 + writeRegister(CAP1188_SAMPLING_CONFIG, 0b00001000); // 1sample per measure, 1.28ms time, 35ms cycle + + // Sensitivity + writeRegister(CAP1188_SENSITIVITY_CONFIG, 0b01100000); // 2x sensitivity + + // General config + writeRegister(CAP1188_GENERAL_CONFIG, 0b00111000); + + // Config 2 + writeRegister(CAP1188_CONFIGURATION2, 0b01100000); + + // Enable led for all keys + writeRegister(CAP1188_LED, 0xFF); + + // Speed up + writeRegister(CAP1188_STANDBY_CONFIG, 0x30); +} + +void TouchSensor::cleanUp() +{ + // Clean up + if (m_resetGpio) { + delete m_resetGpio; + m_resetGpio = nullptr; + } + + if (m_i2cDevice > 0) { + close(m_i2cDevice); + m_i2cDevice = -1; + } +} + +void TouchSensor::run() +{ + setupGpios(); + initializeSensor(); + + qCDebug(dcPianohat()) << "Initialized sensor" << QString().number(m_i2cRegister, 16).prepend("0x"); + + // Poll GPIO for interrupts + while (1) { + + quint8 inputStatus = readRegister(CAP1188_INPUTSTATUS); + if (inputStatus) { + processInputData(inputStatus); + clearInterrupt(); + } else { + processInputData(0); + } + + QMutexLocker locker(&m_stopMutex); + if (m_stop) break; + msleep(50); + } + + cleanUp(); + qCDebug(dcPianohat()) << "Sensor thread finished."; +} + +quint8 TouchSensor::readRegister(quint8 i2cRegister) +{ + i2c_smbus_write_byte(m_i2cDevice, i2cRegister); + return i2c_smbus_read_byte(m_i2cDevice);; +} + +void TouchSensor::writeRegister(quint8 i2cRegister, quint8 value) +{ + i2c_smbus_write_byte_data(m_i2cDevice, i2cRegister, value); +} + +void TouchSensor::clearInterrupt() +{ + quint8 mainConfiguration = readRegister(CAP1188_MAIN); + mainConfiguration &= ~0b00000001; + writeRegister(CAP1188_MAIN, mainConfiguration); +} + +void TouchSensor::processInputData(quint8 data) +{ + if (m_currentInputStatus != data) { + //qCDebug(dcPianohat()) << "-->" << data; + + for (int i = 0; i < 8; i++) { + bool keyPressed = data & (1 << i); + bool previousStatus = m_currentInputStatus & (1 << i); + if (previousStatus != keyPressed) { + //qCDebug(dcPianohat()) << "Touched changed" << QString().number(m_i2cRegister, 16).prepend("0x") << " --> Key" << i << " = " << (keyPressed ? "pressed" : "released"); + emit keyPressedChanged(i, keyPressed); + } + } + + m_currentInputStatus = data; + } +} + +void TouchSensor::enableSensor() +{ + start(); +} + +void TouchSensor::disableSensor() +{ + qCDebug(dcPianohat()) << "Disable sensor"; + QMutexLocker locker(&m_stopMutex); + m_stop = true; +} diff --git a/pianohat/touchsensor.h b/pianohat/touchsensor.h new file mode 100644 index 00000000..3d642b6a --- /dev/null +++ b/pianohat/touchsensor.h @@ -0,0 +1,97 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2018 Simon Stürz * + * * + * This file is part of nymea. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * This library 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 library; If not, see * + * . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef TOUCHSENSOR_H +#define TOUCHSENSOR_H + +#include +#include +#include +#include + +#include "hardware/gpio.h" + +// Registers of CAP1188 capacitive touch sensor (see doc folder for the datasheet) +#define CAP1188_MAIN 0x00 +#define CAP1188_INPUTSTATUS 0x03 +#define CAP1188_GENERAL_CONFIG 0x20 +#define CAP1188_SENSITIVITY_CONFIG 0x1F +#define CAP1188_INPUT_ENABLE 0x21 +#define CAP1188_SAMPLING_CONFIG 0x24 +#define CAP1188_MULTITOUCH_CONFIG 0x2A +#define CAP1188_STANDBY_CONFIG 0x41 +#define CAP1188_CONFIGURATION2 0x44 +#define CAP1188_LED 0x72 +#define CAP1188_LEDPOL 0x73 +#define CAP1188_PROUCT_ID 0xFD +#define CAP1188_MANUFACTURER_ID 0xFE +#define CAP1188_REVISION 0xFF + +class TouchSensor : public QThread +{ + Q_OBJECT +public: + explicit TouchSensor(quint8 i2cRegister, int interruptGpio, int resetGpio, QObject *parent = nullptr); + ~TouchSensor(); + +private: + // Sensor wiring + quint8 m_i2cRegister = 0x28; + int m_interruptGpioPin = 4; + int m_resetGpioPin = 17; + + Gpio *m_resetGpio = nullptr; + quint8 m_currentInputStatus = 0; + + // I2C + QString m_deviceFile = "/dev/i2c-1"; + int m_i2cDevice = -1; + + // Thread stuff + QMutex m_stopMutex; + bool m_stop = false; + void run() override; + + // Setup methods + void setupGpios(); + void initializeSensor(); + void cleanUp(); + + // I2C methods + quint8 readRegister(quint8 i2cRegister); + void writeRegister(quint8 i2cRegister, quint8 value); + + // Sensor methods + void clearInterrupt(); + void processInputData(quint8 data); + +signals: + void keyPressedChanged(int key, bool pressed); + + +public slots: + void enableSensor(); + void disableSensor(); + +}; + +#endif // TOUCHSENSOR_H diff --git a/pianohat/translations/d2cbfd1e-2c4b-4ddd-8a37-21f4c07b9ab3-en_US.ts b/pianohat/translations/d2cbfd1e-2c4b-4ddd-8a37-21f4c07b9ab3-en_US.ts new file mode 100644 index 00000000..f7f66d85 --- /dev/null +++ b/pianohat/translations/d2cbfd1e-2c4b-4ddd-8a37-21f4c07b9ab3-en_US.ts @@ -0,0 +1,4 @@ + + + + \ No newline at end of file