Lightpack device added based on hidapi

Former-commit-id: 4d3d9c01b169991757587a67479094186d52e6e8
This commit is contained in:
johan 2013-11-23 11:14:27 +01:00
parent d1c1e56bd1
commit b2f52aad89
7 changed files with 409 additions and 26 deletions

View File

@ -1,13 +1,11 @@
project(hidapi) project(hidapi)
#add libusb and pthreads #add libusb and pthreads
find_package(UDev REQUIRED)
find_package(libusb-1.0 REQUIRED) find_package(libusb-1.0 REQUIRED)
find_package(Threads REQUIRED) find_package(Threads REQUIRED)
include_directories( include_directories(
../../include/hidapi ../../include/hidapi
${UDEV_INCLUDE_DIR}
${LIBUSB_1_INCLUDE_DIRS}) ${LIBUSB_1_INCLUDE_DIRS})
add_library(hidapi-libusb hid-libusb.c) add_library(hidapi-libusb hid-libusb.c)
@ -15,4 +13,4 @@ add_library(hidapi-libusb hid-libusb.c)
target_link_libraries(hidapi-libusb target_link_libraries(hidapi-libusb
${LIBUSB_1_LIBRARIES} #apt-get install libusb-1.0-0-dev ${LIBUSB_1_LIBRARIES} #apt-get install libusb-1.0-0-dev
${CMAKE_THREAD_LIBS_INIT} ${CMAKE_THREAD_LIBS_INIT}
${UDEV_LIBRARIES}) # apt-get install libudev-dev )

View File

@ -20,7 +20,7 @@
files located at the root of the source distribution. files located at the root of the source distribution.
These files may also be found in the public source These files may also be found in the public source
code repository located at: code repository located at:
http://github.com/signal11/hidapi . http://github.com/signal11/hidapi .
********************************************************/ ********************************************************/
#define _GNU_SOURCE /* needed for wcsdup() before glibc 2.10 */ #define _GNU_SOURCE /* needed for wcsdup() before glibc 2.10 */
@ -167,9 +167,9 @@ static uint32_t get_bytes(uint8_t *rpt, size_t len, size_t num_bytes, size_t cur
} }
else if (num_bytes == 4) { else if (num_bytes == 4) {
return (rpt[cur+4] * 0x01000000 + return (rpt[cur+4] * 0x01000000 +
rpt[cur+3] * 0x00010000 + rpt[cur+3] * 0x00010000 +
rpt[cur+2] * 0x00000100 + rpt[cur+2] * 0x00000100 +
rpt[cur+1] * 0x00000001); rpt[cur+1] * 0x00000001);
} }
else else
return 0; return 0;
@ -180,7 +180,7 @@ static uint32_t get_bytes(uint8_t *rpt, size_t len, size_t num_bytes, size_t cur
Usage and Usage Page that it finds in the descriptor. Usage and Usage Page that it finds in the descriptor.
The return value is 0 on success and -1 on failure. */ The return value is 0 on success and -1 on failure. */
static int get_usage(uint8_t *report_descriptor, size_t size, static int get_usage(uint8_t *report_descriptor, size_t size,
unsigned short *usage_page, unsigned short *usage) unsigned short *usage_page, unsigned short *usage)
{ {
unsigned int i = 0; unsigned int i = 0;
int size_code; int size_code;
@ -473,7 +473,7 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id,
/* Check the VID/PID against the arguments */ /* Check the VID/PID against the arguments */
if ((vendor_id == 0x0 || vendor_id == dev_vid) && if ((vendor_id == 0x0 || vendor_id == dev_vid) &&
(product_id == 0x0 || product_id == dev_pid)) { (product_id == 0x0 || product_id == dev_pid)) {
struct hid_device_info *tmp; struct hid_device_info *tmp;
/* VID/PID match. Create the record. */ /* VID/PID match. Create the record. */
@ -617,7 +617,7 @@ hid_device * hid_open(unsigned short vendor_id, unsigned short product_id, const
cur_dev = devs; cur_dev = devs;
while (cur_dev) { while (cur_dev) {
if (cur_dev->vendor_id == vendor_id && if (cur_dev->vendor_id == vendor_id &&
cur_dev->product_id == product_id) { cur_dev->product_id == product_id) {
if (serial_number) { if (serial_number) {
if (wcscmp(serial_number, cur_dev->serial_number) == 0) { if (wcscmp(serial_number, cur_dev->serial_number) == 0) {
path_to_open = cur_dev->path; path_to_open = cur_dev->path;
@ -744,9 +744,9 @@ static void *read_thread(void *param)
/* Break out of this loop only on fatal error.*/ /* Break out of this loop only on fatal error.*/
if (res != LIBUSB_ERROR_BUSY && if (res != LIBUSB_ERROR_BUSY &&
res != LIBUSB_ERROR_TIMEOUT && res != LIBUSB_ERROR_TIMEOUT &&
res != LIBUSB_ERROR_OVERFLOW && res != LIBUSB_ERROR_OVERFLOW &&
res != LIBUSB_ERROR_INTERRUPTED) { res != LIBUSB_ERROR_INTERRUPTED) {
break; break;
} }
} }
@ -863,23 +863,23 @@ hid_device * HID_API_EXPORT hid_open_path(const char *path)
endpoint. */ endpoint. */
int is_interrupt = int is_interrupt =
(ep->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) (ep->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK)
== LIBUSB_TRANSFER_TYPE_INTERRUPT; == LIBUSB_TRANSFER_TYPE_INTERRUPT;
int is_output = int is_output =
(ep->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) (ep->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK)
== LIBUSB_ENDPOINT_OUT; == LIBUSB_ENDPOINT_OUT;
int is_input = int is_input =
(ep->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) (ep->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK)
== LIBUSB_ENDPOINT_IN; == LIBUSB_ENDPOINT_IN;
/* Decide whether to use it for intput or output. */ /* Decide whether to use it for intput or output. */
if (dev->input_endpoint == 0 && if (dev->input_endpoint == 0 &&
is_interrupt && is_input) { is_interrupt && is_input) {
/* Use this endpoint for INPUT */ /* Use this endpoint for INPUT */
dev->input_endpoint = ep->bEndpointAddress; dev->input_endpoint = ep->bEndpointAddress;
dev->input_ep_max_packet_size = ep->wMaxPacketSize; dev->input_ep_max_packet_size = ep->wMaxPacketSize;
} }
if (dev->output_endpoint == 0 && if (dev->output_endpoint == 0 &&
is_interrupt && is_output) { is_interrupt && is_output) {
/* Use this endpoint for OUTPUT */ /* Use this endpoint for OUTPUT */
dev->output_endpoint = ep->bEndpointAddress; dev->output_endpoint = ep->bEndpointAddress;
} }

View File

@ -4,13 +4,10 @@ SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/hyperion)
SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/hyperion) SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/hyperion)
#add libusb and pthreads (required for the Lighpack usb device) #add libusb and pthreads (required for the Lighpack usb device)
find_package(UDev REQUIRED)
find_package(libusb-1.0 REQUIRED) find_package(libusb-1.0 REQUIRED)
find_package(Threads REQUIRED)
include_directories( include_directories(
${UDEV_INCLUDE_DIR} ${LIBUSB_1_INCLUDE_DIRS}) # for Lightpack device
${LIBUSB_1_INCLUDE_DIRS})
# Group the headers that go through the MOC compiler # Group the headers that go through the MOC compiler
SET(Hyperion_QT_HEADERS SET(Hyperion_QT_HEADERS
@ -40,6 +37,7 @@ SET(Hyperion_HEADERS
${CURRENT_SOURCE_DIR}/device/LedDeviceLpd8806.h ${CURRENT_SOURCE_DIR}/device/LedDeviceLpd8806.h
${CURRENT_SOURCE_DIR}/device/LedDeviceLightpack.h ${CURRENT_SOURCE_DIR}/device/LedDeviceLightpack.h
${CURRENT_SOURCE_DIR}/device/LedDeviceMultiLightpack.h ${CURRENT_SOURCE_DIR}/device/LedDeviceMultiLightpack.h
${CURRENT_SOURCE_DIR}/device/LedDeviceLightpack-hidapi.h
) )
SET(Hyperion_SOURCES SET(Hyperion_SOURCES
@ -64,6 +62,7 @@ SET(Hyperion_SOURCES
${CURRENT_SOURCE_DIR}/device/LedDeviceAdalight.cpp ${CURRENT_SOURCE_DIR}/device/LedDeviceAdalight.cpp
${CURRENT_SOURCE_DIR}/device/LedDeviceLightpack.cpp ${CURRENT_SOURCE_DIR}/device/LedDeviceLightpack.cpp
${CURRENT_SOURCE_DIR}/device/LedDeviceMultiLightpack.cpp ${CURRENT_SOURCE_DIR}/device/LedDeviceMultiLightpack.cpp
${CURRENT_SOURCE_DIR}/device/LedDeviceLightpack-hidapi.cpp
) )
set(Hyperion_RESOURCES set(Hyperion_RESOURCES
@ -85,7 +84,6 @@ add_library(hyperion
target_link_libraries(hyperion target_link_libraries(hyperion
hyperion-utils hyperion-utils
serialport serialport
hidapi-libusb
${QT_LIBRARIES} ${QT_LIBRARIES}
${LIBUSB_1_LIBRARIES} #apt-get install libusb-1.0-0-dev )
${CMAKE_THREAD_LIBS_INIT}
${UDEV_LIBRARIES}) # apt-get install libudev-dev

View File

@ -20,6 +20,7 @@
#include "device/LedDeviceWs2801.h" #include "device/LedDeviceWs2801.h"
#include "device/LedDeviceAdalight.h" #include "device/LedDeviceAdalight.h"
#include "device/LedDeviceLightpack.h" #include "device/LedDeviceLightpack.h"
#include "device/LedDeviceLightpack-hidapi.h"
#include "device/LedDeviceMultiLightpack.h" #include "device/LedDeviceMultiLightpack.h"
#include "LinearColorSmoothing.h" #include "LinearColorSmoothing.h"
@ -94,6 +95,15 @@ LedDevice* Hyperion::createDevice(const Json::Value& deviceConfig)
device = deviceLightpack; device = deviceLightpack;
} }
else if (type == "lightpack-hidapi")
{
const std::string output = deviceConfig.get("output", "").asString();
LedDeviceLightpackHidapi* deviceLightpack = new LedDeviceLightpackHidapi();
deviceLightpack->open(output);
device = deviceLightpack;
}
else if (type == "multi-lightpack") else if (type == "multi-lightpack")
{ {
LedDeviceMultiLightpack* deviceLightpack = new LedDeviceMultiLightpack(); LedDeviceMultiLightpack* deviceLightpack = new LedDeviceMultiLightpack();

View File

@ -0,0 +1,270 @@
// stl includes
#include <exception>
#include <cstring>
#include <wchar.h>
// Local Hyperion includes
#include "LedDeviceLightpack-hidapi.h"
// from USB_ID.h (http://code.google.com/p/light-pack/source/browse/CommonHeaders/USB_ID.h)
#define USB_OLD_VENDOR_ID 0x03EB
#define USB_OLD_PRODUCT_ID 0x204F
#define USB_VENDOR_ID 0x1D50
#define USB_PRODUCT_ID 0x6022
#define LIGHTPACK_INTERFACE 0
// from commands.h (http://code.google.com/p/light-pack/source/browse/CommonHeaders/commands.h)
// Commands to device, sends it in first byte of data[]
enum COMMANDS{
CMD_UPDATE_LEDS = 1,
CMD_OFF_ALL,
CMD_SET_TIMER_OPTIONS,
CMD_SET_PWM_LEVEL_MAX_VALUE, /* deprecated */
CMD_SET_SMOOTH_SLOWDOWN,
CMD_SET_BRIGHTNESS,
CMD_NOP = 0x0F
};
// from commands.h (http://code.google.com/p/light-pack/source/browse/CommonHeaders/commands.h)
enum DATA_VERSION_INDEXES{
INDEX_FW_VER_MAJOR = 1,
INDEX_FW_VER_MINOR
};
LedDeviceLightpackHidapi::LedDeviceLightpackHidapi() :
LedDevice(),
_deviceHandle(nullptr),
_serialNumber(""),
_firmwareVersion({-1,-1}),
_ledCount(-1),
_bitsPerChannel(-1),
_ledBuffer()
{
}
LedDeviceLightpackHidapi::~LedDeviceLightpackHidapi()
{
if (_deviceHandle != nullptr)
{
hid_close(_deviceHandle);
_deviceHandle = nullptr;
}
// TODO: Should be called to avoid memory loss, but only at the end of the application
//hid_exit();
}
int LedDeviceLightpackHidapi::open(const std::string & serialNumber)
{
// 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;
// retrieve the list of usb devices
hid_device_info * deviceList = hid_enumerate(0x0, 0x0);
// iterate the list of devices
for (hid_device_info * deviceInfo = deviceList; deviceInfo != nullptr; deviceInfo = deviceInfo->next)
{
// try to open and initialize the device
error = testAndOpen(deviceInfo, serialNumber);
if (error == 0)
{
// a device was sucessfully opened. break from list
break;
}
}
// free the device list
hid_free_enumeration(deviceList);
if (_deviceHandle == nullptr)
{
if (_serialNumber.empty())
{
std::cerr << "No Lightpack device has been found" << std::endl;
}
else
{
std::cerr << "No Lightpack device has been found with serial " << _serialNumber << std::endl;
}
}
return _deviceHandle == nullptr ? -1 : 0;
}
int LedDeviceLightpackHidapi::testAndOpen(hid_device_info *device, const std::string & requestedSerialNumber)
{
if ((device->vendor_id == USB_VENDOR_ID && device->product_id == USB_PRODUCT_ID) ||
(device->vendor_id == USB_OLD_VENDOR_ID && device->product_id == USB_OLD_PRODUCT_ID))
{
std::cout << "Found a lightpack device. Retrieving more information..." << std::endl;
// get the serial number
std::string serialNumber = "";
if (device->serial_number != nullptr)
{
// the serial number needs to be converted to a char array instead of wchar
size_t size = wcslen(device->serial_number);
serialNumber.resize(size, '.');
for (size_t i = 0; i < size; ++i)
{
int c = wctob(device->serial_number[i]);
if (c != EOF)
{
serialNumber[i] = c;
}
}
}
else
{
std::cerr << "No serial number for Lightpack device" << std::endl;
}
std::cout << "Lightpack device found: path=" << device->path << " serial=" << serialNumber << std::endl;
// check if this is the device we are looking for
if (requestedSerialNumber.empty() || requestedSerialNumber == serialNumber)
{
// This is it!
_deviceHandle = hid_open_path(device->path);
if (_deviceHandle != nullptr)
{
_serialNumber = serialNumber;
std::cout << "Lightpack device successfully opened" << std::endl;
// get the firmware version
uint8_t buffer[256];
buffer[0] = 0; // report id
int error = hid_get_feature_report(_deviceHandle, buffer, sizeof(buffer));
if (error < 4)
{
std::cerr << "Unable to retrieve firmware version number from Lightpack device" << std::endl;
}
else
{
_firmwareVersion.majorVersion = buffer[INDEX_FW_VER_MAJOR+1];
_firmwareVersion.minorVersion = buffer[INDEX_FW_VER_MINOR+1];
}
// FOR TESTING PURPOSE: FORCE MAJOR VERSION TO 6
_firmwareVersion.majorVersion = 6;
// disable smoothing of the chosen device
disableSmoothing();
// determine the number of leds
if (_firmwareVersion.majorVersion == 4)
{
_ledCount = 8;
}
else
{
_ledCount = 10;
}
// determine the bits per channel
if (_firmwareVersion.majorVersion == 6)
{
// maybe also or version 7? The firmware suggest this is only for 6... (2013-11-13)
_bitsPerChannel = 12;
}
else
{
_bitsPerChannel = 8;
}
// set the led buffer size (repport id + command + 6 bytes per led)
_ledBuffer = std::vector<uint8_t>(2 + _ledCount * 6, 0);
_ledBuffer[0] = 0x0; // report id
_ledBuffer[1] = CMD_UPDATE_LEDS;
// return success
std::cout << "Lightpack device opened: path=" << device->path << " serial=" << _serialNumber << " version=" << _firmwareVersion.majorVersion << "." << _firmwareVersion.minorVersion << std::endl;
return 0;
}
else
{
std::cerr << "Unable to open Lightpack device. Searching for other device" << std::endl;
}
}
}
return -1;
}
int LedDeviceLightpackHidapi::write(const std::vector<ColorRgb> &ledValues)
{
return write(ledValues.data(), ledValues.size());
}
int LedDeviceLightpackHidapi::write(const ColorRgb * ledValues, int size)
{
int count = std::min(_ledCount, size);
for (int i = 0; i < count ; ++i)
{
const ColorRgb & color = ledValues[i];
// copy the most significant bits of the rgb values to the first three bytes
// offset 1 to accomodate for the report id and command byte
_ledBuffer[6*i+2] = color.red;
_ledBuffer[6*i+3] = color.green;
_ledBuffer[6*i+4] = color.blue;
// leave the next three bytes on zero...
// 12-bit values having zeros in the lowest 4 bits which is almost correct, but it saves extra
// switches to determine what to do and some bit shuffling
}
int error = writeBytes(_ledBuffer.data(), _ledBuffer.size());
return error >= 0 ? 0 : error;
}
int LedDeviceLightpackHidapi::switchOff()
{
unsigned char buf[2] = {0x0, CMD_OFF_ALL};
return writeBytes(buf, sizeof(buf)) == sizeof(buf);
}
const std::string &LedDeviceLightpackHidapi::getSerialNumber() const
{
return _serialNumber;
}
int LedDeviceLightpackHidapi::getLedCount() const
{
return _ledCount;
}
int LedDeviceLightpackHidapi::writeBytes(uint8_t *data, int size)
{
// std::cout << "Writing " << size << " bytes: ";
// for (int i = 0; i < size ; ++i) printf("%02x ", data[i]);
// std::cout << std::endl;
int error = hid_send_feature_report(_deviceHandle, data, size);
if (error == size)
{
return 0;
}
std::cerr << "Unable to write " << size << " bytes to Lightpack device(" << error << ")" << std::endl;
return error;
}
int LedDeviceLightpackHidapi::disableSmoothing()
{
unsigned char buf[2] = {CMD_SET_SMOOTH_SLOWDOWN, 0};
return writeBytes(buf, sizeof(buf)) == sizeof(buf);
}

View File

@ -0,0 +1,107 @@
#pragma once
// stl includes
#include <vector>
#include <cstdint>
#include <string>
// libusb include
#include <hidapi/hidapi.h>
// Hyperion includes
#include <hyperion/LedDevice.h>
///
/// LedDevice implementation for a lightpack device (http://code.google.com/p/light-pack/)
///
class LedDeviceLightpackHidapi : public LedDevice
{
public:
///
/// Constructs the LedDeviceLightpack
///
LedDeviceLightpackHidapi();
///
/// Destructor of the LedDevice; closes the output device if it is open
///
virtual ~LedDeviceLightpackHidapi();
///
/// Opens and configures the output device
///
/// @return Zero on succes else negative
///
int open(const std::string & serialNumber = "");
///
/// Writes the RGB-Color values to the leds.
///
/// @param[in] ledValues The RGB-color per led
///
/// @return Zero on success else negative
///
virtual int write(const std::vector<ColorRgb>& ledValues);
///
/// Writes the RGB-Color values to the leds.
///
/// @param[in] ledValues Array of RGB values
/// @param[in] size The number of RGB values
///
/// @return Zero on success else negative
///
int write(const ColorRgb * ledValues, int size);
///
/// Switch the leds off
///
/// @return Zero on success else negative
///
virtual int switchOff();
/// Get the serial of the Lightpack
const std::string & getSerialNumber() const;
/// Get the number of leds
int getLedCount() const;
private:
///
/// Test if the device is a (or the) lightpack we are looking for
///
/// @return Zero on succes else negative
///
int testAndOpen(hid_device_info * device, const std::string & requestedSerialNumber);
/// write bytes to the device
int writeBytes(uint8_t *data, int size);
/// Disable the internal smoothing on the Lightpack device
int disableSmoothing();
struct Version
{
int majorVersion;
int minorVersion;
};
private:
/// libusb device handle
hid_device * _deviceHandle;
/// device serial number
std::string _serialNumber;
/// firmware version of the device
Version _firmwareVersion;
/// the number of leds of the device
int _ledCount;
/// the number of bits per channel
int _bitsPerChannel;
/// buffer for led data
std::vector<uint8_t> _ledBuffer;
};

View File

@ -279,7 +279,7 @@ int LedDeviceLightpack::writeBytes(uint8_t *data, int size)
int error = libusb_control_transfer(_deviceHandle, int error = libusb_control_transfer(_deviceHandle,
LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE, LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE,
LIBUSB_REQUEST_SET_CONFIGURATION, 0x09,
(2 << 8), (2 << 8),
0x00, 0x00,
data, size, 1000); data, size, 1000);