First Lighpack device version.

Former-commit-id: 2133e13ed421d363fa28d3f765607e455088618b
This commit is contained in:
johan 2013-11-13 20:15:53 +01:00
parent 2bc84d63bf
commit 12c925d77f
9 changed files with 502 additions and 11 deletions

View File

@ -37,6 +37,9 @@ find_package(Qt4 COMPONENTS QtCore QtGui QtNetwork REQUIRED QUIET)
# add protocol buffers # add protocol buffers
find_package(Protobuf REQUIRED) find_package(Protobuf REQUIRED)
#add libusb
find_package(libusb-1.0 REQUIRED)
#SET(QT_DONT_USE_QTGUI TRUE) #SET(QT_DONT_USE_QTGUI TRUE)
#SET(QT_USE_QTCONSOLE TRUE) #SET(QT_USE_QTCONSOLE TRUE)
include(${QT_USE_FILE}) include(${QT_USE_FILE})

View File

@ -1,6 +1,6 @@
ON RASPBERRY ON RASPBERRY
-------------- --------------
sudo apt-get install libprotobuf-dev libQt4-dev rsync sudo apt-get install libprotobuf-dev libQt4-dev libusb-1.0-0-dev rsync
ON HOST ON HOST
--------- ---------

View File

@ -0,0 +1,98 @@
# - Try to find libusb-1.0
# Once done this will define
#
# LIBUSB_1_FOUND - system has libusb
# LIBUSB_1_INCLUDE_DIRS - the libusb include directory
# LIBUSB_1_LIBRARIES - Link these to use libusb
# LIBUSB_1_DEFINITIONS - Compiler switches required for using libusb
#
# Adapted from cmake-modules Google Code project
#
# Copyright (c) 2006 Andreas Schneider <mail@cynapses.org>
#
# (Changes for libusb) Copyright (c) 2008 Kyle Machulis <kyle@nonpolynomial.com>
#
# Redistribution and use is allowed according to the terms of the New BSD license.
#
# CMake-Modules Project New BSD License
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# * Neither the name of the CMake-Modules Project nor the names of its
# contributors may be used to endorse or promote products derived from this
# software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
if (LIBUSB_1_LIBRARIES AND LIBUSB_1_INCLUDE_DIRS)
# in cache already
set(LIBUSB_FOUND TRUE)
else (LIBUSB_1_LIBRARIES AND LIBUSB_1_INCLUDE_DIRS)
find_path(LIBUSB_1_INCLUDE_DIR
NAMES
libusb.h
PATHS
/usr/include
/usr/local/include
/opt/local/include
/sw/include
PATH_SUFFIXES
libusb-1.0
)
find_library(LIBUSB_1_LIBRARY
NAMES
usb-1.0 usb
PATHS
/usr/lib
/usr/local/lib
/opt/local/lib
/sw/lib
)
set(LIBUSB_1_INCLUDE_DIRS
${LIBUSB_1_INCLUDE_DIR}
)
set(LIBUSB_1_LIBRARIES
${LIBUSB_1_LIBRARY}
)
if (LIBUSB_1_INCLUDE_DIRS AND LIBUSB_1_LIBRARIES)
set(LIBUSB_1_FOUND TRUE)
endif (LIBUSB_1_INCLUDE_DIRS AND LIBUSB_1_LIBRARIES)
if (LIBUSB_1_FOUND)
if (NOT libusb_1_FIND_QUIETLY)
message(STATUS "Found libusb-1.0:")
message(STATUS " - Includes: ${LIBUSB_1_INCLUDE_DIRS}")
message(STATUS " - Libraries: ${LIBUSB_1_LIBRARIES}")
endif (NOT libusb_1_FIND_QUIETLY)
else (LIBUSB_1_FOUND)
if (libusb_1_FIND_REQUIRED)
message(FATAL_ERROR "Could not find libusb")
endif (libusb_1_FIND_REQUIRED)
endif (LIBUSB_1_FOUND)
# show the LIBUSB_1_INCLUDE_DIRS and LIBUSB_1_LIBRARIES variables only in the advanced view
mark_as_advanced(LIBUSB_1_INCLUDE_DIRS LIBUSB_1_LIBRARIES)
endif (LIBUSB_1_LIBRARIES AND LIBUSB_1_INCLUDE_DIRS)

View File

@ -3,6 +3,8 @@
SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/hyperion) 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)
include_directories(${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
${CURRENT_HEADER_DIR}/Hyperion.h ${CURRENT_HEADER_DIR}/Hyperion.h
@ -28,6 +30,7 @@ SET(Hyperion_HEADERS
${CURRENT_SOURCE_DIR}/device/LedDeviceWs2801.h ${CURRENT_SOURCE_DIR}/device/LedDeviceWs2801.h
${CURRENT_SOURCE_DIR}/device/LedDeviceLpd6803.h ${CURRENT_SOURCE_DIR}/device/LedDeviceLpd6803.h
${CURRENT_SOURCE_DIR}/device/LedDeviceAdalight.h ${CURRENT_SOURCE_DIR}/device/LedDeviceAdalight.h
${CURRENT_SOURCE_DIR}/device/LedDeviceLightpack.h
) )
SET(Hyperion_SOURCES SET(Hyperion_SOURCES
@ -49,6 +52,7 @@ SET(Hyperion_SOURCES
${CURRENT_SOURCE_DIR}/device/LedDeviceWs2801.cpp ${CURRENT_SOURCE_DIR}/device/LedDeviceWs2801.cpp
${CURRENT_SOURCE_DIR}/device/LedDeviceLpd6803.cpp ${CURRENT_SOURCE_DIR}/device/LedDeviceLpd6803.cpp
${CURRENT_SOURCE_DIR}/device/LedDeviceAdalight.cpp ${CURRENT_SOURCE_DIR}/device/LedDeviceAdalight.cpp
${CURRENT_SOURCE_DIR}/device/LedDeviceLightpack.cpp
) )
set(Hyperion_RESOURCES set(Hyperion_RESOURCES
@ -70,4 +74,5 @@ add_library(hyperion
target_link_libraries(hyperion target_link_libraries(hyperion
hyperion-utils hyperion-utils
serialport serialport
${QT_LIBRARIES}) ${QT_LIBRARIES}
${LIBUSB_1_LIBRARIES})

View File

@ -18,6 +18,7 @@
#include "device/LedDeviceTest.h" #include "device/LedDeviceTest.h"
#include "device/LedDeviceWs2801.h" #include "device/LedDeviceWs2801.h"
#include "device/LedDeviceAdalight.h" #include "device/LedDeviceAdalight.h"
#include "device/LedDeviceLightpack.h"
#include "LinearColorSmoothing.h" #include "LinearColorSmoothing.h"
@ -72,6 +73,13 @@ LedDevice* Hyperion::createDevice(const Json::Value& deviceConfig)
device = deviceAdalight; device = deviceAdalight;
} }
else if (type == "lightpack")
{
LedDeviceLightpack* deviceLightpack = new LedDeviceLightpack();
deviceLightpack->open();
device = deviceLightpack;
}
else if (type == "test") else if (type == "test")
{ {
const std::string output = deviceConfig["output"].asString(); const std::string output = deviceConfig["output"].asString();

View File

@ -0,0 +1,290 @@
// stl includes
#include <exception>
#include <cstring>
// Local Hyperion includes
#include "LedDeviceLightpack.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
};
LedDeviceLightpack::LedDeviceLightpack(const std::string &serialNumber) :
LedDevice(),
_libusbContext(nullptr),
_deviceHandle(nullptr),
_busNumber(-1),
_addressNumber(-1),
_serialNumber(serialNumber),
_firmwareVersion({-1,-1}),
_ledCount(-1),
_bitsPerChannel(-1),
_ledBuffer()
{
}
LedDeviceLightpack::~LedDeviceLightpack()
{
if (_deviceHandle != nullptr)
{
libusb_release_interface(_deviceHandle, LIGHTPACK_INTERFACE);
libusb_attach_kernel_driver(_deviceHandle, LIGHTPACK_INTERFACE);
libusb_close(_deviceHandle);
_deviceHandle = nullptr;
}
if (_libusbContext != nullptr)
{
libusb_exit(_libusbContext);
_libusbContext = nullptr;
}
}
int LedDeviceLightpack::open()
{
int error;
// initialize the usb context
if ((error = libusb_init(&_libusbContext)) != LIBUSB_SUCCESS)
{
std::cerr << "Error while initializing USB context(" << error << "): " << libusb_error_name(error) << std::endl;
_libusbContext = nullptr;
return -1;
}
std::cout << "USB context initialized" << std::endl;
// retrieve the list of usb devices
libusb_device ** deviceList;
ssize_t deviceCount = libusb_get_device_list(_libusbContext, &deviceList);
// iterate the list of devices
for (ssize_t i = 0 ; i < deviceCount; ++i)
{
libusb_device_descriptor deviceDescriptor;
error = libusb_get_device_descriptor(deviceList[i], &deviceDescriptor);
if (error != LIBUSB_SUCCESS)
{
std::cerr << "Error while retrieving device descriptor(" << error << "): " << libusb_error_name(error) << std::endl;
// continue with next usb device
continue;
}
if ((deviceDescriptor.idVendor == USB_VENDOR_ID && deviceDescriptor.idProduct == USB_PRODUCT_ID) ||
(deviceDescriptor.idVendor == USB_OLD_VENDOR_ID && deviceDescriptor.idProduct == USB_OLD_PRODUCT_ID))
{
// get the hardware address
int busNumber = libusb_get_bus_number(deviceList[i]);
int addressNumber = libusb_get_device_address(deviceList[i]);
// get the serial number
std::string serialNumber;
if (deviceDescriptor.iSerialNumber != 0)
{
try
{
serialNumber = LedDeviceLightpack::getString(deviceList[i], deviceDescriptor.iSerialNumber);
}
catch (int e)
{
std::cerr << "unable to retrieve serial number from Lightpack device(" << e << "): " << libusb_error_name(e) << std::endl;
}
}
// get the firmware version
Version version = {-1,-1};
try
{
version = LedDeviceLightpack::getVersion(deviceList[i]);
}
catch (int e)
{
std::cerr << "unable to retrieve firmware version number from Lightpack device(" << e << "): " << libusb_error_name(e) << std::endl;
}
std::cout << "Lightpack device found: bus=" << busNumber << " address=" << addressNumber << " serial=" << serialNumber << " version=" << version.majorVersion << "." << version.minorVersion << std::endl;
// check if this is the device we are looking for
if (_serialNumber.empty() || _serialNumber == serialNumber)
{
// This is it!
try
{
_deviceHandle = openDevice(deviceList[i]);
_serialNumber = serialNumber;
_busNumber = busNumber;
_addressNumber = addressNumber;
std::cout << "Lightpack device successfully opened" << std::endl;
// break from the search loop
break;
}
catch(int e)
{
std::cerr << "unable to retrieve open Lightpack device(" << e << "): " << libusb_error_name(e) << std::endl;
}
}
}
}
// free the device list
libusb_free_device_list(deviceList, 1);
if (_deviceHandle != nullptr)
{
// 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 (command + 6 bytes per led)
_ledBuffer.resize(1 + _ledCount * 6, 0);
_ledBuffer[0] = CMD_UPDATE_LEDS;
}
return _deviceHandle == nullptr ? -1 : 0;
}
int LedDeviceLightpack::write(const std::vector<ColorRgb> &ledValues)
{
int count = std::min(_ledCount, (int) ledValues.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
_ledBuffer[6*i] = color.red;
_ledBuffer[6*i+1] = color.green;
_ledBuffer[6*i+2] = color.blue;
// leave the next three bytes on zero...
// 12-bit values have zeros in the lowest 4 bits which is almost correct, but it saves extra
// switches to determine what to do and some bit shuffling
}
return writeBytes(_ledBuffer.data(), _ledBuffer.size());
}
int LedDeviceLightpack::switchOff()
{
unsigned char buf[1] = {CMD_OFF_ALL};
return writeBytes(buf, sizeof(buf)) == sizeof(buf);
}
int LedDeviceLightpack::writeBytes(uint8_t *data, int size)
{
return libusb_control_transfer(_deviceHandle,
LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE,
0x09,
(2 << 8),
0x00,
data, size, 100);
}
int LedDeviceLightpack::disableSmoothing()
{
unsigned char buf[2] = {CMD_SET_SMOOTH_SLOWDOWN, 0};
return writeBytes(buf, sizeof(buf)) == sizeof(buf);
}
libusb_device_handle * LedDeviceLightpack::openDevice(libusb_device *device)
{
libusb_device_handle * handle = nullptr;
int error = libusb_open(device, &handle);
if (error != LIBUSB_SUCCESS)
{
throw error;
}
error = libusb_detach_kernel_driver(handle, LIGHTPACK_INTERFACE);
if (error != LIBUSB_SUCCESS)
{
throw error;
}
error = libusb_claim_interface(handle, LIGHTPACK_INTERFACE);
if (error != LIBUSB_SUCCESS)
{
throw error;
}
return handle;
}
std::string LedDeviceLightpack::getString(libusb_device * device, int stringDescriptorIndex)
{
libusb_device_handle * deviceHandle = openDevice(device);
char buffer[256];
int error = libusb_get_string_descriptor_ascii(deviceHandle, stringDescriptorIndex, reinterpret_cast<unsigned char *>(buffer), sizeof(buffer));
if (error <= 0)
{
throw error;
}
libusb_close(deviceHandle);
return std::string(buffer, error);
}
LedDeviceLightpack::Version LedDeviceLightpack::getVersion(libusb_device *device)
{
libusb_device_handle * deviceHandle = openDevice(device);
uint8_t buffer[256];
int error = libusb_get_descriptor(deviceHandle, LIBUSB_DT_REPORT, 0, buffer, sizeof(buffer));
if (error <= 3)
{
throw error;
}
libusb_close(deviceHandle);
return Version{buffer[INDEX_FW_VER_MAJOR], buffer[INDEX_FW_VER_MINOR]};
}

View File

@ -0,0 +1,85 @@
#pragma once
// stl includes
#include <vector>
#include <cstdint>
#include <string>
// libusb include
#include <libusb.h>
// Hyperion includes
#include <hyperion/LedDevice.h>
///
/// LedDevice implementation for a lightpack device (http://code.google.com/p/light-pack/)
///
class LedDeviceLightpack : public LedDevice
{
public:
///
/// Constructs the LedDeviceLightpack
///
LedDeviceLightpack(const std::string & serialNumber = "");
///
/// Destructor of the LedDevice; closes the output device if it is open
///
virtual ~LedDeviceLightpack();
///
/// Opens and configures the output device7
///
/// @return Zero on succes else negative
///
int open();
int write(const std::vector<ColorRgb>& ledValues);
int switchOff();
private:
struct Version
{
int majorVersion;
int minorVersion;
};
/// write bytes to the device
int writeBytes(uint8_t *data, int size);
/// Disable the internal smoothing on the Lightpack device
int disableSmoothing();
static libusb_device_handle * openDevice(libusb_device * device);
static std::string getString(libusb_device * device, int stringDescriptorIndex);
static Version getVersion(libusb_device * device);
private:
/// libusb context
libusb_context * _libusbContext;
/// libusb device handle
libusb_device_handle * _deviceHandle;
/// harware bus number
int _busNumber;
/// hardware address number
int _addressNumber;
/// 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

@ -10,7 +10,8 @@
#include <sys/mman.h> #include <sys/mman.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <utils/RgbImage.h> #include <utils/Image.h>
#include <utils/ColorRgb.h>
/// ///
/// FbWriter allows direct access tot the FrameBuffer. It writes and image to the framebuffer, /// FbWriter allows direct access tot the FrameBuffer. It writes and image to the framebuffer,
@ -83,7 +84,7 @@ public:
/// ///
/// @param image The RGB Image /// @param image The RGB Image
/// ///
void writeImage(const RgbImage& image) void writeImage(const Image<ColorRgb>& image)
{ {
std::cout << "Writing image [" << image.width() << "x" << image.height() << "]" << std::endl; std::cout << "Writing image [" << image.width() << "x" << image.height() << "]" << std::endl;

View File

@ -6,7 +6,8 @@
#include <png.h> #include <png.h>
// Utils includes // Utils includes
#include <utils/RgbImage.h> #include <utils/Image.h>
#include <utils/ColorRgb.h>
#include <utils/jsonschema/JsonFactory.h> #include <utils/jsonschema/JsonFactory.h>
// Raspilight includes // Raspilight includes
@ -15,7 +16,7 @@
// Local includes // Local includes
#include "FbWriter.h" #include "FbWriter.h"
bool read_png(std::string file_name, RgbImage*& rgbImage) bool read_png(std::string file_name, Image<ColorRgb>*& rgbImage)
{ {
png_structp png_ptr; png_structp png_ptr;
png_infop info_ptr; png_infop info_ptr;
@ -70,16 +71,16 @@ bool read_png(std::string file_name, RgbImage*& rgbImage)
png_bytepp row_pointers; png_bytepp row_pointers;
row_pointers = png_get_rows(png_ptr, info_ptr); row_pointers = png_get_rows(png_ptr, info_ptr);
rgbImage = new RgbImage(width, height); rgbImage = new Image<ColorRgb>(width, height);
for (unsigned iRow=0; iRow<height; ++iRow) for (unsigned iRow=0; iRow<height; ++iRow)
{ {
if (color_type == PNG_COLOR_TYPE_RGB) if (color_type == PNG_COLOR_TYPE_RGB)
{ {
RgbColor* rowPtr = reinterpret_cast<RgbColor*>(row_pointers[iRow]); ColorRgb* rowPtr = reinterpret_cast<ColorRgb*>(row_pointers[iRow]);
for (unsigned iCol=0; iCol<width; ++iCol) for (unsigned iCol=0; iCol<width; ++iCol)
{ {
rgbImage->setPixel(iCol, iRow, rowPtr[iCol]); (*rgbImage)(iCol, iRow) = rowPtr[iCol];
} }
} }
else if (color_type == PNG_COLOR_TYPE_RGBA) else if (color_type == PNG_COLOR_TYPE_RGBA)
@ -88,7 +89,7 @@ bool read_png(std::string file_name, RgbImage*& rgbImage)
for (unsigned iCol=0; iCol<width; ++iCol) for (unsigned iCol=0; iCol<width; ++iCol)
{ {
const unsigned argbValue = rowPtr[iCol]; const unsigned argbValue = rowPtr[iCol];
rgbImage->setPixel(iCol, iRow, {uint8_t((argbValue >> 16) & 0xFF), uint8_t((argbValue >> 8) & 0xFF), uint8_t((argbValue) & 0xFF)}); (*rgbImage)(iCol, iRow) = ColorRgb{uint8_t((argbValue >> 16) & 0xFF), uint8_t((argbValue >> 8) & 0xFF), uint8_t((argbValue) & 0xFF)};
} }
} }
else else
@ -115,7 +116,7 @@ int main(int argc, char** argv)
const std::string pngFilename = argv[1]; const std::string pngFilename = argv[1];
RgbImage* image = nullptr; Image<ColorRgb>* image = nullptr;
if (!read_png(pngFilename, image) || image == nullptr) if (!read_png(pngFilename, image) || image == nullptr)
{ {
std::cout << "Failed to load image" << std::endl; std::cout << "Failed to load image" << std::endl;