From 4d80fcb0f3a337fd9756d3713a76e971e9480c93 Mon Sep 17 00:00:00 2001 From: NicoHood Date: Sun, 8 Nov 2015 16:26:55 +0100 Subject: [PATCH 1/5] Added HID Device Former-commit-id: 6284152fd9ffdad69fa1bbd4490da9547c4dbe87 --- libsrc/leddevice/LedHIDDevice.cpp | 152 ++++++++++++++++++++++++++++++ libsrc/leddevice/LedHIDDevice.h | 66 +++++++++++++ 2 files changed, 218 insertions(+) create mode 100644 libsrc/leddevice/LedHIDDevice.cpp create mode 100644 libsrc/leddevice/LedHIDDevice.h diff --git a/libsrc/leddevice/LedHIDDevice.cpp b/libsrc/leddevice/LedHIDDevice.cpp new file mode 100644 index 00000000..e1506560 --- /dev/null +++ b/libsrc/leddevice/LedHIDDevice.cpp @@ -0,0 +1,152 @@ + +// STL includes +#include +#include + +// Qt includes +#include + +// Local Hyperion includes +#include "LedHIDDevice.h" + +LedHIDDevice::LedHIDDevice(const unsigned short VendorId, const unsigned short ProductId, int delayAfterConnect_ms, const bool useFeature) : + _VendorId(VendorId), + _ProductId(ProductId), + _useFeature(useFeature), + _deviceHandle(nullptr), + _delayAfterConnect_ms(delayAfterConnect_ms), + _blockedForDelay(false) +{ + // empty +} + +LedHIDDevice::~LedHIDDevice() +{ + if (_deviceHandle != nullptr) + { + hid_close(_deviceHandle); + _deviceHandle = nullptr; + } + + hid_exit(); +} + +int LedHIDDevice::open() +{ + // Initialize the usb context + int error = hid_init(); + if (error != 0) + { + std::cerr << "Error while initializing the hidapi context" << std::endl; + return -1; + } + std::cout << "Hidapi initialized" << std::endl; + + // Open the device + printf("Opening device: VID %04hx PID %04hx\n", _VendorId, _ProductId); + _deviceHandle = hid_open(_VendorId, _ProductId, nullptr); + + if (_deviceHandle == nullptr) + { + // Failed to open the device + std::cerr << "Failed to open HID device. Maybe your PID/VID setting is wrong?" << std::endl; + + // http://www.signal11.us/oss/hidapi/ + /* + std::cout << "Showing a list of all available HID devices:" << std::endl; + auto devs = hid_enumerate(0x00, 0x00); + auto cur_dev = devs; + while (cur_dev) { + printf("Device Found\n type: %04hx %04hx\n path: %s\n serial_number: %ls", + cur_dev->vendor_id, cur_dev->product_id, cur_dev->path, cur_dev->serial_number); + printf("\n"); + printf(" Manufacturer: %ls\n", cur_dev->manufacturer_string); + printf(" Product: %ls\n", cur_dev->product_string); + printf("\n"); + cur_dev = cur_dev->next; + } + hid_free_enumeration(devs); + */ + + return -1; + } + else{ + std::cout << "Opened HID device successful" << std::endl; + } + + // Wait after device got opened if enabled + if (_delayAfterConnect_ms > 0) + { + _blockedForDelay = true; + QTimer::singleShot(_delayAfterConnect_ms, this, SLOT(unblockAfterDelay())); + std::cout << "Device blocked for " << _delayAfterConnect_ms << " ms" << std::endl; + } + + return 0; +} + + +int LedHIDDevice::writeBytes(const unsigned size, const uint8_t * data) +{ + if (_blockedForDelay) { + return 0; + } + + if (_deviceHandle == nullptr) + { + // try to reopen + auto status = open(); + if(status < 0){ + // Try again in 3 seconds + int seconds = 3000; + _blockedForDelay = true; + QTimer::singleShot(seconds, this, SLOT(unblockAfterDelay())); + std::cout << "Device blocked for " << seconds << " ms" << std::endl; + } + // Return here, to not write led data if the device should be blocked after connect + return status; + } + + // Prepend report ID to the buffer + uint8_t ledData[size + 1]; + ledData[0] = 0; // Report ID + memcpy(ledData + 1, data, size_t(size)); + + // Send data via feature or out report + int ret; + if(_useFeature){ + ret = hid_send_feature_report(_deviceHandle, ledData, size + 1); + } + else{ + ret = hid_write(_deviceHandle, ledData, size + 1); + } + + // Handle first error + if(ret < 0){ + std::cerr << "Failed to write to HID device." << std::endl; + + // Try again + if(_useFeature){ + ret = hid_send_feature_report(_deviceHandle, ledData, size + 1); + } + else{ + ret = hid_write(_deviceHandle, ledData, size + 1); + } + + // Writing failed again, device might have disconnected + if(ret < 0){ + std::cerr << "Failed to write to HID device." << std::endl; + + hid_close(_deviceHandle); + _deviceHandle = nullptr; + } + } + + return ret; +} + +void LedHIDDevice::unblockAfterDelay() +{ + std::cout << "Device unblocked" << std::endl; + _blockedForDelay = false; +} diff --git a/libsrc/leddevice/LedHIDDevice.h b/libsrc/leddevice/LedHIDDevice.h new file mode 100644 index 00000000..fb4aa6b3 --- /dev/null +++ b/libsrc/leddevice/LedHIDDevice.h @@ -0,0 +1,66 @@ +#pragma once + +#include + +// libusb include +#include + +// Leddevice includes +#include + +/// +/// The LedHIDDevice implements an abstract base-class for LedDevices using an HID-device. +/// +class LedHIDDevice : public QObject, public LedDevice +{ + Q_OBJECT + +public: + /// + /// Constructs the LedDevice attached to an HID-device + /// + /// @param[in] VendorId The USB VID of the output device + /// @param[in] ProductId The USB PID of the output device + /// + LedHIDDevice(const unsigned short VendorId, const unsigned short ProductId, int delayAfterConnect_ms = 0, const bool useFeature = false); + + /// + /// Destructor of the LedDevice; closes the output device if it is open + /// + virtual ~LedHIDDevice(); + + /// + /// Opens and configures the output device + /// + /// @return Zero on succes else negative + /// + int open(); +protected: + /** + * Writes the given bytes to the HID-device and + * + * @param[in[ size The length of the data + * @param[in] data The data + * + * @return Zero on succes else negative + */ + int writeBytes(const unsigned size, const uint8_t *data); + +private slots: + /// Unblock the device after a connection delay + void unblockAfterDelay(); + +private: + // HID VID and PID + const unsigned short _VendorId; + const unsigned short _ProductId; + const bool _useFeature; + + /// libusb device handle + hid_device * _deviceHandle; + + /// Sleep after the connect before continuing + const int _delayAfterConnect_ms; + + bool _blockedForDelay; +}; From 29d3209d7de9b0e8ea5c08c94cc6298bf8d03fb9 Mon Sep 17 00:00:00 2001 From: NicoHood Date: Sun, 8 Nov 2015 16:31:18 +0100 Subject: [PATCH 2/5] Added Raw HID device Former-commit-id: bd36530b6b63959ee4d83693e396cbb0af70ddb3 --- libsrc/leddevice/CMakeLists.txt | 4 ++ libsrc/leddevice/LedDeviceFactory.cpp | 16 ++++++++ libsrc/leddevice/LedDeviceRawHID.cpp | 57 +++++++++++++++++++++++++++ libsrc/leddevice/LedDeviceRawHID.h | 48 ++++++++++++++++++++++ 4 files changed, 125 insertions(+) create mode 100644 libsrc/leddevice/LedDeviceRawHID.cpp create mode 100644 libsrc/leddevice/LedDeviceRawHID.h diff --git a/libsrc/leddevice/CMakeLists.txt b/libsrc/leddevice/CMakeLists.txt index 16463fb5..d6d4682c 100755 --- a/libsrc/leddevice/CMakeLists.txt +++ b/libsrc/leddevice/CMakeLists.txt @@ -18,6 +18,8 @@ SET(Leddevice_QT_HEADERS ${CURRENT_SOURCE_DIR}/LedDeviceAdalightApa102.h ${CURRENT_SOURCE_DIR}/LedDeviceAmbiLed.h ${CURRENT_SOURCE_DIR}/LedDevicePhilipsHue.h + ${CURRENT_SOURCE_DIR}/LedHIDDevice.h + ${CURRENT_SOURCE_DIR}/LedDeviceRawHID.h ) SET(Leddevice_HEADERS @@ -39,10 +41,12 @@ SET(Leddevice_SOURCES ${CURRENT_SOURCE_DIR}/LedDeviceFactory.cpp ${CURRENT_SOURCE_DIR}/LedRs232Device.cpp + ${CURRENT_SOURCE_DIR}/LedHIDDevice.cpp ${CURRENT_SOURCE_DIR}/LedDeviceAdalight.cpp ${CURRENT_SOURCE_DIR}/LedDeviceAdalightApa102.cpp ${CURRENT_SOURCE_DIR}/LedDeviceAmbiLed.cpp + ${CURRENT_SOURCE_DIR}/LedDeviceRawHID.cpp ${CURRENT_SOURCE_DIR}/LedDeviceLightpack.cpp ${CURRENT_SOURCE_DIR}/LedDeviceMultiLightpack.cpp ${CURRENT_SOURCE_DIR}/LedDevicePaintpack.cpp diff --git a/libsrc/leddevice/LedDeviceFactory.cpp b/libsrc/leddevice/LedDeviceFactory.cpp index ce8cb835..d5d40611 100755 --- a/libsrc/leddevice/LedDeviceFactory.cpp +++ b/libsrc/leddevice/LedDeviceFactory.cpp @@ -23,6 +23,7 @@ #include "LedDeviceAdalight.h" #include "LedDeviceAmbiLed.h" +#include "LedDeviceRawHID.h" #include "LedDeviceLightpack.h" #include "LedDeviceMultiLightpack.h" #include "LedDevicePaintpack.h" @@ -147,6 +148,21 @@ LedDevice * LedDeviceFactory::construct(const Json::Value & deviceConfig) device = deviceTinkerforge; } #endif + else if (type == "rawhid") + { + const int delay_ms = deviceConfig["delayAfterConnect"].asInt(); + auto VendorIdString = deviceConfig.get("VID", "0x2341").asString(); + auto ProductIdString = deviceConfig.get("PID", "0x8036").asString(); + + // Convert HEX values to integer + auto VendorId = std::stoul(VendorIdString, nullptr, 16); + auto ProductId = std::stoul(ProductIdString, nullptr, 16); + + LedDeviceRawHID* deviceHID = new LedDeviceRawHID(VendorId, ProductId, delay_ms); + deviceHID->open(); + + device = deviceHID; + } else if (type == "lightpack") { const std::string output = deviceConfig.get("output", "").asString(); diff --git a/libsrc/leddevice/LedDeviceRawHID.cpp b/libsrc/leddevice/LedDeviceRawHID.cpp new file mode 100644 index 00000000..18d678c3 --- /dev/null +++ b/libsrc/leddevice/LedDeviceRawHID.cpp @@ -0,0 +1,57 @@ + +// STL includes +#include +#include +#include + +// Linux includes +#include +#include + +// hyperion local includes +#include "LedDeviceRawHID.h" + +// Use feature report HID device +LedDeviceRawHID::LedDeviceRawHID(const unsigned short VendorId, const unsigned short ProductId, int delayAfterConnect_ms) : + LedHIDDevice(VendorId, ProductId, delayAfterConnect_ms, true), + _ledBuffer(0), + _timer() +{ + // setup the timer + _timer.setSingleShot(false); + _timer.setInterval(5000); + connect(&_timer, SIGNAL(timeout()), this, SLOT(rewriteLeds())); + + // start the timer + _timer.start(); +} + +int LedDeviceRawHID::write(const std::vector & ledValues) +{ + // Resize buffer if required + if (_ledBuffer.size() < ledValues.size() * 3) { + _ledBuffer.resize(3 * ledValues.size()); + } + + // restart the timer + _timer.start(); + + // write data + memcpy(_ledBuffer.data(), ledValues.data(), ledValues.size() * 3); + return writeBytes(_ledBuffer.size(), _ledBuffer.data()); +} + +int LedDeviceRawHID::switchOff() +{ + // restart the timer + _timer.start(); + + // write data + std::fill(_ledBuffer.begin(), _ledBuffer.end(), uint8_t(0)); + return writeBytes(_ledBuffer.size(), _ledBuffer.data()); +} + +void LedDeviceRawHID::rewriteLeds() +{ + writeBytes(_ledBuffer.size(), _ledBuffer.data()); +} diff --git a/libsrc/leddevice/LedDeviceRawHID.h b/libsrc/leddevice/LedDeviceRawHID.h new file mode 100644 index 00000000..6e23fe02 --- /dev/null +++ b/libsrc/leddevice/LedDeviceRawHID.h @@ -0,0 +1,48 @@ +#pragma once + +// STL includes +#include + +// Qt includes +#include + +// hyperion include +#include "LedHIDDevice.h" + +/// +/// Implementation of the LedDevice interface for writing to an RawHID led device. +/// +class LedDeviceRawHID : public LedHIDDevice +{ + Q_OBJECT + +public: + /// + /// Constructs the LedDevice for attached RawHID device + /// + LedDeviceRawHID(const unsigned short VendorId, const unsigned short ProductId, int delayAfterConnect_ms); + + /// + /// Writes the led color values to the led-device + /// + /// @param ledValues The color-value per led + /// @return Zero on succes else negative + /// + virtual int write(const std::vector & ledValues); + + /// Switch the leds off + virtual int switchOff(); + +private slots: + /// Write the last data to the leds again + void rewriteLeds(); + +private: + /// The buffer containing the packed RGB values + std::vector _ledBuffer; + + /// Timer object which makes sure that led data is written at a minimum rate + /// The RawHID device will switch off when it does not receive data at least + /// every 15 seconds + QTimer _timer; +}; From a7d9a44dcc6088644dd50517525091ab1ec71d65 Mon Sep 17 00:00:00 2001 From: NicoHood Date: Sun, 8 Nov 2015 16:31:41 +0100 Subject: [PATCH 3/5] Fixed adalight typos Former-commit-id: c4bccd1e6c036b244f69283d2edff41c618ffc1b --- libsrc/leddevice/LedDeviceAdalight.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libsrc/leddevice/LedDeviceAdalight.h b/libsrc/leddevice/LedDeviceAdalight.h index 66d299d1..650fdfb1 100644 --- a/libsrc/leddevice/LedDeviceAdalight.h +++ b/libsrc/leddevice/LedDeviceAdalight.h @@ -6,7 +6,7 @@ // Qt includes #include -// hyperion incluse +// hyperion include #include "LedRs232Device.h" /// From 3595709099397d8c72a16b973b17925262af248a Mon Sep 17 00:00:00 2001 From: NicoHood Date: Sun, 8 Nov 2015 16:32:53 +0100 Subject: [PATCH 4/5] Adapted Paintpack to HIDDevice API Former-commit-id: 06101f0e99fbe99f8994193cea337b400acbafe3 --- libsrc/leddevice/LedDeviceFactory.cpp | 10 +++- libsrc/leddevice/LedDevicePaintpack.cpp | 65 ++++++------------------- libsrc/leddevice/LedDevicePaintpack.h | 26 ++-------- 3 files changed, 27 insertions(+), 74 deletions(-) diff --git a/libsrc/leddevice/LedDeviceFactory.cpp b/libsrc/leddevice/LedDeviceFactory.cpp index d5d40611..85f665a5 100755 --- a/libsrc/leddevice/LedDeviceFactory.cpp +++ b/libsrc/leddevice/LedDeviceFactory.cpp @@ -181,7 +181,15 @@ LedDevice * LedDeviceFactory::construct(const Json::Value & deviceConfig) } else if (type == "paintpack") { - LedDevicePaintpack * devicePainLightpack = new LedDevicePaintpack(); + const int delay_ms = deviceConfig["delayAfterConnect"].asInt(); + auto VendorIdString = deviceConfig.get("VID", "0x0EBF").asString(); + auto ProductIdString = deviceConfig.get("PID", "0x0025").asString(); + + // Convert HEX values to integer + auto VendorId = std::stoul(VendorIdString, nullptr, 16); + auto ProductId = std::stoul(ProductIdString, nullptr, 16); + + LedDevicePaintpack * devicePainLightpack = new LedDevicePaintpack(VendorId, ProductId, delay_ms); devicePainLightpack->open(); device = devicePainLightpack; diff --git a/libsrc/leddevice/LedDevicePaintpack.cpp b/libsrc/leddevice/LedDevicePaintpack.cpp index 7ed0c847..db87bf49 100644 --- a/libsrc/leddevice/LedDevicePaintpack.cpp +++ b/libsrc/leddevice/LedDevicePaintpack.cpp @@ -2,61 +2,25 @@ // Hyperion includes #include "LedDevicePaintpack.h" -LedDevicePaintpack::LedDevicePaintpack() : - LedDevice(), - _deviceHandle(nullptr) +// Use out report HID device +LedDevicePaintpack::LedDevicePaintpack(const unsigned short VendorId, const unsigned short ProductId, int delayAfterConnect_ms) : + LedHIDDevice(VendorId, ProductId, delayAfterConnect_ms, false), + _ledBuffer(0) { // empty } -int LedDevicePaintpack::open() + +int LedDevicePaintpack::write(const std::vector & ledValues) { - // initialize the usb context - int error = hid_init(); - if (error != 0) + if (_ledBuffer.size() < 2 + ledValues.size()*3) { - std::cerr << "Error while initializing the hidapi context" << std::endl; - return -1; - } - std::cout << "Hidapi initialized" << std::endl; - - // Initialise the paintpack device - const unsigned short Paintpack_VendorId = 0x0ebf; - const unsigned short Paintpack_ProductId = 0x0025; - _deviceHandle = hid_open(Paintpack_VendorId, Paintpack_ProductId, nullptr); - if (_deviceHandle == nullptr) - { - // Failed to open the device - std::cerr << "Failed to open HID Paintpakc device " << std::endl; - return -1; + _ledBuffer.resize(2 + ledValues.size()*3, uint8_t(0)); + _ledBuffer[0] = 3; + _ledBuffer[1] = 0; } - return 0; -} - -LedDevicePaintpack::~LedDevicePaintpack() -{ - if (_deviceHandle != nullptr) - { - hid_close(_deviceHandle); - _deviceHandle = nullptr; - } - - hid_exit(); -} - -int LedDevicePaintpack::write(const std::vector& ledValues) -{ - if (_ledBuffer.size() < 3 + ledValues.size()*3) - { - _ledBuffer.resize(3 + ledValues.size()*3, uint8_t(0)); - - _ledBuffer[0] = 0; - _ledBuffer[1] = 3; - _ledBuffer[2] = 0; - } - - auto bufIt = _ledBuffer.begin()+3; + auto bufIt = _ledBuffer.begin()+2; for (const ColorRgb & ledValue : ledValues) { *bufIt = ledValue.red; @@ -67,11 +31,12 @@ int LedDevicePaintpack::write(const std::vector& ledValues) ++bufIt; } - return hid_write(_deviceHandle, _ledBuffer.data(), _ledBuffer.size()); + return writeBytes(_ledBuffer.size(), _ledBuffer.data()); } + int LedDevicePaintpack::switchOff() { - std::fill(_ledBuffer.begin()+3, _ledBuffer.end(), uint8_t(0)); - return hid_write(_deviceHandle, _ledBuffer.data(), _ledBuffer.size()); + std::fill(_ledBuffer.begin() + 2, _ledBuffer.end(), uint8_t(0)); + return writeBytes(_ledBuffer.size(), _ledBuffer.data()); } diff --git a/libsrc/leddevice/LedDevicePaintpack.h b/libsrc/leddevice/LedDevicePaintpack.h index 327dc123..3ed91eab 100644 --- a/libsrc/leddevice/LedDevicePaintpack.h +++ b/libsrc/leddevice/LedDevicePaintpack.h @@ -3,34 +3,19 @@ // STL includes #include -// libusb include -#include - // Hyperion includes -#include +#include "LedHIDDevice.h" /// /// LedDevice implementation for a paintpack device () /// -class LedDevicePaintpack : public LedDevice +class LedDevicePaintpack : public LedHIDDevice { public: /** * Constructs the paintpack device */ - LedDevicePaintpack(); - - /** - * Destructs the paintpack device, closes USB connection if open - */ - virtual ~LedDevicePaintpack(); - - /** - * Opens the Paintpack device - * - * @return Zero on succes else negative - */ - int open(); + LedDevicePaintpack(const unsigned short VendorId, const unsigned short ProductId, int delayAfterConnect_ms); /// /// Writes the RGB-Color values to the leds. @@ -49,11 +34,6 @@ public: virtual int switchOff(); private: - /// libusb device handle - hid_device * _deviceHandle; - /// buffer for led data std::vector _ledBuffer; - - }; From 1b2b3b3135ac0a57c59da136f2ef69bdcdb3420b Mon Sep 17 00:00:00 2001 From: NicoHood Date: Sun, 8 Nov 2015 21:05:12 +0100 Subject: [PATCH 5/5] Added udev/sudo note to HID Device Former-commit-id: 30e1524a5ca587cd5928aae028de868df2725aca --- libsrc/leddevice/LedHIDDevice.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libsrc/leddevice/LedHIDDevice.cpp b/libsrc/leddevice/LedHIDDevice.cpp index e1506560..0ed53524 100644 --- a/libsrc/leddevice/LedHIDDevice.cpp +++ b/libsrc/leddevice/LedHIDDevice.cpp @@ -49,7 +49,7 @@ int LedHIDDevice::open() if (_deviceHandle == nullptr) { // Failed to open the device - std::cerr << "Failed to open HID device. Maybe your PID/VID setting is wrong?" << std::endl; + std::cerr << "Failed to open HID device. Maybe your PID/VID setting is wrong? Make sure to add a udev rule/use sudo." << std::endl; // http://www.signal11.us/oss/hidapi/ /*