diff --git a/CMakeLists.txt b/CMakeLists.txt index 1ef229dc..db0b2303 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,6 +37,9 @@ find_package(Qt4 COMPONENTS QtCore QtGui QtNetwork REQUIRED QUIET) # add protocol buffers find_package(Protobuf REQUIRED) +#add libusb +find_package(libusb-1.0 REQUIRED) + #SET(QT_DONT_USE_QTGUI TRUE) #SET(QT_USE_QTCONSOLE TRUE) include(${QT_USE_FILE}) diff --git a/CrossCompileHowto.txt b/CrossCompileHowto.txt index f0e38499..5d5150f5 100644 --- a/CrossCompileHowto.txt +++ b/CrossCompileHowto.txt @@ -1,6 +1,6 @@ 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 --------- diff --git a/cmake/Findlibusb-1.0.cmake b/cmake/Findlibusb-1.0.cmake new file mode 100644 index 00000000..26a82c4f --- /dev/null +++ b/cmake/Findlibusb-1.0.cmake @@ -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 +# +# (Changes for libusb) Copyright (c) 2008 Kyle Machulis +# +# 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) diff --git a/libsrc/hyperion/CMakeLists.txt b/libsrc/hyperion/CMakeLists.txt index f2c6a195..891d3158 100644 --- a/libsrc/hyperion/CMakeLists.txt +++ b/libsrc/hyperion/CMakeLists.txt @@ -3,6 +3,8 @@ SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/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 SET(Hyperion_QT_HEADERS ${CURRENT_HEADER_DIR}/Hyperion.h @@ -28,6 +30,7 @@ SET(Hyperion_HEADERS ${CURRENT_SOURCE_DIR}/device/LedDeviceWs2801.h ${CURRENT_SOURCE_DIR}/device/LedDeviceLpd6803.h ${CURRENT_SOURCE_DIR}/device/LedDeviceAdalight.h + ${CURRENT_SOURCE_DIR}/device/LedDeviceLightpack.h ) SET(Hyperion_SOURCES @@ -49,6 +52,7 @@ SET(Hyperion_SOURCES ${CURRENT_SOURCE_DIR}/device/LedDeviceWs2801.cpp ${CURRENT_SOURCE_DIR}/device/LedDeviceLpd6803.cpp ${CURRENT_SOURCE_DIR}/device/LedDeviceAdalight.cpp + ${CURRENT_SOURCE_DIR}/device/LedDeviceLightpack.cpp ) set(Hyperion_RESOURCES @@ -70,4 +74,5 @@ add_library(hyperion target_link_libraries(hyperion hyperion-utils serialport - ${QT_LIBRARIES}) + ${QT_LIBRARIES} + ${LIBUSB_1_LIBRARIES}) diff --git a/libsrc/hyperion/Hyperion.cpp b/libsrc/hyperion/Hyperion.cpp index 88590e65..14bf6231 100644 --- a/libsrc/hyperion/Hyperion.cpp +++ b/libsrc/hyperion/Hyperion.cpp @@ -18,6 +18,7 @@ #include "device/LedDeviceTest.h" #include "device/LedDeviceWs2801.h" #include "device/LedDeviceAdalight.h" +#include "device/LedDeviceLightpack.h" #include "LinearColorSmoothing.h" @@ -72,6 +73,13 @@ LedDevice* Hyperion::createDevice(const Json::Value& deviceConfig) device = deviceAdalight; } + else if (type == "lightpack") + { + LedDeviceLightpack* deviceLightpack = new LedDeviceLightpack(); + deviceLightpack->open(); + + device = deviceLightpack; + } else if (type == "test") { const std::string output = deviceConfig["output"].asString(); diff --git a/libsrc/hyperion/device/LedDeviceLightpack.cpp b/libsrc/hyperion/device/LedDeviceLightpack.cpp new file mode 100644 index 00000000..f09d5cf0 --- /dev/null +++ b/libsrc/hyperion/device/LedDeviceLightpack.cpp @@ -0,0 +1,290 @@ +// stl includes +#include +#include + +// 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 &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(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]}; +} diff --git a/libsrc/hyperion/device/LedDeviceLightpack.h b/libsrc/hyperion/device/LedDeviceLightpack.h new file mode 100644 index 00000000..91a797f0 --- /dev/null +++ b/libsrc/hyperion/device/LedDeviceLightpack.h @@ -0,0 +1,85 @@ +#pragma once + +// stl includes +#include +#include +#include + +// libusb include +#include + +// Hyperion includes +#include + +/// +/// 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& 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 _ledBuffer; +}; diff --git a/src/viewpng/FbWriter.h b/src/viewpng/FbWriter.h index f6edc6b1..bfb54ae3 100644 --- a/src/viewpng/FbWriter.h +++ b/src/viewpng/FbWriter.h @@ -10,7 +10,8 @@ #include #include -#include +#include +#include /// /// FbWriter allows direct access tot the FrameBuffer. It writes and image to the framebuffer, @@ -83,7 +84,7 @@ public: /// /// @param image The RGB Image /// - void writeImage(const RgbImage& image) + void writeImage(const Image& image) { std::cout << "Writing image [" << image.width() << "x" << image.height() << "]" << std::endl; diff --git a/src/viewpng/ViewPng.cpp b/src/viewpng/ViewPng.cpp index 04e0ccd1..65dbd0cc 100644 --- a/src/viewpng/ViewPng.cpp +++ b/src/viewpng/ViewPng.cpp @@ -6,7 +6,8 @@ #include // Utils includes -#include +#include +#include #include // Raspilight includes @@ -15,7 +16,7 @@ // Local includes #include "FbWriter.h" -bool read_png(std::string file_name, RgbImage*& rgbImage) +bool read_png(std::string file_name, Image*& rgbImage) { png_structp png_ptr; png_infop info_ptr; @@ -70,16 +71,16 @@ bool read_png(std::string file_name, RgbImage*& rgbImage) png_bytepp row_pointers; row_pointers = png_get_rows(png_ptr, info_ptr); - rgbImage = new RgbImage(width, height); + rgbImage = new Image(width, height); for (unsigned iRow=0; iRow(row_pointers[iRow]); + ColorRgb* rowPtr = reinterpret_cast(row_pointers[iRow]); for (unsigned iCol=0; iColsetPixel(iCol, iRow, rowPtr[iCol]); + (*rgbImage)(iCol, iRow) = rowPtr[iCol]; } } 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; iColsetPixel(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 @@ -115,7 +116,7 @@ int main(int argc, char** argv) const std::string pngFilename = argv[1]; - RgbImage* image = nullptr; + Image* image = nullptr; if (!read_png(pngFilename, image) || image == nullptr) { std::cout << "Failed to load image" << std::endl;