Merge remote-tracking branch 'origin/master'

Former-commit-id: b4eb2e7a1c78d107cb7bc69a04b1cff694f6e308
This commit is contained in:
T. van der Zwan 2014-08-19 10:54:39 +02:00
commit f12bffb44f
94 changed files with 5826 additions and 2027 deletions

2
.gitignore vendored
View File

@ -1,3 +1,5 @@
/*.user
/build
/build-x86
.DS_Store

View File

@ -7,19 +7,22 @@ cmake_minimum_required(VERSION 2.8)
#set(CMAKE_TOOLCHAIN_FILE /opt/raspberrypi/Toolchain-RaspberryPi.cmake)
# set the build options
option (ENABLE_DISPMANX "Enable the RPi dispmanx grabber" ON)
option (ENABLE_SPIDEV "Enable the SPIDEV device" ON)
option(ENABLE_DISPMANX "Enable the RPi dispmanx grabber" ON)
message(STATUS "ENABLE_DISPMANX = " ${ENABLE_DISPMANX})
message(STATUS "ENABLE_SPIDEV = " ${ENABLE_SPIDEV})
option (ENABLE_V4L2 "Enable the V4L2 grabber" ON)
option(ENABLE_SPIDEV "Enable the SPIDEV device" ON)
message(STATUS "ENABLE_SPIDEV = " ${ENABLE_SPIDEV})
option(ENABLE_V4L2 "Enable the V4L2 grabber" ON)
message(STATUS "ENABLE_V4L2 = " ${ENABLE_V4L2})
option(ENABLE_TINKERFORGE "Enable the TINKERFORGE device" ON)
message(STATUS "ENABLE_TINKERFORGE = " ${ENABLE_TINKERFORGE})
# Createt the configuration file
# configure a header file to pass some of the CMake settings
# to the source code
configure_file ("${PROJECT_SOURCE_DIR}/HyperionConfig.h.in" "${PROJECT_BINARY_DIR}/HyperionConfig.h")
configure_file("${PROJECT_SOURCE_DIR}/HyperionConfig.h.in" "${PROJECT_BINARY_DIR}/HyperionConfig.h")
include_directories("${PROJECT_BINARY_DIR}")
# Add project specific cmake modules (find, etc)
@ -62,6 +65,10 @@ set(CMAKE_FIND_LIBRARY_SUFFIXES_OLD)
find_package(libusb-1.0 REQUIRED)
find_package(Threads REQUIRED)
if (ENABLE_TINKERFORGE)
#find_package(libtinkerforge-1.0 REQUIRED)
endif (ENABLE_TINKERFORGE)
include(${QT_USE_FILE})
add_definitions(${QT_DEFINITIONS})
# TODO[TvdZ]: This linking directory should only be added if we are cross compiling

View File

@ -8,3 +8,6 @@
// Define to enable the spi-device
#cmakedefine ENABLE_SPIDEV
// Define to enable the spi-device
#cmakedefine ENABLE_TINKERFORGE

View File

@ -7,22 +7,27 @@ DAEMON=hyperiond
DAEMONOPTS="/etc/hyperion.config.json"
DAEMON_PATH="/usr/bin"
NAME=$DEAMON
NAME=$DAEMON
DESC="Hyperion ambilight server"
PIDFILE=/var/run/$NAME.pid
SCRIPTNAME=/etc/init.d/$NAME
case "$1" in
start)
printf "%-50s" "Starting $NAME..."
cd $DAEMON_PATH
PID=`$DAEMON $DAEMONOPTS > /dev/null 2>&1 & echo $!`
#echo "Saving PID" $PID " to " $PIDFILE
if [ -z $PID ]; then
printf "%s\n" "Fail"
if [ $(pgrep -l $NAME |wc -l) = 1 ]
then
printf "%-50s\n" "Already running..."
exit 1
else
echo $PID > $PIDFILE
printf "%s\n" "Ok"
printf "%-50s" "Starting $NAME..."
cd $DAEMON_PATH
PID=`$DAEMON $DAEMONOPTS > /dev/null 2>&1 & echo $!`
#echo "Saving PID" $PID " to " $PIDFILE
if [ -z $PID ]; then
printf "%s\n" "Fail"
else
echo $PID > $PIDFILE
printf "%s\n" "Ok"
fi
fi
;;
status)
@ -39,24 +44,31 @@ status)
fi
;;
stop)
printf "%-50s" "Stopping $NAME"
PID=`cat $PIDFILE`
cd $DAEMON_PATH
if [ -f $PIDFILE ]; then
kill -HUP $PID
printf "%s\n" "Ok"
rm -f $PIDFILE
if [ -f $PIDFILE ]
then
printf "%-50s" "Stopping $NAME"
PID=`cat $PIDFILE`
cd $DAEMON_PATH
if [ -f $PIDFILE ]; then
kill -HUP $PID
printf "%s\n" "Ok"
rm -f $PIDFILE
else
printf "%s\n" "pidfile not found"
fi
else
printf "%s\n" "pidfile not found"
printf "%-50s\n" "No PID file $NAME not running?"
fi
;;
restart)
$0 stop
$0 start
$0 stop
$0 start
;;
*)
echo "Usage: $0 {status|start|stop|restart}"
exit 1
esac
exit 0

View File

@ -34,13 +34,13 @@ fi
echo 'Downloading hyperion'
if [ $IS_OPENELEC -eq 1 ]; then
# OpenELEC has a readonly file system. Use alternative location
curl --get https://raw.github.com/tvdzwan/hyperion/master/deploy/hyperion.tar.gz | tar -C /storage -xz
curl --get https://raw.github.com/tvdzwan/hyperion/master/deploy/hyperion.deps.openelec-rpi.tar.gz | tar -C /storage/hyperion/bin -xz
curl -L --get https://raw.githubusercontent.com/tvdzwan/hyperion/master/deploy/hyperion.tar.gz | tar -C /storage -xz
curl -L --get https://raw.githubusercontent.com/tvdzwan/hyperion/master/deploy/hyperion.deps.openelec-rpi.tar.gz | tar -C /storage/hyperion/bin -xz
# modify the default config to have a correct effect path
sed -i 's:/opt:/storage:g' /storage/hyperion/config/hyperion.config.json
else
wget https://raw.github.com/tvdzwan/hyperion/master/deploy/hyperion.tar.gz -O - | tar -C /opt -xz
wget https://raw.githubusercontent.com/tvdzwan/hyperion/master/deploy/hyperion.tar.gz -O - | tar -C /opt -xz
fi
# create links to the binaries
@ -68,9 +68,9 @@ fi
if [ $USE_INITCTL -eq 1 ]; then
echo 'Installing initctl script'
if [ $IS_RASPBMC -eq 1 ]; then
wget -N https://raw.github.com/tvdzwan/hyperion/master/deploy/hyperion.conf -P /etc/init/
wget -N https://raw.githubusercontent.com/tvdzwan/hyperion/master/deploy/hyperion.conf -P /etc/init/
else
wget -N https://raw.github.com/tvdzwan/hyperion/master/deploy/hyperion.xbian.conf -O /etc/init/hyperion.conf
wget -N https://raw.githubusercontent.com/tvdzwan/hyperion/master/deploy/hyperion.xbian.conf -O /etc/init/hyperion.conf
fi
elif [ $USE_SERVICE -eq 1 ]; then
echo 'Installing startup script in init.d'

View File

@ -14,7 +14,7 @@
{
"name" : "MyPi",
"type" : "adalight",
"output" : "/dev/ttyUSB0",
"output" : "/dev/ttyUSB0",
"rate" : 115200,
"colorOrder" : "rgb"
},
@ -363,7 +363,7 @@
{
"paths" :
[
"/opt/hyperion/effects"
"/home/dincs/projects/hyperion/effects"
]
},

View File

@ -1,5 +1,6 @@
add_subdirectory(jsoncpp)
add_subdirectory(getoptPlusPlus)
add_subdirectory(serial)
add_subdirectory(hidapi)
add_subdirectory(jsoncpp)
add_subdirectory(serial)
add_subdirectory(tinkerforge)

View File

@ -280,7 +280,6 @@ void PresettableUniquelySwitchable::preset() {
template<>
PODParameter<string>::PODParameter(char shortOption, const char *longOption,
const char* description) : CommonParameter<PresettableUniquelySwitchable>(shortOption, longOption, description) {
preset();
}

View File

@ -0,0 +1,14 @@
project(tinkerforge)
# define the current source/header path
set(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/dependencies/include/tinkerforge)
set(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/dependencies/build/tinkerforge)
include_directories(${CURRENT_HEADER_DIR})
add_library(tinkerforge
${CURRENT_HEADER_DIR}/bricklet_led_strip.h
${CURRENT_HEADER_DIR}/ip_connection.h
${CURRENT_SOURCE_DIR}/bricklet_led_strip.c
${CURRENT_SOURCE_DIR}/ip_connection.c)

View File

@ -0,0 +1,373 @@
/* ***********************************************************
* This file was automatically generated on 2013-12-19. *
* *
* Bindings Version 2.0.13 *
* *
* If you have a bugfix for this file and want to commit it, *
* please fix the bug in the generator. You can find a link *
* to the generator git on tinkerforge.com *
*************************************************************/
#define IPCON_EXPOSE_INTERNALS
#include "bricklet_led_strip.h"
#include <string.h>
typedef void (*FrameRenderedCallbackFunction)(uint16_t, void *);
#if defined _MSC_VER || defined __BORLANDC__
#pragma pack(push)
#pragma pack(1)
#define ATTRIBUTE_PACKED
#elif defined __GNUC__
#ifdef _WIN32
// workaround struct packing bug in GCC 4.7 on Windows
// http://gcc.gnu.org/bugzilla/show_bug.cgi?id=52991
#define ATTRIBUTE_PACKED __attribute__((gcc_struct, packed))
#else
#define ATTRIBUTE_PACKED __attribute__((packed))
#endif
#else
#error unknown compiler, do not know how to enable struct packing
#endif
typedef struct {
PacketHeader header;
uint16_t index;
uint8_t length;
uint8_t r[16];
uint8_t g[16];
uint8_t b[16];
} ATTRIBUTE_PACKED SetRGBValues_;
typedef struct {
PacketHeader header;
uint16_t index;
uint8_t length;
} ATTRIBUTE_PACKED GetRGBValues_;
typedef struct {
PacketHeader header;
uint8_t r[16];
uint8_t g[16];
uint8_t b[16];
} ATTRIBUTE_PACKED GetRGBValuesResponse_;
typedef struct {
PacketHeader header;
uint16_t duration;
} ATTRIBUTE_PACKED SetFrameDuration_;
typedef struct {
PacketHeader header;
} ATTRIBUTE_PACKED GetFrameDuration_;
typedef struct {
PacketHeader header;
uint16_t duration;
} ATTRIBUTE_PACKED GetFrameDurationResponse_;
typedef struct {
PacketHeader header;
} ATTRIBUTE_PACKED GetSupplyVoltage_;
typedef struct {
PacketHeader header;
uint16_t voltage;
} ATTRIBUTE_PACKED GetSupplyVoltageResponse_;
typedef struct {
PacketHeader header;
uint16_t length;
} ATTRIBUTE_PACKED FrameRenderedCallback_;
typedef struct {
PacketHeader header;
uint32_t frequency;
} ATTRIBUTE_PACKED SetClockFrequency_;
typedef struct {
PacketHeader header;
} ATTRIBUTE_PACKED GetClockFrequency_;
typedef struct {
PacketHeader header;
uint32_t frequency;
} ATTRIBUTE_PACKED GetClockFrequencyResponse_;
typedef struct {
PacketHeader header;
} ATTRIBUTE_PACKED GetIdentity_;
typedef struct {
PacketHeader header;
char uid[8];
char connected_uid[8];
char position;
uint8_t hardware_version[3];
uint8_t firmware_version[3];
uint16_t device_identifier;
} ATTRIBUTE_PACKED GetIdentityResponse_;
#if defined _MSC_VER || defined __BORLANDC__
#pragma pack(pop)
#endif
#undef ATTRIBUTE_PACKED
static void led_strip_callback_wrapper_frame_rendered(DevicePrivate *device_p, Packet *packet) {
FrameRenderedCallbackFunction callback_function;
void *user_data = device_p->registered_callback_user_data[LED_STRIP_CALLBACK_FRAME_RENDERED];
FrameRenderedCallback_ *callback = (FrameRenderedCallback_ *)packet;
*(void **)(&callback_function) = device_p->registered_callbacks[LED_STRIP_CALLBACK_FRAME_RENDERED];
if (callback_function == NULL) {
return;
}
callback->length = leconvert_uint16_from(callback->length);
callback_function(callback->length, user_data);
}
void led_strip_create(LEDStrip *led_strip, const char *uid, IPConnection *ipcon) {
DevicePrivate *device_p;
device_create(led_strip, uid, ipcon->p, 2, 0, 1);
device_p = led_strip->p;
device_p->response_expected[LED_STRIP_FUNCTION_SET_RGB_VALUES] = DEVICE_RESPONSE_EXPECTED_FALSE;
device_p->response_expected[LED_STRIP_FUNCTION_GET_RGB_VALUES] = DEVICE_RESPONSE_EXPECTED_ALWAYS_TRUE;
device_p->response_expected[LED_STRIP_FUNCTION_SET_FRAME_DURATION] = DEVICE_RESPONSE_EXPECTED_FALSE;
device_p->response_expected[LED_STRIP_FUNCTION_GET_FRAME_DURATION] = DEVICE_RESPONSE_EXPECTED_ALWAYS_TRUE;
device_p->response_expected[LED_STRIP_FUNCTION_GET_SUPPLY_VOLTAGE] = DEVICE_RESPONSE_EXPECTED_ALWAYS_TRUE;
device_p->response_expected[LED_STRIP_CALLBACK_FRAME_RENDERED] = DEVICE_RESPONSE_EXPECTED_ALWAYS_FALSE;
device_p->response_expected[LED_STRIP_FUNCTION_SET_CLOCK_FREQUENCY] = DEVICE_RESPONSE_EXPECTED_FALSE;
device_p->response_expected[LED_STRIP_FUNCTION_GET_CLOCK_FREQUENCY] = DEVICE_RESPONSE_EXPECTED_ALWAYS_TRUE;
device_p->response_expected[LED_STRIP_FUNCTION_GET_IDENTITY] = DEVICE_RESPONSE_EXPECTED_ALWAYS_TRUE;
device_p->callback_wrappers[LED_STRIP_CALLBACK_FRAME_RENDERED] = led_strip_callback_wrapper_frame_rendered;
}
void led_strip_destroy(LEDStrip *led_strip) {
device_destroy(led_strip);
}
int led_strip_get_response_expected(LEDStrip *led_strip, uint8_t function_id, bool *ret_response_expected) {
return device_get_response_expected(led_strip->p, function_id, ret_response_expected);
}
int led_strip_set_response_expected(LEDStrip *led_strip, uint8_t function_id, bool response_expected) {
return device_set_response_expected(led_strip->p, function_id, response_expected);
}
int led_strip_set_response_expected_all(LEDStrip *led_strip, bool response_expected) {
return device_set_response_expected_all(led_strip->p, response_expected);
}
void led_strip_register_callback(LEDStrip *led_strip, uint8_t id, void *callback, void *user_data) {
device_register_callback(led_strip->p, id, callback, user_data);
}
int led_strip_get_api_version(LEDStrip *led_strip, uint8_t ret_api_version[3]) {
return device_get_api_version(led_strip->p, ret_api_version);
}
int led_strip_set_rgb_values(LEDStrip *led_strip, uint16_t index, uint8_t length, uint8_t r[16], uint8_t g[16], uint8_t b[16]) {
DevicePrivate *device_p = led_strip->p;
SetRGBValues_ request;
int ret;
ret = packet_header_create(&request.header, sizeof(request), LED_STRIP_FUNCTION_SET_RGB_VALUES, device_p->ipcon_p, device_p);
if (ret < 0) {
return ret;
}
request.index = leconvert_uint16_to(index);
request.length = length;
memcpy(request.r, r, 16 * sizeof(uint8_t));
memcpy(request.g, g, 16 * sizeof(uint8_t));
memcpy(request.b, b, 16 * sizeof(uint8_t));
ret = device_send_request(device_p, (Packet *)&request, NULL);
return ret;
}
int led_strip_get_rgb_values(LEDStrip *led_strip, uint16_t index, uint8_t length, uint8_t ret_r[16], uint8_t ret_g[16], uint8_t ret_b[16]) {
DevicePrivate *device_p = led_strip->p;
GetRGBValues_ request;
GetRGBValuesResponse_ response;
int ret;
ret = packet_header_create(&request.header, sizeof(request), LED_STRIP_FUNCTION_GET_RGB_VALUES, device_p->ipcon_p, device_p);
if (ret < 0) {
return ret;
}
request.index = leconvert_uint16_to(index);
request.length = length;
ret = device_send_request(device_p, (Packet *)&request, (Packet *)&response);
if (ret < 0) {
return ret;
}
memcpy(ret_r, response.r, 16 * sizeof(uint8_t));
memcpy(ret_g, response.g, 16 * sizeof(uint8_t));
memcpy(ret_b, response.b, 16 * sizeof(uint8_t));
return ret;
}
int led_strip_set_frame_duration(LEDStrip *led_strip, uint16_t duration) {
DevicePrivate *device_p = led_strip->p;
SetFrameDuration_ request;
int ret;
ret = packet_header_create(&request.header, sizeof(request), LED_STRIP_FUNCTION_SET_FRAME_DURATION, device_p->ipcon_p, device_p);
if (ret < 0) {
return ret;
}
request.duration = leconvert_uint16_to(duration);
ret = device_send_request(device_p, (Packet *)&request, NULL);
return ret;
}
int led_strip_get_frame_duration(LEDStrip *led_strip, uint16_t *ret_duration) {
DevicePrivate *device_p = led_strip->p;
GetFrameDuration_ request;
GetFrameDurationResponse_ response;
int ret;
ret = packet_header_create(&request.header, sizeof(request), LED_STRIP_FUNCTION_GET_FRAME_DURATION, device_p->ipcon_p, device_p);
if (ret < 0) {
return ret;
}
ret = device_send_request(device_p, (Packet *)&request, (Packet *)&response);
if (ret < 0) {
return ret;
}
*ret_duration = leconvert_uint16_from(response.duration);
return ret;
}
int led_strip_get_supply_voltage(LEDStrip *led_strip, uint16_t *ret_voltage) {
DevicePrivate *device_p = led_strip->p;
GetSupplyVoltage_ request;
GetSupplyVoltageResponse_ response;
int ret;
ret = packet_header_create(&request.header, sizeof(request), LED_STRIP_FUNCTION_GET_SUPPLY_VOLTAGE, device_p->ipcon_p, device_p);
if (ret < 0) {
return ret;
}
ret = device_send_request(device_p, (Packet *)&request, (Packet *)&response);
if (ret < 0) {
return ret;
}
*ret_voltage = leconvert_uint16_from(response.voltage);
return ret;
}
int led_strip_set_clock_frequency(LEDStrip *led_strip, uint32_t frequency) {
DevicePrivate *device_p = led_strip->p;
SetClockFrequency_ request;
int ret;
ret = packet_header_create(&request.header, sizeof(request), LED_STRIP_FUNCTION_SET_CLOCK_FREQUENCY, device_p->ipcon_p, device_p);
if (ret < 0) {
return ret;
}
request.frequency = leconvert_uint32_to(frequency);
ret = device_send_request(device_p, (Packet *)&request, NULL);
return ret;
}
int led_strip_get_clock_frequency(LEDStrip *led_strip, uint32_t *ret_frequency) {
DevicePrivate *device_p = led_strip->p;
GetClockFrequency_ request;
GetClockFrequencyResponse_ response;
int ret;
ret = packet_header_create(&request.header, sizeof(request), LED_STRIP_FUNCTION_GET_CLOCK_FREQUENCY, device_p->ipcon_p, device_p);
if (ret < 0) {
return ret;
}
ret = device_send_request(device_p, (Packet *)&request, (Packet *)&response);
if (ret < 0) {
return ret;
}
*ret_frequency = leconvert_uint32_from(response.frequency);
return ret;
}
int led_strip_get_identity(LEDStrip *led_strip, char ret_uid[8], char ret_connected_uid[8], char *ret_position, uint8_t ret_hardware_version[3], uint8_t ret_firmware_version[3], uint16_t *ret_device_identifier) {
DevicePrivate *device_p = led_strip->p;
GetIdentity_ request;
GetIdentityResponse_ response;
int ret;
ret = packet_header_create(&request.header, sizeof(request), LED_STRIP_FUNCTION_GET_IDENTITY, device_p->ipcon_p, device_p);
if (ret < 0) {
return ret;
}
ret = device_send_request(device_p, (Packet *)&request, (Packet *)&response);
if (ret < 0) {
return ret;
}
strncpy(ret_uid, response.uid, 8);
strncpy(ret_connected_uid, response.connected_uid, 8);
*ret_position = response.position;
memcpy(ret_hardware_version, response.hardware_version, 3 * sizeof(uint8_t));
memcpy(ret_firmware_version, response.firmware_version, 3 * sizeof(uint8_t));
*ret_device_identifier = leconvert_uint16_from(response.device_identifier);
return ret;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,301 @@
/* ***********************************************************
* This file was automatically generated on 2013-12-19. *
* *
* Bindings Version 2.0.13 *
* *
* If you have a bugfix for this file and want to commit it, *
* please fix the bug in the generator. You can find a link *
* to the generator git on tinkerforge.com *
*************************************************************/
#ifndef BRICKLET_LED_STRIP_H
#define BRICKLET_LED_STRIP_H
#include "ip_connection.h"
/**
* \defgroup BrickletLEDStrip LEDStrip Bricklet
*/
/**
* \ingroup BrickletLEDStrip
*
* Device to control up to 320 RGB LEDs
*/
typedef Device LEDStrip;
/**
* \ingroup BrickletLEDStrip
*/
#define LED_STRIP_FUNCTION_SET_RGB_VALUES 1
/**
* \ingroup BrickletLEDStrip
*/
#define LED_STRIP_FUNCTION_GET_RGB_VALUES 2
/**
* \ingroup BrickletLEDStrip
*/
#define LED_STRIP_FUNCTION_SET_FRAME_DURATION 3
/**
* \ingroup BrickletLEDStrip
*/
#define LED_STRIP_FUNCTION_GET_FRAME_DURATION 4
/**
* \ingroup BrickletLEDStrip
*/
#define LED_STRIP_FUNCTION_GET_SUPPLY_VOLTAGE 5
/**
* \ingroup BrickletLEDStrip
*/
#define LED_STRIP_FUNCTION_SET_CLOCK_FREQUENCY 7
/**
* \ingroup BrickletLEDStrip
*/
#define LED_STRIP_FUNCTION_GET_CLOCK_FREQUENCY 8
/**
* \ingroup BrickletLEDStrip
*/
#define LED_STRIP_FUNCTION_GET_IDENTITY 255
/**
* \ingroup BrickletLEDStrip
*
* Signature: \code void callback(uint16_t length, void *user_data) \endcode
*
* This callback is triggered directly after a new frame is rendered.
*
* You should send the data for the next frame directly after this callback
* was triggered.
*
* For an explanation of the general approach see {@link led_strip_set_rgb_values}.
*/
#define LED_STRIP_CALLBACK_FRAME_RENDERED 6
/**
* \ingroup BrickletLEDStrip
*
* This constant is used to identify a LEDStrip Bricklet.
*
* The {@link led_strip_get_identity} function and the
* {@link IPCON_CALLBACK_ENUMERATE} callback of the IP Connection have a
* \c device_identifier parameter to specify the Brick's or Bricklet's type.
*/
#define LED_STRIP_DEVICE_IDENTIFIER 231
/**
* \ingroup BrickletLEDStrip
*
* Creates the device object \c led_strip with the unique device ID \c uid and adds
* it to the IPConnection \c ipcon.
*/
void led_strip_create(LEDStrip *led_strip, const char *uid, IPConnection *ipcon);
/**
* \ingroup BrickletLEDStrip
*
* Removes the device object \c led_strip from its IPConnection and destroys it.
* The device object cannot be used anymore afterwards.
*/
void led_strip_destroy(LEDStrip *led_strip);
/**
* \ingroup BrickletLEDStrip
*
* Returns the response expected flag for the function specified by the
* \c function_id parameter. It is *true* if the function is expected to
* send a response, *false* otherwise.
*
* For getter functions this is enabled by default and cannot be disabled,
* because those functions will always send a response. For callback
* configuration functions it is enabled by default too, but can be disabled
* via the led_strip_set_response_expected function. For setter functions it is
* disabled by default and can be enabled.
*
* Enabling the response expected flag for a setter function allows to
* detect timeouts and other error conditions calls of this setter as well.
* The device will then send a response for this purpose. If this flag is
* disabled for a setter function then no response is send and errors are
* silently ignored, because they cannot be detected.
*/
int led_strip_get_response_expected(LEDStrip *led_strip, uint8_t function_id, bool *ret_response_expected);
/**
* \ingroup BrickletLEDStrip
*
* Changes the response expected flag of the function specified by the
* \c function_id parameter. This flag can only be changed for setter
* (default value: *false*) and callback configuration functions
* (default value: *true*). For getter functions it is always enabled and
* callbacks it is always disabled.
*
* Enabling the response expected flag for a setter function allows to detect
* timeouts and other error conditions calls of this setter as well. The device
* will then send a response for this purpose. If this flag is disabled for a
* setter function then no response is send and errors are silently ignored,
* because they cannot be detected.
*/
int led_strip_set_response_expected(LEDStrip *led_strip, uint8_t function_id, bool response_expected);
/**
* \ingroup BrickletLEDStrip
*
* Changes the response expected flag for all setter and callback configuration
* functions of this device at once.
*/
int led_strip_set_response_expected_all(LEDStrip *led_strip, bool response_expected);
/**
* \ingroup BrickletLEDStrip
*
* Registers a callback with ID \c id to the function \c callback. The
* \c user_data will be given as a parameter of the callback.
*/
void led_strip_register_callback(LEDStrip *led_strip, uint8_t id, void *callback, void *user_data);
/**
* \ingroup BrickletLEDStrip
*
* Returns the API version (major, minor, release) of the bindings for this
* device.
*/
int led_strip_get_api_version(LEDStrip *led_strip, uint8_t ret_api_version[3]);
/**
* \ingroup BrickletLEDStrip
*
* Sets the *rgb* values for the LEDs with the given *length* starting
* from *index*.
*
* The maximum length is 16, the index goes from 0 to 319 and the rgb values
* have 8 bits each.
*
* Example: If you set
*
* * index to 5,
* * length to 3,
* * r to [255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
* * g to [0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] and
* * b to [0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
*
* the LED with index 5 will be red, 6 will be green and 7 will be blue.
*
* The colors will be transfered to actual LEDs when the next
* frame duration ends, see {@link led_strip_set_frame_duration}.
*
* Generic approach:
*
* * Set the frame duration to a value that represents
* the number of frames per second you want to achieve.
* * Set all of the LED colors for one frame.
* * Wait for the {@link LED_STRIP_CALLBACK_FRAME_RENDERED} callback.
* * Set all of the LED colors for next frame.
* * Wait for the {@link LED_STRIP_CALLBACK_FRAME_RENDERED} callback.
* * and so on.
*
* This approach ensures that you can change the LED colors with
* a fixed frame rate.
*
* The actual number of controllable LEDs depends on the number of free
* Bricklet ports. See :ref:`here <led_strip_bricklet_ram_constraints>` for more
* information. A call of {@link led_strip_set_rgb_values} with index + length above the
* bounds is ignored completely.
*/
int led_strip_set_rgb_values(LEDStrip *led_strip, uint16_t index, uint8_t length, uint8_t r[16], uint8_t g[16], uint8_t b[16]);
/**
* \ingroup BrickletLEDStrip
*
* Returns the rgb with the given *length* starting from the
* given *index*.
*
* The values are the last values that were set by {@link led_strip_set_rgb_values}.
*/
int led_strip_get_rgb_values(LEDStrip *led_strip, uint16_t index, uint8_t length, uint8_t ret_r[16], uint8_t ret_g[16], uint8_t ret_b[16]);
/**
* \ingroup BrickletLEDStrip
*
* Sets the frame duration in ms.
*
* Example: If you want to achieve 20 frames per second, you should
* set the frame duration to 50ms (50ms * 20 = 1 second).
*
* For an explanation of the general approach see {@link led_strip_set_rgb_values}.
*
* Default value: 100ms (10 frames per second).
*/
int led_strip_set_frame_duration(LEDStrip *led_strip, uint16_t duration);
/**
* \ingroup BrickletLEDStrip
*
* Returns the frame duration as set by {@link led_strip_set_frame_duration}.
*/
int led_strip_get_frame_duration(LEDStrip *led_strip, uint16_t *ret_duration);
/**
* \ingroup BrickletLEDStrip
*
* Returns the current supply voltage of the LEDs. The voltage is given in mV.
*/
int led_strip_get_supply_voltage(LEDStrip *led_strip, uint16_t *ret_voltage);
/**
* \ingroup BrickletLEDStrip
*
* Sets the frequency of the clock in Hz. The range is 10000Hz (10kHz) up to
* 2000000Hz (2MHz).
*
* The Bricklet will choose the nearest achievable frequency, which may
* be off by a few Hz. You can get the exact frequency that is used by
* calling {@link led_strip_get_clock_frequency}.
*
* If you have problems with flickering LEDs, they may be bits flipping. You
* can fix this by either making the connection between the LEDs and the
* Bricklet shorter or by reducing the frequency.
*
* With a decreasing frequency your maximum frames per second will decrease
* too.
*
* The default value is 1.66MHz.
*
* \note
* The frequency in firmware version 2.0.0 is fixed at 2MHz.
*
* .. versionadded:: 2.0.1~(Plugin)
*/
int led_strip_set_clock_frequency(LEDStrip *led_strip, uint32_t frequency);
/**
* \ingroup BrickletLEDStrip
*
* Returns the currently used clock frequency.
*
* .. versionadded:: 2.0.1~(Plugin)
*/
int led_strip_get_clock_frequency(LEDStrip *led_strip, uint32_t *ret_frequency);
/**
* \ingroup BrickletLEDStrip
*
* Returns the UID, the UID where the Bricklet is connected to,
* the position, the hardware and firmware version as well as the
* device identifier.
*
* The position can be 'a', 'b', 'c' or 'd'.
*
* The device identifiers can be found :ref:`here <device_identifier>`.
*
* .. versionadded:: 2.0.0~(Plugin)
*/
int led_strip_get_identity(LEDStrip *led_strip, char ret_uid[8], char ret_connected_uid[8], char *ret_position, uint8_t ret_hardware_version[3], uint8_t ret_firmware_version[3], uint16_t *ret_device_identifier);
#endif

View File

@ -0,0 +1,630 @@
/*
* Copyright (C) 2012-2013 Matthias Bolte <matthias@tinkerforge.com>
* Copyright (C) 2011 Olaf Lüke <olaf@tinkerforge.com>
*
* Redistribution and use in source and binary forms of this file,
* with or without modification, are permitted.
*/
#ifndef IP_CONNECTION_H
#define IP_CONNECTION_H
/**
* \defgroup IPConnection IP Connection
*/
#ifndef __STDC_LIMIT_MACROS
#define __STDC_LIMIT_MACROS
#endif
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#if !defined __cplusplus && defined __GNUC__
#include <stdbool.h>
#endif
#ifdef _WIN32
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#else
#include <pthread.h>
#include <semaphore.h>
#endif
enum {
E_OK = 0,
E_TIMEOUT = -1,
E_NO_STREAM_SOCKET = -2,
E_HOSTNAME_INVALID = -3,
E_NO_CONNECT = -4,
E_NO_THREAD = -5,
E_NOT_ADDED = -6, // unused since v2.0
E_ALREADY_CONNECTED = -7,
E_NOT_CONNECTED = -8,
E_INVALID_PARAMETER = -9, // error response from device
E_NOT_SUPPORTED = -10, // error response from device
E_UNKNOWN_ERROR_CODE = -11 // error response from device
};
#ifdef IPCON_EXPOSE_INTERNALS
typedef struct _Socket Socket;
typedef struct {
#ifdef _WIN32
CRITICAL_SECTION handle;
#else
pthread_mutex_t handle;
#endif
} Mutex;
void mutex_create(Mutex *mutex);
void mutex_destroy(Mutex *mutex);
void mutex_lock(Mutex *mutex);
void mutex_unlock(Mutex *mutex);
typedef struct {
#ifdef _WIN32
HANDLE handle;
#else
pthread_cond_t condition;
pthread_mutex_t mutex;
bool flag;
#endif
} Event;
typedef struct {
#ifdef _WIN32
HANDLE handle;
#else
sem_t object;
sem_t *pointer;
#endif
} Semaphore;
typedef void (*ThreadFunction)(void *opaque);
typedef struct {
#ifdef _WIN32
HANDLE handle;
DWORD id;
#else
pthread_t handle;
#endif
ThreadFunction function;
void *opaque;
} Thread;
typedef struct {
Mutex mutex;
int used;
int allocated;
uint32_t *keys;
void **values;
} Table;
typedef struct _QueueItem {
struct _QueueItem *next;
int kind;
void *data;
int length;
} QueueItem;
typedef struct {
Mutex mutex;
Semaphore semaphore;
QueueItem *head;
QueueItem *tail;
} Queue;
#if defined _MSC_VER || defined __BORLANDC__
#pragma pack(push)
#pragma pack(1)
#define ATTRIBUTE_PACKED
#elif defined __GNUC__
#ifdef _WIN32
// workaround struct packing bug in GCC 4.7 on Windows
// http://gcc.gnu.org/bugzilla/show_bug.cgi?id=52991
#define ATTRIBUTE_PACKED __attribute__((gcc_struct, packed))
#else
#define ATTRIBUTE_PACKED __attribute__((packed))
#endif
#else
#error unknown compiler, do not know how to enable struct packing
#endif
typedef struct {
uint32_t uid;
uint8_t length;
uint8_t function_id;
uint8_t sequence_number_and_options;
uint8_t error_code_and_future_use;
} ATTRIBUTE_PACKED PacketHeader;
typedef struct {
PacketHeader header;
uint8_t payload[64];
uint8_t optional_data[8];
} ATTRIBUTE_PACKED Packet;
#if defined _MSC_VER || defined __BORLANDC__
#pragma pack(pop)
#endif
#undef ATTRIBUTE_PACKED
#endif // IPCON_EXPOSE_INTERNALS
typedef struct _IPConnection IPConnection;
typedef struct _IPConnectionPrivate IPConnectionPrivate;
typedef struct _Device Device;
typedef struct _DevicePrivate DevicePrivate;
#ifdef IPCON_EXPOSE_INTERNALS
typedef struct _CallbackContext CallbackContext;
#endif
typedef void (*EnumerateCallbackFunction)(const char *uid,
const char *connected_uid,
char position,
uint8_t hardware_version[3],
uint8_t firmware_version[3],
uint16_t device_identifier,
uint8_t enumeration_type,
void *user_data);
typedef void (*ConnectedCallbackFunction)(uint8_t connect_reason,
void *user_data);
typedef void (*DisconnectedCallbackFunction)(uint8_t disconnect_reason,
void *user_data);
#ifdef IPCON_EXPOSE_INTERNALS
typedef void (*CallbackWrapperFunction)(DevicePrivate *device_p, Packet *packet);
#endif
/**
* \internal
*/
struct _Device {
DevicePrivate *p;
};
#ifdef IPCON_EXPOSE_INTERNALS
#define DEVICE_NUM_FUNCTION_IDS 256
/**
* \internal
*/
struct _DevicePrivate {
uint32_t uid;
IPConnectionPrivate *ipcon_p;
uint8_t api_version[3];
Mutex request_mutex;
uint8_t expected_response_function_id; // protected by request_mutex
uint8_t expected_response_sequence_number; // protected by request_mutex
Mutex response_mutex;
Packet response_packet; // protected by response_mutex
Event response_event;
int response_expected[DEVICE_NUM_FUNCTION_IDS];
void *registered_callbacks[DEVICE_NUM_FUNCTION_IDS];
void *registered_callback_user_data[DEVICE_NUM_FUNCTION_IDS];
CallbackWrapperFunction callback_wrappers[DEVICE_NUM_FUNCTION_IDS];
};
/**
* \internal
*/
enum {
DEVICE_RESPONSE_EXPECTED_INVALID_FUNCTION_ID = 0,
DEVICE_RESPONSE_EXPECTED_ALWAYS_TRUE, // getter
DEVICE_RESPONSE_EXPECTED_ALWAYS_FALSE, // callback
DEVICE_RESPONSE_EXPECTED_TRUE, // setter
DEVICE_RESPONSE_EXPECTED_FALSE // setter, default
};
/**
* \internal
*/
void device_create(Device *device, const char *uid,
IPConnectionPrivate *ipcon_p, uint8_t api_version_major,
uint8_t api_version_minor, uint8_t api_version_release);
/**
* \internal
*/
void device_destroy(Device *device);
/**
* \internal
*/
int device_get_response_expected(DevicePrivate *device_p, uint8_t function_id,
bool *ret_response_expected);
/**
* \internal
*/
int device_set_response_expected(DevicePrivate *device_p, uint8_t function_id,
bool response_expected);
/**
* \internal
*/
int device_set_response_expected_all(DevicePrivate *device_p, bool response_expected);
/**
* \internal
*/
void device_register_callback(DevicePrivate *device_p, uint8_t id, void *callback,
void *user_data);
/**
* \internal
*/
int device_get_api_version(DevicePrivate *device_p, uint8_t ret_api_version[3]);
/**
* \internal
*/
int device_send_request(DevicePrivate *device_p, Packet *request, Packet *response);
#endif // IPCON_EXPOSE_INTERNALS
/**
* \ingroup IPConnection
*
* Possible IDs for ipcon_register_callback.
*/
enum {
IPCON_CALLBACK_ENUMERATE = 253,
IPCON_CALLBACK_CONNECTED = 0,
IPCON_CALLBACK_DISCONNECTED = 1
};
/**
* \ingroup IPConnection
*
* Possible values for enumeration_type parameter of EnumerateCallback.
*/
enum {
IPCON_ENUMERATION_TYPE_AVAILABLE = 0,
IPCON_ENUMERATION_TYPE_CONNECTED = 1,
IPCON_ENUMERATION_TYPE_DISCONNECTED = 2
};
/**
* \ingroup IPConnection
*
* Possible values for connect_reason parameter of ConnectedCallback.
*/
enum {
IPCON_CONNECT_REASON_REQUEST = 0,
IPCON_CONNECT_REASON_AUTO_RECONNECT = 1
};
/**
* \ingroup IPConnection
*
* Possible values for disconnect_reason parameter of DisconnectedCallback.
*/
enum {
IPCON_DISCONNECT_REASON_REQUEST = 0,
IPCON_DISCONNECT_REASON_ERROR = 1,
IPCON_DISCONNECT_REASON_SHUTDOWN = 2
};
/**
* \ingroup IPConnection
*
* Possible return values of ipcon_get_connection_state.
*/
enum {
IPCON_CONNECTION_STATE_DISCONNECTED = 0,
IPCON_CONNECTION_STATE_CONNECTED = 1,
IPCON_CONNECTION_STATE_PENDING = 2 // auto-reconnect in progress
};
/**
* \internal
*/
struct _IPConnection {
IPConnectionPrivate *p;
};
#ifdef IPCON_EXPOSE_INTERNALS
#define IPCON_NUM_CALLBACK_IDS 256
/**
* \internal
*/
struct _IPConnectionPrivate {
#ifdef _WIN32
bool wsa_startup_done; // protected by socket_mutex
#endif
char *host;
uint16_t port;
uint32_t timeout; // in msec
bool auto_reconnect;
bool auto_reconnect_allowed;
bool auto_reconnect_pending;
Mutex sequence_number_mutex;
uint8_t next_sequence_number; // protected by sequence_number_mutex
Table devices;
void *registered_callbacks[IPCON_NUM_CALLBACK_IDS];
void *registered_callback_user_data[IPCON_NUM_CALLBACK_IDS];
Mutex socket_mutex;
Socket *socket; // protected by socket_mutex
uint64_t socket_id; // protected by socket_mutex
bool receive_flag;
Thread receive_thread; // protected by socket_mutex
CallbackContext *callback;
bool disconnect_probe_flag;
Thread disconnect_probe_thread; // protected by socket_mutex
Event disconnect_probe_event;
Semaphore wait;
};
#endif // IPCON_EXPOSE_INTERNALS
/**
* \ingroup IPConnection
*
* Creates an IP Connection object that can be used to enumerate the available
* devices. It is also required for the constructor of Bricks and Bricklets.
*/
void ipcon_create(IPConnection *ipcon);
/**
* \ingroup IPConnection
*
* Destroys the IP Connection object. Calls ipcon_disconnect internally.
* The connection to the Brick Daemon gets closed and the threads of the
* IP Connection are terminated.
*/
void ipcon_destroy(IPConnection *ipcon);
/**
* \ingroup IPConnection
*
* Creates a TCP/IP connection to the given \c host and c\ port. The host and
* port can point to a Brick Daemon or to a WIFI/Ethernet Extension.
*
* Devices can only be controlled when the connection was established
* successfully.
*
* Blocks until the connection is established and returns an error code if
* there is no Brick Daemon or WIFI/Ethernet Extension listening at the given
* host and port.
*/
int ipcon_connect(IPConnection *ipcon, const char *host, uint16_t port);
/**
* \ingroup IPConnection
*
* Disconnects the TCP/IP connection from the Brick Daemon or the WIFI/Ethernet
* Extension.
*/
int ipcon_disconnect(IPConnection *ipcon);
/**
* \ingroup IPConnection
*
* Can return the following states:
*
* - IPCON_CONNECTION_STATE_DISCONNECTED: No connection is established.
* - IPCON_CONNECTION_STATE_CONNECTED: A connection to the Brick Daemon or
* the WIFI/Ethernet Extension is established.
* - IPCON_CONNECTION_STATE_PENDING: IP Connection is currently trying to
* connect.
*/
int ipcon_get_connection_state(IPConnection *ipcon);
/**
* \ingroup IPConnection
*
* Enables or disables auto-reconnect. If auto-reconnect is enabled,
* the IP Connection will try to reconnect to the previously given
* host and port, if the connection is lost.
*
* Default value is *true*.
*/
void ipcon_set_auto_reconnect(IPConnection *ipcon, bool auto_reconnect);
/**
* \ingroup IPConnection
*
* Returns *true* if auto-reconnect is enabled, *false* otherwise.
*/
bool ipcon_get_auto_reconnect(IPConnection *ipcon);
/**
* \ingroup IPConnection
*
* Sets the timeout in milliseconds for getters and for setters for which the
* response expected flag is activated.
*
* Default timeout is 2500.
*/
void ipcon_set_timeout(IPConnection *ipcon, uint32_t timeout);
/**
* \ingroup IPConnection
*
* Returns the timeout as set by ipcon_set_timeout.
*/
uint32_t ipcon_get_timeout(IPConnection *ipcon);
/**
* \ingroup IPConnection
*
* Broadcasts an enumerate request. All devices will respond with an enumerate
* callback.
*/
int ipcon_enumerate(IPConnection *ipcon);
/**
* \ingroup IPConnection
*
* Stops the current thread until ipcon_unwait is called.
*
* This is useful if you rely solely on callbacks for events, if you want
* to wait for a specific callback or if the IP Connection was created in
* a thread.
*
* ipcon_wait and ipcon_unwait act in the same way as "acquire" and "release"
* of a semaphore.
*/
void ipcon_wait(IPConnection *ipcon);
/**
* \ingroup IPConnection
*
* Unwaits the thread previously stopped by ipcon_wait.
*
* ipcon_wait and ipcon_unwait act in the same way as "acquire" and "release"
* of a semaphore.
*/
void ipcon_unwait(IPConnection *ipcon);
/**
* \ingroup IPConnection
*
* Registers a callback for a given ID.
*/
void ipcon_register_callback(IPConnection *ipcon, uint8_t id,
void *callback, void *user_data);
#ifdef IPCON_EXPOSE_INTERNALS
/**
* \internal
*/
int packet_header_create(PacketHeader *header, uint8_t length,
uint8_t function_id, IPConnectionPrivate *ipcon_p,
DevicePrivate *device_p);
/**
* \internal
*/
uint8_t packet_header_get_sequence_number(PacketHeader *header);
/**
* \internal
*/
void packet_header_set_sequence_number(PacketHeader *header,
uint8_t sequence_number);
/**
* \internal
*/
uint8_t packet_header_get_response_expected(PacketHeader *header);
/**
* \internal
*/
void packet_header_set_response_expected(PacketHeader *header,
uint8_t response_expected);
/**
* \internal
*/
uint8_t packet_header_get_error_code(PacketHeader *header);
/**
* \internal
*/
int16_t leconvert_int16_to(int16_t native);
/**
* \internal
*/
uint16_t leconvert_uint16_to(uint16_t native);
/**
* \internal
*/
int32_t leconvert_int32_to(int32_t native);
/**
* \internal
*/
uint32_t leconvert_uint32_to(uint32_t native);
/**
* \internal
*/
int64_t leconvert_int64_to(int64_t native);
/**
* \internal
*/
uint64_t leconvert_uint64_to(uint64_t native);
/**
* \internal
*/
float leconvert_float_to(float native);
/**
* \internal
*/
int16_t leconvert_int16_from(int16_t little);
/**
* \internal
*/
uint16_t leconvert_uint16_from(uint16_t little);
/**
* \internal
*/
int32_t leconvert_int32_from(int32_t little);
/**
* \internal
*/
uint32_t leconvert_uint32_from(uint32_t little);
/**
* \internal
*/
int64_t leconvert_int64_from(int64_t little);
/**
* \internal
*/
uint64_t leconvert_uint64_from(uint64_t little);
/**
* \internal
*/
float leconvert_float_from(float little);
#endif // IPCON_EXPOSE_INTERNALS
#endif

Binary file not shown.

View File

@ -1 +1 @@
f3afa39f64294a9ce71af8e424e4bab72d2b29cb
9546e335179f732ff68ea9bc47020a19e4b6f44c

View File

@ -4,6 +4,7 @@
"args" :
{
"speed" : 1.0,
"fadeFactor" : 0.7
"fadeFactor" : 0.7,
"color" : [255,0,0]
}
}

View File

@ -2,9 +2,10 @@ import hyperion
import time
import colorsys
# Get the rotation time
# Get the parameters
speed = float(hyperion.args.get('speed', 1.0))
fadeFactor = float(hyperion.args.get('fadeFactor', 0.7))
color = hyperion.args.get('color', (255,0,0))
# Check parameters
speed = max(0.0001, speed)
@ -13,7 +14,9 @@ fadeFactor = max(0.0, min(fadeFactor, 1.0))
# Initialize the led data
width = 25
imageData = bytearray(width * (0,0,0))
imageData[0] = 255
imageData[0] = color[0]
imageData[1] = color[1]
imageData[2] = color[2]
# Calculate the sleep time and rotation increment
increment = 1
@ -41,9 +44,13 @@ while not hyperion.abort():
# Fade the old data
for j in range(width):
imageData[3*j] = int(fadeFactor * imageData[3*j])
imageData[3*j+1] = int(fadeFactor * imageData[3*j+1])
imageData[3*j+2] = int(fadeFactor * imageData[3*j+2])
# Insert new data
imageData[3*position] = 255
imageData[3*position] = color[0]
imageData[3*position+1] = color[1]
imageData[3*position+2] = color[2]
# Sleep for a while
time.sleep(sleepTime)

View File

@ -0,0 +1,16 @@
{
"name" : "Cold mood blobs",
"script" : "mood-blobs.py",
"args" :
{
"rotationTime" : 60.0,
"color" : [0,0,255],
"hueChange" : 30.0,
"blobs" : 5,
"reverse" : false,
"baseChange" : true,
"baseColorRangeLeft" : 160,
"baseColorRangeRight" : 320,
"baseColorChangeRate" : 2.0
}
}

View File

@ -0,0 +1,16 @@
{
"name" : "Full color mood blobs",
"script" : "mood-blobs.py",
"args" :
{
"rotationTime" : 60.0,
"color" : [0,0,255],
"hueChange" : 30.0,
"blobs" : 5,
"reverse" : false,
"baseChange" : true,
"baseColorRangeLeft" : 0,
"baseColorRangeRight" : 360,
"baseColorChangeRate" : 0.2
}
}

View File

@ -0,0 +1,16 @@
{
"name" : "Warm mood blobs",
"script" : "mood-blobs.py",
"args" :
{
"rotationTime" : 60.0,
"color" : [255,0,0],
"hueChange" : 30.0,
"blobs" : 5,
"reverse" : false,
"baseChange" : true,
"baseColorRangeLeft" : 333,
"baseColorRangeRight" : 151,
"baseColorChangeRate" : 2.0
}
}

View File

@ -6,14 +6,31 @@ import math
# Get the parameters
rotationTime = float(hyperion.args.get('rotationTime', 20.0))
color = hyperion.args.get('color', (0,0,255))
hueChange = float(hyperion.args.get('hueChange', 60.0)) / 360.0
hueChange = float(hyperion.args.get('hueChange', 60.0))
blobs = int(hyperion.args.get('blobs', 5))
reverse = bool(hyperion.args.get('reverse', False))
baseColorChange = bool(hyperion.args.get('baseChange', False))
baseColorRangeLeft = float(hyperion.args.get('baseColorRangeLeft',0.0)) # Degree
baseColorRangeRight = float(hyperion.args.get('baseColorRangeRight',360.0)) # Degree
baseColorChangeRate = float(hyperion.args.get('baseColorChangeRate',10.0)) # Seconds for one Degree
# switch baseColor change off if left and right are too close together to see a difference in color
if (baseColorRangeRight > baseColorRangeLeft and (baseColorRangeRight - baseColorRangeLeft) < 10) or \
(baseColorRangeLeft > baseColorRangeRight and ((baseColorRangeRight + 360) - baseColorRangeLeft) < 10):
baseColorChange = False
# 360 -> 1
fullColorWheelAvailable = (baseColorRangeRight % 360) == (baseColorRangeLeft % 360)
baseColorChangeIncreaseValue = 1.0 / 360.0 # 1 degree
hueChange /= 360.0
baseColorRangeLeft = (baseColorRangeLeft / 360.0)
baseColorRangeRight = (baseColorRangeRight / 360.0)
# Check parameters
rotationTime = max(0.1, rotationTime)
hueChange = max(0.0, min(abs(hueChange), .5))
blobs = max(1, blobs)
baseColorChangeRate = max(0, baseColorChangeRate) # > 0
# Calculate the color data
baseHsv = colorsys.rgb_to_hsv(color[0]/255.0, color[1]/255.0, color[2]/255.0)
@ -27,6 +44,7 @@ for i in range(hyperion.ledCount):
sleepTime = 0.1
amplitudePhaseIncrement = blobs * math.pi * sleepTime / rotationTime
colorDataIncrement = 3
baseColorChangeRate /= sleepTime
# Switch direction if needed
if reverse:
@ -39,23 +57,58 @@ colors = bytearray(hyperion.ledCount * (0,0,0))
# Start the write data loop
amplitudePhase = 0.0
rotateColors = False
while not hyperion.abort():
# Calculate new colors
for i in range(hyperion.ledCount):
amplitude = max(0.0, math.sin(-amplitudePhase + 2*math.pi * blobs * i / hyperion.ledCount))
colors[3*i+0] = int(colorData[3*i+0] * amplitude)
colors[3*i+1] = int(colorData[3*i+1] * amplitude)
colors[3*i+2] = int(colorData[3*i+2] * amplitude)
baseColorChangeStepCount = 0
baseHSVValue = baseHsv[0]
numberOfRotates = 0
# set colors
hyperion.setColor(colors)
# increment the phase
amplitudePhase = (amplitudePhase + amplitudePhaseIncrement) % (2*math.pi)
if rotateColors:
colorData = colorData[-colorDataIncrement:] + colorData[:-colorDataIncrement]
rotateColors = not rotateColors
# sleep for a while
time.sleep(sleepTime)
while not hyperion.abort():
# move the basecolor
if baseColorChange:
# every baseColorChangeRate seconds
if baseColorChangeStepCount >= baseColorChangeRate:
baseColorChangeStepCount = 0
# cyclic increment when the full colorwheel is available, move up and down otherwise
if fullColorWheelAvailable:
baseHSVValue = (baseHSVValue + baseColorChangeIncreaseValue) % baseColorRangeRight
else:
# switch increment direction if baseHSV <= left or baseHSV >= right
if baseColorChangeIncreaseValue < 0 and baseHSVValue > baseColorRangeLeft and (baseHSVValue + baseColorChangeIncreaseValue) <= baseColorRangeLeft:
baseColorChangeIncreaseValue = abs(baseColorChangeIncreaseValue)
elif baseColorChangeIncreaseValue > 0 and baseHSVValue < baseColorRangeRight and (baseHSVValue + baseColorChangeIncreaseValue) >= baseColorRangeRight :
baseColorChangeIncreaseValue = -abs(baseColorChangeIncreaseValue)
baseHSVValue = (baseHSVValue + baseColorChangeIncreaseValue) % 1.0
# update color values
colorData = bytearray()
for i in range(hyperion.ledCount):
hue = (baseHSVValue + hueChange * math.sin(2*math.pi * i / hyperion.ledCount)) % 1.0
rgb = colorsys.hsv_to_rgb(hue, baseHsv[1], baseHsv[2])
colorData += bytearray((int(255*rgb[0]), int(255*rgb[1]), int(255*rgb[2])))
# set correct rotation after reinitialisation of the array
colorData = colorData[-colorDataIncrement*numberOfRotates:] + colorData[:-colorDataIncrement*numberOfRotates]
baseColorChangeStepCount += 1
# Calculate new colors
for i in range(hyperion.ledCount):
amplitude = max(0.0, math.sin(-amplitudePhase + 2*math.pi * blobs * i / hyperion.ledCount))
colors[3*i+0] = int(colorData[3*i+0] * amplitude)
colors[3*i+1] = int(colorData[3*i+1] * amplitude)
colors[3*i+2] = int(colorData[3*i+2] * amplitude)
# set colors
hyperion.setColor(colors)
# increment the phase
amplitudePhase = (amplitudePhase + amplitudePhaseIncrement) % (2*math.pi)
if rotateColors:
colorData = colorData[-colorDataIncrement:] + colorData[:-colorDataIncrement]
numberOfRotates = (numberOfRotates + 1) % hyperion.ledCount
rotateColors = not rotateColors
# sleep for a while
time.sleep(sleepTime)

View File

@ -1,4 +1,3 @@
#pragma once
// Utils includes
@ -126,7 +125,7 @@ namespace hyperion
inline bool isBlack(const Pixel_T & color)
{
// Return the simple compare of the color against black
return color.red < _blackborderThreshold && color.green < _blackborderThreshold && color.green < _blackborderThreshold;
return color.red < _blackborderThreshold && color.green < _blackborderThreshold && color.blue < _blackborderThreshold;
}
private:

View File

@ -55,5 +55,5 @@ private:
std::list<Effect *> _activeEffects;
PyThreadState * _mainThreadState;
PyThreadState * _mainThreadState;
};

View File

@ -73,7 +73,7 @@ private:
const int _updateInterval_ms;
/// The timeout of the led colors [ms]
const int _timeout_ms;
/// The priority of the led colors [ms]
/// The priority of the led colors
const int _priority;
/// The timer for generating events with the specified update rate

View File

@ -0,0 +1,36 @@
#pragma once
#include <string>
#include <algorithm>
/**
* Enumeration of the possible pixel formats the grabber can be set to
*/
enum PixelFormat {
PIXELFORMAT_YUYV,
PIXELFORMAT_UYVY,
PIXELFORMAT_RGB32,
PIXELFORMAT_NO_CHANGE
};
inline PixelFormat parsePixelFormat(std::string pixelFormat)
{
// convert to lower case
std::transform(pixelFormat.begin(), pixelFormat.end(), pixelFormat.begin(), ::tolower);
if (pixelFormat == "yuyv")
{
return PIXELFORMAT_YUYV;
}
else if (pixelFormat == "uyvy")
{
return PIXELFORMAT_UYVY;
}
else if (pixelFormat == "rgb32")
{
return PIXELFORMAT_RGB32;
}
// return the default NO_CHANGE
return PIXELFORMAT_NO_CHANGE;
}

View File

@ -4,31 +4,30 @@
#include <string>
#include <vector>
// Qt includes
#include <QObject>
#include <QSocketNotifier>
// util includes
#include <utils/Image.h>
#include <utils/ColorRgb.h>
#include <utils/VideoMode.h>
// grabber includes
#include <grabber/VideoStandard.h>
#include <grabber/PixelFormat.h>
/// Capture class for V4L2 devices
///
/// @see http://linuxtv.org/downloads/v4l-dvb-apis/capture-example.html
class V4L2Grabber
class V4L2Grabber : public QObject
{
public:
typedef void (*ImageCallback)(void * arg, const Image<ColorRgb> & image);
enum VideoStandard {
PAL, NTSC, NO_CHANGE
};
enum Mode3D {
MODE_NONE, MODE_3DSBS, MODE_3DTAB
};
Q_OBJECT
public:
V4L2Grabber(
const std::string & device,
V4L2Grabber(const std::string & device,
int input,
VideoStandard videoStandard,
VideoStandard videoStandard, PixelFormat pixelFormat,
int width,
int height,
int frameDecimation,
@ -36,21 +35,29 @@ public:
int verticalPixelDecimation);
virtual ~V4L2Grabber();
public slots:
void setCropping(int cropLeft,
int cropRight,
int cropTop,
int cropBottom);
void set3D(Mode3D mode);
void set3D(VideoMode mode);
void setCallback(ImageCallback callback, void * arg);
void setSignalThreshold(double redSignalThreshold,
double greenSignalThreshold,
double blueSignalThreshold,
int noSignalCounterThreshold);
void start();
void capture(int frameCount = -1);
void stop();
signals:
void newFrame(const Image<ColorRgb> & image);
private slots:
int read_frame();
private:
void open_device();
@ -70,8 +77,6 @@ private:
void stop_capturing();
int read_frame();
bool process_image(const void *p, int size);
void process_image(const uint8_t *p);
@ -100,9 +105,10 @@ private:
int _fileDescriptor;
std::vector<buffer> _buffers;
uint32_t _pixelFormat;
PixelFormat _pixelFormat;
int _width;
int _height;
int _frameByteSize;
int _cropLeft;
int _cropRight;
int _cropTop;
@ -110,11 +116,14 @@ private:
int _frameDecimation;
int _horizontalPixelDecimation;
int _verticalPixelDecimation;
int _noSignalCounterThreshold;
Mode3D _mode3D;
ColorRgb _noSignalThresholdColor;
VideoMode _mode3D;
int _currentFrame;
int _noSignalCounter;
ImageCallback _callback;
void * _callbackArg;
QSocketNotifier * _streamNotifier;
};

View File

@ -0,0 +1,74 @@
#pragma once
// Qt includes
#include <QTimer>
// Hyperion includes
#include <hyperion/Hyperion.h>
#include <hyperion/ImageProcessor.h>
// Grabber includes
#include <grabber/V4L2Grabber.h>
class V4L2Wrapper : public QObject
{
Q_OBJECT
public:
V4L2Wrapper(const std::string & device,
int input,
VideoStandard videoStandard,
PixelFormat pixelFormat,
int width,
int height,
int frameDecimation,
int pixelDecimation,
double redSignalThreshold,
double greenSignalThreshold,
double blueSignalThreshold,
Hyperion * hyperion,
int hyperionPriority);
virtual ~V4L2Wrapper();
public slots:
void start();
void stop();
void setCropping(int cropLeft,
int cropRight,
int cropTop,
int cropBottom);
void set3D(VideoMode mode);
signals:
void emitColors(int priority, const std::vector<ColorRgb> &ledColors, const int timeout_ms);
private slots:
void newFrame(const Image<ColorRgb> & image);
void checkSources();
private:
/// The timeout of the led colors [ms]
const int _timeout_ms;
/// The priority of the led colors
const int _priority;
/// The V4L2 grabber
V4L2Grabber _grabber;
/// The processor for transforming images to led colors
ImageProcessor * _processor;
/// The Hyperion instance
Hyperion * _hyperion;
/// The list with computed led colors
std::vector<ColorRgb> _ledColors;
/// Timer which tests if a higher priority source is active
QTimer _timer;
};

View File

@ -0,0 +1,31 @@
#pragma once
#include <string>
#include <algorithm>
/**
* Enumeration of the possible video standards the grabber can be set to
*/
enum VideoStandard {
VIDEOSTANDARD_PAL,
VIDEOSTANDARD_NTSC,
VIDEOSTANDARD_NO_CHANGE
};
inline VideoStandard parseVideoStandard(std::string videoStandard)
{
// convert to lower case
std::transform(videoStandard.begin(), videoStandard.end(), videoStandard.begin(), ::tolower);
if (videoStandard == "pal")
{
return VIDEOSTANDARD_PAL;
}
else if (videoStandard == "ntsc")
{
return VIDEOSTANDARD_NTSC;
}
// return the default NO_CHANGE
return VIDEOSTANDARD_NO_CHANGE;
}

View File

@ -37,6 +37,9 @@ public:
///
void setSize(const unsigned width, const unsigned height);
/// Enable or disable the black border detector
void enableBalckBorderDetector(bool enable);
///
/// Processes the image to a list of led colors. This will update the size of the buffer-image
/// if required and call the image-to-leds mapping to determine the mean color per led.
@ -142,7 +145,7 @@ private:
const LedString _ledString;
/// Flag the enables(true)/disabled(false) blackborder detector
const bool _enableBlackBorderRemoval;
bool _enableBlackBorderRemoval;
/// The processor for black border detection
hyperion::BlackBorderProcessor * _borderProcessor;

View File

@ -48,3 +48,16 @@ inline std::ostream& operator<<(std::ostream& os, const ColorRgb& color)
os << "{" << unsigned(color.red) << "," << unsigned(color.green) << "," << unsigned(color.blue) << "}";
return os;
}
/// Compare operator to check if a color is 'smaller' than another color
inline bool operator<(const ColorRgb & lhs, const ColorRgb & rhs)
{
return (lhs.red < rhs.red) && (lhs.green < rhs.green) && (lhs.blue < rhs.blue);
}
/// Compare operator to check if a color is 'smaller' than or 'equal' to another color
inline bool operator<=(const ColorRgb & lhs, const ColorRgb & rhs)
{
return (lhs.red <= rhs.red) && (lhs.green <= rhs.green) && (lhs.blue <= rhs.blue);
}

View File

@ -13,6 +13,18 @@ public:
typedef Pixel_T pixel_type;
///
/// Default constructor for an image
///
Image() :
_width(1),
_height(1),
_pixels(new Pixel_T[2]),
_endOfPixels(_pixels + 1)
{
memset(_pixels, 0, 2*sizeof(Pixel_T));
}
///
/// Constructor for an image with specified width and height
///
@ -22,8 +34,8 @@ public:
Image(const unsigned width, const unsigned height) :
_width(width),
_height(height),
_pixels(new Pixel_T[width*height + 1]),
_endOfPixels(_pixels + width*height)
_pixels(new Pixel_T[width * height + 1]),
_endOfPixels(_pixels + width * height)
{
memset(_pixels, 0, (_width*_height+1)*sizeof(Pixel_T));
}
@ -38,12 +50,24 @@ public:
Image(const unsigned width, const unsigned height, const Pixel_T background) :
_width(width),
_height(height),
_pixels(new Pixel_T[width*height + 1]),
_endOfPixels(_pixels + width*height)
_pixels(new Pixel_T[width * height + 1]),
_endOfPixels(_pixels + width * height)
{
std::fill(_pixels, _endOfPixels, background);
}
///
/// Copy constructor for an image
///
Image(const Image & other) :
_width(other._width),
_height(other._height),
_pixels(new Pixel_T[other._width * other._height + 1]),
_endOfPixels(_pixels + other._width * other._height)
{
memcpy(_pixels, other._pixels, other._width * other._height * sizeof(Pixel_T));
}
///
/// Destructor
///

10
include/utils/Sleep.h Normal file
View File

@ -0,0 +1,10 @@
#pragma once
#include <QThread>
class Sleep : protected QThread {
public:
static inline void msleep(unsigned long msecs) {
QThread::msleep(msecs);
}
};

View File

@ -1,5 +1,8 @@
#pragma once
#include <string>
#include <algorithm>
/**
* Enumeration of the possible modes in which video can be playing (2D, 3D)
*/
@ -9,3 +12,21 @@ enum VideoMode
VIDEO_3DSBS,
VIDEO_3DTAB
};
inline VideoMode parse3DMode(std::string videoMode)
{
// convert to lower case
std::transform(videoMode.begin(), videoMode.end(), videoMode.begin(), ::tolower);
if (videoMode == "3DTAB")
{
return VIDEO_3DTAB;
}
else if (videoMode == "3DSBS")
{
return VIDEO_3DSBS;
}
// return the default 2D
return VIDEO_2D;
}

View File

@ -38,7 +38,7 @@ public:
/// @param grabPhoto Whether or not to grab when the XBMC photo player is playing
/// @param grabAudio Whether or not to grab when the XBMC audio player is playing
/// @param grabMenu Whether or not to grab when nothing is playing (in XBMC menu)
/// @param grabScreensaver Whether or not to grab when the XBMC screensaver is activated
/// @param grabScreensaver Whether or not to grab when the XBMC screensaver is activated
/// @param enable3DDetection Wheter or not to enable the detection of 3D movies playing
///
XBMCVideoChecker(const std::string & address, uint16_t port, bool grabVideo, bool grabPhoto, bool grabAudio, bool grabMenu, bool grabScreensaver, bool enable3DDetection);
@ -96,6 +96,12 @@ private:
/// The JSON-RPC message to check the screensaver
const QString _checkScreensaverRequest;
/// The JSON-RPC message to check the active stereoscopicmode
const QString _getStereoscopicMode;
/// The JSON-RPC message to check the xbmc version
const QString _getXbmcVersion;
/// The QT TCP Socket with connection to XBMC
QTcpSocket _socket;
@ -111,7 +117,7 @@ private:
/// Flag indicating whether or not to grab when XBMC is playing nothing (in menu)
const bool _grabMenu;
/// Flag inidcating whether or not to grab when the XBMC screensaver is activated
/// Flag indicating whether or not to grab when the XBMC screensaver is activated
const bool _grabScreensaver;
/// Flag indicating wheter or not to enable the detection of 3D movies playing
@ -125,4 +131,7 @@ private:
/// Previous emitted video mode
VideoMode _previousVideoMode;
/// XBMC version number
int _xbmcVersion;
};

View File

@ -12,7 +12,4 @@ add_subdirectory(leddevice)
add_subdirectory(utils)
add_subdirectory(xbmcvideochecker)
add_subdirectory(effectengine)
if (ENABLE_DISPMANX)
add_subdirectory(dispmanx-grabber)
endif (ENABLE_DISPMANX)
add_subdirectory(grabber)

View File

@ -28,9 +28,5 @@ add_library(boblightserver
target_link_libraries(boblightserver
hyperion
hyperion-utils)
qt4_use_modules(boblightserver
Core
Gui
Network)
hyperion-utils
${QT_LIBRARIES})

View File

@ -19,9 +19,39 @@ PyMethodDef Effect::effectMethods[] = {
{NULL, NULL, 0, NULL}
};
#if PY_MAJOR_VERSION >= 3
// create the hyperion module
struct PyModuleDef Effect::moduleDef = {
PyModuleDef_HEAD_INIT,
"hyperion", /* m_name */
"Hyperion module", /* m_doc */
-1, /* m_size */
Effect::effectMethods, /* m_methods */
NULL, /* m_reload */
NULL, /* m_traverse */
NULL, /* m_clear */
NULL, /* m_free */
};
Effect::Effect(int priority, int timeout, const std::string & script, const Json::Value & args) :
PyObject* Effect::PyInit_hyperion()
{
return PyModule_Create(&moduleDef);
}
#else
void Effect::PyInit_hyperion()
{
Py_InitModule("hyperion", effectMethods);
}
#endif
void Effect::registerHyperionExtensionModule()
{
PyImport_AppendInittab("hyperion", &PyInit_hyperion);
}
Effect::Effect(PyThreadState * mainThreadState, int priority, int timeout, const std::string & script, const Json::Value & args) :
QThread(),
_mainThreadState(mainThreadState),
_priority(priority),
_timeout(timeout),
_script(script),
@ -34,6 +64,9 @@ Effect::Effect(int priority, int timeout, const std::string & script, const Json
{
_colors.resize(_imageProcessor->getLedCount(), ColorRgb::BLACK);
// disable the black border detector for effects
_imageProcessor->enableBalckBorderDetector(false);
// connect the finished signal
connect(this, SIGNAL(finished()), this, SLOT(effectFinished()));
}
@ -44,20 +77,26 @@ Effect::~Effect()
void Effect::run()
{
// switch to the main thread state and acquire the GIL
PyEval_RestoreThread(_mainThreadState);
// Initialize a new thread state
PyEval_AcquireLock(); // Get the GIL
_interpreterThreadState = Py_NewInterpreter();
// add methods extra builtin methods to the interpreter
PyObject * thisCapsule = PyCapsule_New(this, nullptr, nullptr);
PyObject * module = Py_InitModule4("hyperion", effectMethods, nullptr, thisCapsule, PYTHON_API_VERSION);
// import the buildtin Hyperion module
PyObject * module = PyImport_ImportModule("hyperion");
// add a capsule containing 'this' to the module to be able to retrieve the effect from the callback function
PyObject_SetAttrString(module, "__effectObj", PyCapsule_New(this, nullptr, nullptr));
// add ledCount variable to the interpreter
PyObject_SetAttrString(module, "ledCount", Py_BuildValue("i", _imageProcessor->getLedCount()));
// add a args variable to the interpreter
PyObject_SetAttrString(module, "args", json2python(_args));
//PyObject_SetAttrString(module, "args", Py_BuildValue("s", _args.c_str()));
// decref the module
Py_XDECREF(module);
// Set the end time if applicable
if (_timeout > 0)
@ -119,19 +158,23 @@ PyObject *Effect::json2python(const Json::Value &json) const
return Py_BuildValue("s", json.asCString());
case Json::objectValue:
{
PyObject * obj = PyDict_New();
PyObject * dict= PyDict_New();
for (Json::Value::iterator i = json.begin(); i != json.end(); ++i)
{
PyDict_SetItemString(obj, i.memberName(), json2python(*i));
PyObject * obj = json2python(*i);
PyDict_SetItemString(dict, i.memberName(), obj);
Py_XDECREF(obj);
}
return obj;
return dict;
}
case Json::arrayValue:
{
PyObject * list = PyList_New(json.size());
for (Json::Value::iterator i = json.begin(); i != json.end(); ++i)
{
PyList_SetItem(list, i.index(), json2python(*i));
PyObject * obj = json2python(*i);
PyList_SetItem(list, i.index(), obj);
Py_XDECREF(obj);
}
return list;
}
@ -144,7 +187,7 @@ PyObject *Effect::json2python(const Json::Value &json) const
PyObject* Effect::wrapSetColor(PyObject *self, PyObject *args)
{
// get the effect
Effect * effect = getEffect(self);
Effect * effect = getEffect();
// check if we have aborted already
if (effect->_abortRequested)
@ -229,7 +272,7 @@ PyObject* Effect::wrapSetColor(PyObject *self, PyObject *args)
PyObject* Effect::wrapSetImage(PyObject *self, PyObject *args)
{
// get the effect
Effect * effect = getEffect(self);
Effect * effect = getEffect();
// check if we have aborted already
if (effect->_abortRequested)
@ -292,7 +335,7 @@ PyObject* Effect::wrapSetImage(PyObject *self, PyObject *args)
PyObject* Effect::wrapAbort(PyObject *self, PyObject *)
{
Effect * effect = getEffect(self);
Effect * effect = getEffect();
// Test if the effect has reached it end time
if (effect->_timeout > 0 && QDateTime::currentMSecsSinceEpoch() > effect->_endTime)
@ -303,8 +346,33 @@ PyObject* Effect::wrapAbort(PyObject *self, PyObject *)
return Py_BuildValue("i", effect->_abortRequested ? 1 : 0);
}
Effect * Effect::getEffect(PyObject *self)
Effect * Effect::getEffect()
{
// Get the effect from the capsule in the self pointer
return reinterpret_cast<Effect *>(PyCapsule_GetPointer(self, nullptr));
// extract the module from the runtime
PyObject * module = PyObject_GetAttrString(PyImport_AddModule("__main__"), "hyperion");
if (!PyModule_Check(module))
{
// something is wrong
Py_XDECREF(module);
std::cerr << "Unable to retrieve the effect object from the Python runtime" << std::endl;
return nullptr;
}
// retrieve the capsule with the effect
PyObject * effectCapsule = PyObject_GetAttrString(module, "__effectObj");
Py_XDECREF(module);
if (!PyCapsule_CheckExact(effectCapsule))
{
// something is wrong
Py_XDECREF(effectCapsule);
std::cerr << "Unable to retrieve the effect object from the Python runtime" << std::endl;
return nullptr;
}
// Get the effect from the capsule
Effect * effect = reinterpret_cast<Effect *>(PyCapsule_GetPointer(effectCapsule, nullptr));
Py_XDECREF(effectCapsule);
return effect;
}

View File

@ -14,7 +14,7 @@ class Effect : public QThread
Q_OBJECT
public:
Effect(int priority, int timeout, const std::string & script, const Json::Value & args = Json::Value());
Effect(PyThreadState * mainThreadState, int priority, int timeout, const std::string & script, const Json::Value & args = Json::Value());
virtual ~Effect();
virtual void run();
@ -23,6 +23,9 @@ public:
bool isAbortRequested() const;
/// This function registers the extension module in Python
static void registerHyperionExtensionModule();
public slots:
void abort();
@ -38,13 +41,22 @@ private:
PyObject * json2python(const Json::Value & json) const;
// Wrapper methods for Python interpreter extra buildin methods
static PyMethodDef effectMethods[];
static PyObject* wrapSetColor(PyObject *self, PyObject *args);
static PyMethodDef effectMethods[];
static PyObject* wrapSetColor(PyObject *self, PyObject *args);
static PyObject* wrapSetImage(PyObject *self, PyObject *args);
static PyObject* wrapAbort(PyObject *self, PyObject *args);
static Effect * getEffect(PyObject *self);
static Effect * getEffect();
#if PY_MAJOR_VERSION >= 3
static struct PyModuleDef moduleDef;
static PyObject* PyInit_hyperion();
#else
static void PyInit_hyperion();
#endif
private:
PyThreadState * _mainThreadState;
const int _priority;
const int _timeout;

View File

@ -54,6 +54,7 @@ EffectEngine::EffectEngine(Hyperion * hyperion, const Json::Value & jsonEffectCo
// initialize the python interpreter
std::cout << "Initializing Python interpreter" << std::endl;
Effect::registerHyperionExtensionModule();
Py_InitializeEx(0);
PyEval_InitThreads(); // Create the GIL
_mainThreadState = PyEval_SaveThread();
@ -151,7 +152,7 @@ int EffectEngine::runEffectScript(const std::string &script, const Json::Value &
channelCleared(priority);
// create the effect
Effect * effect = new Effect(priority, timeout, script, args);
Effect * effect = new Effect(_mainThreadState, priority, timeout, script, args);
connect(effect, SIGNAL(setColors(int,std::vector<ColorRgb>,int,bool)), _hyperion, SLOT(setColors(int,std::vector<ColorRgb>,int,bool)), Qt::QueuedConnection);
connect(effect, SIGNAL(effectFinished(Effect*)), this, SLOT(effectFinished(Effect*)));
_activeEffects.push_back(effect);

View File

@ -0,0 +1,8 @@
if (ENABLE_DISPMANX)
add_subdirectory(dispmanx)
endif (ENABLE_DISPMANX)
if (ENABLE_V4L2)
add_subdirectory(v4l2)
endif (ENABLE_V4L2)

View File

@ -4,8 +4,8 @@ find_package(BCM REQUIRED)
include_directories(${BCM_INCLUDE_DIRS})
# Define the current source locations
SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/dispmanx-grabber)
SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/dispmanx-grabber)
SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/grabber)
SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/grabber/dispmanx)
# Group the headers that go through the MOC compiler
SET(DispmanxGrabberQT_HEADERS

View File

@ -7,8 +7,8 @@
#include <hyperion/ImageProcessorFactory.h>
#include <hyperion/ImageProcessor.h>
// Local-dispmanx includes
#include <dispmanx-grabber/DispmanxWrapper.h>
// Dispmanx grabber includes
#include <grabber/DispmanxWrapper.h>
#include "DispmanxFrameGrabber.h"

View File

@ -0,0 +1,32 @@
# Define the current source locations
SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/grabber)
SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/grabber/v4l2)
SET(V4L2_QT_HEADERS
${CURRENT_HEADER_DIR}/V4L2Grabber.h
${CURRENT_HEADER_DIR}/V4L2Wrapper.h
)
SET(V4L2_HEADERS
${CURRENT_HEADER_DIR}/VideoStandard.h
${CURRENT_HEADER_DIR}/PixelFormat.h
)
SET(V4L2_SOURCES
${CURRENT_SOURCE_DIR}/V4L2Grabber.cpp
${CURRENT_SOURCE_DIR}/V4L2Wrapper.cpp
)
QT4_WRAP_CPP(V4L2_HEADERS_MOC ${V4L2_QT_HEADERS})
add_library(v4l2-grabber
${V4L2_HEADERS}
${V4L2_SOURCES}
${V4L2_QT_HEADERS}
${V4L2_HEADERS_MOC}
)
target_link_libraries(v4l2-grabber
hyperion
${QT_LIBRARIES}
)

View File

@ -14,7 +14,7 @@
#include <sys/ioctl.h>
#include <linux/videodev2.h>
#include "V4L2Grabber.h"
#include "grabber/V4L2Grabber.h"
#define CLEAR(x) memset(&(x), 0, sizeof(x))
@ -36,10 +36,10 @@ static void yuv2rgb(uint8_t y, uint8_t u, uint8_t v, uint8_t & r, uint8_t & g, u
}
V4L2Grabber::V4L2Grabber(
const std::string & device,
V4L2Grabber::V4L2Grabber(const std::string & device,
int input,
VideoStandard videoStandard,
PixelFormat pixelFormat,
int width,
int height,
int frameDecimation,
@ -49,9 +49,10 @@ V4L2Grabber::V4L2Grabber(
_ioMethod(IO_METHOD_MMAP),
_fileDescriptor(-1),
_buffers(),
_pixelFormat(0),
_pixelFormat(pixelFormat),
_width(width),
_height(height),
_frameByteSize(-1),
_cropLeft(0),
_cropRight(0),
_cropTop(0),
@ -59,10 +60,12 @@ V4L2Grabber::V4L2Grabber(
_frameDecimation(std::max(1, frameDecimation)),
_horizontalPixelDecimation(std::max(1, horizontalPixelDecimation)),
_verticalPixelDecimation(std::max(1, verticalPixelDecimation)),
_mode3D(MODE_NONE),
_noSignalCounterThreshold(50),
_noSignalThresholdColor(ColorRgb{0,0,0}),
_mode3D(VIDEO_2D),
_currentFrame(0),
_callback(nullptr),
_callbackArg(nullptr)
_noSignalCounter(0),
_streamNotifier(nullptr)
{
open_device();
init_device(videoStandard, input);
@ -70,6 +73,8 @@ V4L2Grabber::V4L2Grabber(
V4L2Grabber::~V4L2Grabber()
{
// stop if the grabber was not stopped
stop();
uninit_device();
close_device();
}
@ -82,66 +87,39 @@ void V4L2Grabber::setCropping(int cropLeft, int cropRight, int cropTop, int crop
_cropBottom = cropBottom;
}
void V4L2Grabber::set3D(Mode3D mode)
void V4L2Grabber::set3D(VideoMode mode)
{
_mode3D = mode;
}
void V4L2Grabber::setCallback(V4L2Grabber::ImageCallback callback, void *arg)
void V4L2Grabber::setSignalThreshold(double redSignalThreshold, double greenSignalThreshold, double blueSignalThreshold, int noSignalCounterThreshold)
{
_callback = callback;
_callbackArg = arg;
_noSignalThresholdColor.red = uint8_t(255*redSignalThreshold);
_noSignalThresholdColor.green = uint8_t(255*greenSignalThreshold);
_noSignalThresholdColor.blue = uint8_t(255*blueSignalThreshold);
_noSignalCounterThreshold = std::max(1, noSignalCounterThreshold);
std::cout << "V4L2 grabber signal threshold set to: " << _noSignalThresholdColor << std::endl;
}
void V4L2Grabber::start()
{
start_capturing();
}
void V4L2Grabber::capture(int frameCount)
{
for (int count = 0; count < frameCount || frameCount < 0; ++count)
if (_streamNotifier != nullptr && !_streamNotifier->isEnabled())
{
for (;;)
{
// the set of file descriptors for select
fd_set fds;
FD_ZERO(&fds);
FD_SET(_fileDescriptor, &fds);
// timeout
struct timeval tv;
tv.tv_sec = 2;
tv.tv_usec = 0;
// block until data is available
int r = select(_fileDescriptor + 1, &fds, NULL, NULL, &tv);
if (-1 == r)
{
if (EINTR == errno)
continue;
throw_errno_exception("select");
}
if (0 == r)
{
throw_exception("select timeout");
}
if (read_frame())
{
break;
}
/* EAGAIN - continue select loop. */
}
_streamNotifier->setEnabled(true);
start_capturing();
std::cout << "V4L2 grabber started" << std::endl;
}
}
void V4L2Grabber::stop()
{
stop_capturing();
if (_streamNotifier != nullptr && _streamNotifier->isEnabled())
{
stop_capturing();
_streamNotifier->setEnabled(false);
std::cout << "V4L2 grabber stopped" << std::endl;
}
}
void V4L2Grabber::open_device()
@ -170,6 +148,11 @@ void V4L2Grabber::open_device()
oss << "Cannot open '" << _deviceName << "'";
throw_errno_exception(oss.str());
}
// create the notifier for when a new frame is available
_streamNotifier = new QSocketNotifier(_fileDescriptor, QSocketNotifier::Read);
_streamNotifier->setEnabled(false);
connect(_streamNotifier, SIGNAL(activated(int)), this, SLOT(read_frame()));
}
void V4L2Grabber::close_device()
@ -178,6 +161,12 @@ void V4L2Grabber::close_device()
throw_errno_exception("close");
_fileDescriptor = -1;
if (_streamNotifier != nullptr)
{
delete _streamNotifier;
_streamNotifier = nullptr;
}
}
void V4L2Grabber::init_read(unsigned int buffer_size)
@ -359,7 +348,7 @@ void V4L2Grabber::init_device(VideoStandard videoStandard, int input)
// set the video standard if needed
switch (videoStandard)
{
case PAL:
case VIDEOSTANDARD_PAL:
{
v4l2_std_id std_id = V4L2_STD_PAL;
if (-1 == xioctl(VIDIOC_S_STD, &std_id))
@ -368,7 +357,7 @@ void V4L2Grabber::init_device(VideoStandard videoStandard, int input)
}
}
break;
case NTSC:
case VIDEOSTANDARD_NTSC:
{
v4l2_std_id std_id = V4L2_STD_NTSC;
if (-1 == xioctl(VIDIOC_S_STD, &std_id))
@ -377,7 +366,7 @@ void V4L2Grabber::init_device(VideoStandard videoStandard, int input)
}
}
break;
case NO_CHANGE:
case VIDEOSTANDARD_NO_CHANGE:
default:
// No change to device settings
break;
@ -393,17 +382,25 @@ void V4L2Grabber::init_device(VideoStandard videoStandard, int input)
throw_errno_exception("VIDIOC_G_FMT");
}
// check pixel format
switch (fmt.fmt.pix.pixelformat)
// set the requested pixel format
switch (_pixelFormat)
{
case V4L2_PIX_FMT_UYVY:
case V4L2_PIX_FMT_YUYV:
_pixelFormat = fmt.fmt.pix.pixelformat;
case PIXELFORMAT_UYVY:
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY;
break;
case PIXELFORMAT_YUYV:
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
break;
case PIXELFORMAT_RGB32:
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB32;
break;
case PIXELFORMAT_NO_CHANGE:
default:
throw_exception("Only pixel formats UYVY and YUYV are supported");
// No change to device settings
break;
}
// set the requested withd and height
if (_width > 0 || _height > 0)
{
if (_width > 0)
@ -415,19 +412,19 @@ void V4L2Grabber::init_device(VideoStandard videoStandard, int input)
{
fmt.fmt.pix.height = _height;
}
}
// set the settings
if (-1 == xioctl(VIDIOC_S_FMT, &fmt))
{
throw_errno_exception("VIDIOC_S_FMT");
}
// set the settings
if (-1 == xioctl(VIDIOC_S_FMT, &fmt))
{
throw_errno_exception("VIDIOC_S_FMT");
}
// get the format settings again
// (the size may not have been accepted without an error)
if (-1 == xioctl(VIDIOC_G_FMT, &fmt))
{
throw_errno_exception("VIDIOC_G_FMT");
}
// get the format settings again
// (the size may not have been accepted without an error)
if (-1 == xioctl(VIDIOC_G_FMT, &fmt))
{
throw_errno_exception("VIDIOC_G_FMT");
}
// store width & height
@ -437,6 +434,28 @@ void V4L2Grabber::init_device(VideoStandard videoStandard, int input)
// print the eventually used width and height
std::cout << "V4L2 width=" << _width << " height=" << _height << std::endl;
// check pixel format and frame size
switch (fmt.fmt.pix.pixelformat)
{
case V4L2_PIX_FMT_UYVY:
_pixelFormat = PIXELFORMAT_UYVY;
_frameByteSize = _width * _height * 2;
std::cout << "V4L2 pixel format=UYVY" << std::endl;
break;
case V4L2_PIX_FMT_YUYV:
_pixelFormat = PIXELFORMAT_YUYV;
_frameByteSize = _width * _height * 2;
std::cout << "V4L2 pixel format=YUYV" << std::endl;
break;
case V4L2_PIX_FMT_RGB32:
_pixelFormat = PIXELFORMAT_RGB32;
_frameByteSize = _width * _height * 4;
std::cout << "V4L2 pixel format=RGB32" << std::endl;
break;
default:
throw_exception("Only pixel formats UYVY, YUYV, and RGB32 are supported");
}
switch (_ioMethod) {
case IO_METHOD_READ:
init_read(fmt.fmt.pix.sizeimage);
@ -652,9 +671,9 @@ bool V4L2Grabber::process_image(const void *p, int size)
{
// We do want a new frame...
if (size != 2*_width*_height)
if (size != _frameByteSize)
{
std::cout << "Frame too small: " << size << " != " << (2*_width*_height) << std::endl;
std::cout << "Frame too small: " << size << " != " << _frameByteSize << std::endl;
}
else
{
@ -674,10 +693,10 @@ void V4L2Grabber::process_image(const uint8_t * data)
switch (_mode3D)
{
case MODE_3DSBS:
case VIDEO_3DSBS:
width = _width/2;
break;
case MODE_3DTAB:
case VIDEO_3DTAB:
height = _height/2;
break;
default:
@ -693,33 +712,79 @@ void V4L2Grabber::process_image(const uint8_t * data)
{
for (int xSource = _cropLeft + _horizontalPixelDecimation/2, xDest = 0; xSource < width - _cropRight; xSource += _horizontalPixelDecimation, ++xDest)
{
int index = (_width * ySource + xSource) * 2;
uint8_t y = 0;
uint8_t u = 0;
uint8_t v = 0;
ColorRgb & rgb = image(xDest, yDest);
switch (_pixelFormat)
{
case V4L2_PIX_FMT_UYVY:
y = data[index+1];
u = (xSource%2 == 0) ? data[index ] : data[index-2];
v = (xSource%2 == 0) ? data[index+2] : data[index ];
case PIXELFORMAT_UYVY:
{
int index = (_width * ySource + xSource) * 2;
uint8_t y = data[index+1];
uint8_t u = (xSource%2 == 0) ? data[index ] : data[index-2];
uint8_t v = (xSource%2 == 0) ? data[index+2] : data[index ];
yuv2rgb(y, u, v, rgb.red, rgb.green, rgb.blue);
}
break;
case V4L2_PIX_FMT_YUYV:
y = data[index];
u = (xSource%2 == 0) ? data[index+1] : data[index-1];
v = (xSource%2 == 0) ? data[index+3] : data[index+1];
case PIXELFORMAT_YUYV:
{
int index = (_width * ySource + xSource) * 2;
uint8_t y = data[index];
uint8_t u = (xSource%2 == 0) ? data[index+1] : data[index-1];
uint8_t v = (xSource%2 == 0) ? data[index+3] : data[index+1];
yuv2rgb(y, u, v, rgb.red, rgb.green, rgb.blue);
}
break;
case PIXELFORMAT_RGB32:
{
int index = (_width * ySource + xSource) * 4;
rgb.red = data[index ];
rgb.green = data[index+1];
rgb.blue = data[index+2];
}
break;
default:
// this should not be possible
break;
}
ColorRgb & rgb = image(xDest, yDest);
yuv2rgb(y, u, v, rgb.red, rgb.green, rgb.blue);
}
}
if (_callback != nullptr)
// check signal (only in center of the resulting image, because some grabbers have noise values along the borders)
bool noSignal = true;
for (unsigned x = 0; noSignal && x < (image.width()>>1); ++x)
{
(*_callback)(_callbackArg, image);
int xImage = (image.width()>>2) + x;
for (unsigned y = 0; noSignal && y < (image.height()>>1); ++y)
{
int yImage = (image.height()>>2) + y;
ColorRgb & rgb = image(xImage, yImage);
noSignal &= rgb <= _noSignalThresholdColor;
}
}
if (noSignal)
{
++_noSignalCounter;
}
else
{
if (_noSignalCounter >= _noSignalCounterThreshold)
{
std::cout << "V4L2 Grabber: " << "Signal detected" << std::endl;
}
_noSignalCounter = 0;
}
if (_noSignalCounter < _noSignalCounterThreshold)
{
emit newFrame(image);
}
else if (_noSignalCounter == _noSignalCounterThreshold)
{
std::cout << "V4L2 Grabber: " << "Signal lost" << std::endl;
}
}

View File

@ -0,0 +1,117 @@
#include <QMetaType>
#include <grabber/V4L2Wrapper.h>
#include <hyperion/ImageProcessorFactory.h>
V4L2Wrapper::V4L2Wrapper(const std::string &device,
int input,
VideoStandard videoStandard,
PixelFormat pixelFormat,
int width,
int height,
int frameDecimation,
int pixelDecimation,
double redSignalThreshold,
double greenSignalThreshold,
double blueSignalThreshold,
Hyperion *hyperion,
int hyperionPriority) :
_timeout_ms(1000),
_priority(hyperionPriority),
_grabber(device,
input,
videoStandard,
pixelFormat,
width,
height,
frameDecimation,
pixelDecimation,
pixelDecimation),
_processor(ImageProcessorFactory::getInstance().newImageProcessor()),
_hyperion(hyperion),
_ledColors(hyperion->getLedCount(), ColorRgb{0,0,0}),
_timer()
{
// set the signal detection threshold of the grabber
_grabber.setSignalThreshold(
redSignalThreshold,
greenSignalThreshold,
blueSignalThreshold,
50);
// register the image type
qRegisterMetaType<Image<ColorRgb>>("Image<ColorRgb>");
qRegisterMetaType<std::vector<ColorRgb>>("std::vector<ColorRgb>");
// Handle the image in the captured thread using a direct connection
QObject::connect(
&_grabber, SIGNAL(newFrame(Image<ColorRgb>)),
this, SLOT(newFrame(Image<ColorRgb>)),
Qt::DirectConnection);
// send color data to Hyperion using a queued connection to handle the data over to the main event loop
QObject::connect(
this, SIGNAL(emitColors(int,std::vector<ColorRgb>,int)),
_hyperion, SLOT(setColors(int,std::vector<ColorRgb>,int)),
Qt::QueuedConnection);
// setup the higher prio source checker
// this will disable the v4l2 grabber when a source with hisher priority is active
_timer.setInterval(500);
_timer.setSingleShot(false);
QObject::connect(&_timer, SIGNAL(timeout()), this, SLOT(checkSources()));
_timer.start();
}
V4L2Wrapper::~V4L2Wrapper()
{
delete _processor;
}
void V4L2Wrapper::start()
{
_grabber.start();
}
void V4L2Wrapper::stop()
{
_grabber.stop();
}
void V4L2Wrapper::setCropping(int cropLeft, int cropRight, int cropTop, int cropBottom)
{
_grabber.setCropping(cropLeft, cropRight, cropTop, cropBottom);
}
void V4L2Wrapper::set3D(VideoMode mode)
{
_grabber.set3D(mode);
}
void V4L2Wrapper::newFrame(const Image<ColorRgb> &image)
{
// process the new image
_processor->process(image, _ledColors);
// send colors to Hyperion
emit emitColors(_priority, _ledColors, _timeout_ms);
}
void V4L2Wrapper::checkSources()
{
QList<int> activePriorities = _hyperion->getActivePriorities();
for (int x : activePriorities)
{
if (x < _priority)
{
// found a higher priority source: grabber should be disabled
_grabber.stop();
return;
}
}
// no higher priority source was found: grabber should be enabled
_grabber.start();
}

View File

@ -43,6 +43,11 @@ void ImageProcessor::setSize(const unsigned width, const unsigned height)
_imageToLeds = new ImageToLedsMap(width, height, 0, 0, _ledString.leds());
}
void ImageProcessor::enableBalckBorderDetector(bool enable)
{
_enableBlackBorderRemoval = enable;
}
bool ImageProcessor::getScanParameters(size_t led, double &hscanBegin, double &hscanEnd, double &vscanBegin, double &vscanEnd) const
{
if (led < _ledString.leds().size())

View File

@ -1,4 +1,7 @@
// STL includes
#include <cmath>
// Hyperion includes
#include <hyperion/ImageProcessorFactory.h>
#include <hyperion/ImageProcessor.h>

View File

@ -1,4 +1,3 @@
// STL includes
#include <algorithm>
#include <cmath>
@ -33,17 +32,36 @@ ImageToLedsMap::ImageToLedsMap(
for (const Led& led : leds)
{
// skip leds without area
if ((led.maxX_frac-led.minX_frac) < 1e-6 || (led.maxY_frac-led.minY_frac) < 1e-6)
{
mColorsMap.emplace_back();
continue;
}
// Compute the index boundaries for this led
const unsigned minX_idx = xOffset + unsigned(std::round((actualWidth-1) * led.minX_frac));
const unsigned maxX_idx = xOffset + unsigned(std::round((actualWidth-1) * led.maxX_frac));
const unsigned minY_idx = yOffset + unsigned(std::round((actualHeight-1) * led.minY_frac));
const unsigned maxY_idx = yOffset + unsigned(std::round((actualHeight-1) * led.maxY_frac));
unsigned minX_idx = xOffset + unsigned(std::round(actualWidth * led.minX_frac));
unsigned maxX_idx = xOffset + unsigned(std::round(actualWidth * led.maxX_frac));
unsigned minY_idx = yOffset + unsigned(std::round(actualHeight * led.minY_frac));
unsigned maxY_idx = yOffset + unsigned(std::round(actualHeight * led.maxY_frac));
// make sure that the area is at least a single led large
minX_idx = std::min(minX_idx, xOffset + actualWidth - 1);
if (minX_idx == maxX_idx)
{
maxX_idx = minX_idx + 1;
}
minY_idx = std::min(minY_idx, yOffset + actualHeight - 1);
if (minY_idx == maxY_idx)
{
maxY_idx = minY_idx + 1;
}
// Add all the indices in the above defined rectangle to the indices for this led
std::vector<unsigned> ledColors;
for (unsigned y = minY_idx; y<=maxY_idx && y<height; ++y)
for (unsigned y = minY_idx; y<maxY_idx && y<(yOffset+actualHeight); ++y)
{
for (unsigned x = minX_idx; x<=maxX_idx && x<width; ++x)
for (unsigned x = minX_idx; x<maxX_idx && x<(xOffset+actualWidth); ++x)
{
ledColors.push_back(y*width + x);
}

View File

@ -37,9 +37,5 @@ add_library(jsonserver
target_link_libraries(jsonserver
hyperion
hyperion-utils
jsoncpp)
qt4_use_modules(jsonserver
Core
Gui
Network)
jsoncpp
${QT_LIBRARIES})

View File

@ -16,12 +16,12 @@
"required" : false
},
"saturationGain" : {
"type" : "double",
"type" : "number",
"required" : false,
"minimum" : 0.0
},
"valueGain" : {
"type" : "double",
"type" : "number",
"required" : false,
"minimum" : 0.0
},
@ -29,7 +29,7 @@
"type": "array",
"required": false,
"items" : {
"type": "double",
"type": "number",
"minimum": 0.0,
"maximum": 1.0
},
@ -40,7 +40,7 @@
"type": "array",
"required": false,
"items" : {
"type": "double",
"type": "number",
"minimum": 0.0
},
"minItems": 3,
@ -50,7 +50,7 @@
"type": "array",
"required": false,
"items" : {
"type": "double"
"type": "number"
},
"minItems": 3,
"maxItems": 3
@ -59,7 +59,7 @@
"type": "array",
"required": false,
"items" : {
"type": "double"
"type": "number"
},
"minItems": 3,
"maxItems": 3

43
libsrc/leddevice/CMakeLists.txt Normal file → Executable file
View File

@ -8,28 +8,28 @@ find_package(libusb-1.0 REQUIRED)
find_package(Threads REQUIRED)
include_directories(
../../include/hidapi
${LIBUSB_1_INCLUDE_DIRS}) # for Lightpack device
../../include/hidapi
${LIBUSB_1_INCLUDE_DIRS}) # for Lightpack device
# Group the headers that go through the MOC compiler
SET(Leddevice_QT_HEADERS
${CURRENT_SOURCE_DIR}/LedRs232Device.h
${CURRENT_SOURCE_DIR}/LedDeviceAdalight.h
${CURRENT_SOURCE_DIR}/LedDevicePhilipsHue.h
)
SET(Leddevice_HEADERS
${CURRENT_HEADER_DIR}/LedDevice.h
${CURRENT_HEADER_DIR}/LedDeviceFactory.h
${CURRENT_SOURCE_DIR}/LedRs232Device.h
${CURRENT_SOURCE_DIR}/LedDeviceLightpack.h
${CURRENT_SOURCE_DIR}/LedDeviceMultiLightpack.h
${CURRENT_SOURCE_DIR}/LedDevicePaintpack.h
${CURRENT_SOURCE_DIR}/LedDevicePiBlaster.h
${CURRENT_SOURCE_DIR}/LedDeviceSedu.h
${CURRENT_SOURCE_DIR}/LedDeviceTest.h
${CURRENT_SOURCE_DIR}/LedDeviceWs2812b.h
${CURRENT_SOURCE_DIR}/LedDeviceWs2811.h
${CURRENT_SOURCE_DIR}/LedDeviceHyperionUsbasp.h
${CURRENT_SOURCE_DIR}/LedDeviceTpm2.h
)
SET(Leddevice_SOURCES
@ -44,8 +44,9 @@ SET(Leddevice_SOURCES
${CURRENT_SOURCE_DIR}/LedDevicePiBlaster.cpp
${CURRENT_SOURCE_DIR}/LedDeviceSedu.cpp
${CURRENT_SOURCE_DIR}/LedDeviceTest.cpp
${CURRENT_SOURCE_DIR}/LedDeviceWs2811.cpp
${CURRENT_SOURCE_DIR}/LedDeviceWs2812b.cpp
${CURRENT_SOURCE_DIR}/LedDeviceHyperionUsbasp.cpp
${CURRENT_SOURCE_DIR}/LedDevicePhilipsHue.cpp
${CURRENT_SOURCE_DIR}/LedDeviceTpm2.cpp
)
if(ENABLE_SPIDEV)
@ -67,6 +68,17 @@ if(ENABLE_SPIDEV)
)
endif(ENABLE_SPIDEV)
if(ENABLE_TINKERFORGE)
SET(Leddevice_HEADERS
${Leddevice_HEADERS}
${CURRENT_SOURCE_DIR}/LedDeviceTinkerforge.h
)
SET(Leddevice_SOURCES
${Leddevice_SOURCES}
${CURRENT_SOURCE_DIR}/LedDeviceTinkerforge.cpp
)
endif(ENABLE_TINKERFORGE)
QT4_WRAP_CPP(Leddevice_HEADERS_MOC ${Leddevice_QT_HEADERS})
@ -78,12 +90,17 @@ add_library(leddevice
)
target_link_libraries(leddevice
hyperion-utils
serialport
${LIBUSB_1_LIBRARIES} #apt-get install libusb-1.0-0-dev
${CMAKE_THREAD_LIBS_INIT}
${QT_LIBRARIES}
hyperion-utils
serialport
${LIBUSB_1_LIBRARIES} #apt-get install libusb-1.0-0-dev
${CMAKE_THREAD_LIBS_INIT}
${QT_LIBRARIES}
)
if(ENABLE_TINKERFORGE)
target_link_libraries(leddevice tinkerforge)
endif()
if(APPLE)
target_link_libraries(leddevice hidapi-mac)
else()

View File

@ -11,8 +11,8 @@
// hyperion local includes
#include "LedDeviceAdalight.h"
LedDeviceAdalight::LedDeviceAdalight(const std::string& outputDevice, const unsigned baudrate) :
LedRs232Device(outputDevice, baudrate),
LedDeviceAdalight::LedDeviceAdalight(const std::string& outputDevice, const unsigned baudrate, int delayAfterConnect_ms) :
LedRs232Device(outputDevice, baudrate, delayAfterConnect_ms),
_ledBuffer(0),
_timer()
{

View File

@ -12,7 +12,7 @@
///
/// Implementation of the LedDevice interface for writing to an Adalight led device.
///
class LedDeviceAdalight : public QObject, public LedRs232Device
class LedDeviceAdalight : public LedRs232Device
{
Q_OBJECT
@ -23,7 +23,7 @@ public:
/// @param outputDevice The name of the output device (eg '/dev/ttyS0')
/// @param baudrate The used baudrate for writing to the output device
///
LedDeviceAdalight(const std::string& outputDevice, const unsigned baudrate);
LedDeviceAdalight(const std::string& outputDevice, const unsigned baudrate, int delayAfterConnect_ms);
///
/// Writes the led color values to the led-device

70
libsrc/leddevice/LedDeviceFactory.cpp Normal file → Executable file
View File

@ -1,3 +1,6 @@
// Stl includes
#include <string>
#include <algorithm>
// Build configuration
#include <HyperionConfig.h>
@ -13,6 +16,10 @@
#include "LedDeviceWs2801.h"
#endif
#ifdef ENABLE_TINKERFORGE
#include "LedDeviceTinkerforge.h"
#endif
#include "LedDeviceAdalight.h"
#include "LedDeviceLightpack.h"
#include "LedDeviceMultiLightpack.h"
@ -20,8 +27,9 @@
#include "LedDevicePiBlaster.h"
#include "LedDeviceSedu.h"
#include "LedDeviceTest.h"
#include "LedDeviceWs2811.h"
#include "LedDeviceWs2812b.h"
#include "LedDeviceHyperionUsbasp.h"
#include "LedDevicePhilipsHue.h"
#include "LedDeviceTpm2.h"
LedDevice * LedDeviceFactory::construct(const Json::Value & deviceConfig)
{
@ -36,8 +44,9 @@ LedDevice * LedDeviceFactory::construct(const Json::Value & deviceConfig)
{
const std::string output = deviceConfig["output"].asString();
const unsigned rate = deviceConfig["rate"].asInt();
const int delay_ms = deviceConfig["delayAfterConnect"].asInt();
LedDeviceAdalight* deviceAdalight = new LedDeviceAdalight(output, rate);
LedDeviceAdalight* deviceAdalight = new LedDeviceAdalight(output, rate, delay_ms);
deviceAdalight->open();
device = deviceAdalight;
@ -84,23 +93,20 @@ LedDevice * LedDeviceFactory::construct(const Json::Value & deviceConfig)
device = deviceWs2801;
}
#endif
// else if (type == "ws2811")
// {
// const std::string output = deviceConfig["output"].asString();
// const std::string outputSpeed = deviceConfig["output"].asString();
// const std::string timingOption = deviceConfig["timingOption"].asString();
#ifdef ENABLE_TINKERFORGE
else if (type=="tinkerforge")
{
const std::string host = deviceConfig.get("output", "127.0.0.1").asString();
const uint16_t port = deviceConfig.get("port", 4223).asInt();
const std::string uid = deviceConfig["uid"].asString();
const unsigned rate = deviceConfig["rate"].asInt();
// ws2811::SpeedMode speedMode = (outputSpeed == "high")? ws2811::highspeed : ws2811::lowspeed;
// if (outputSpeed != "high" && outputSpeed != "low")
// {
// std::cerr << "Incorrect speed-mode selected for WS2811: " << outputSpeed << " != {'high', 'low'}" << std::endl;
// }
LedDeviceTinkerforge* deviceTinkerforge = new LedDeviceTinkerforge(host, port, uid, rate);
deviceTinkerforge->open();
// LedDeviceWs2811 * deviceWs2811 = new LedDeviceWs2811(output, ws2811::fromString(timingOption, ws2811::option_2855), speedMode);
// deviceWs2811->open();
// device = deviceWs2811;
// }
device = deviceTinkerforge;
}
#endif
else if (type == "lightpack")
{
const std::string output = deviceConfig.get("output", "").asString();
@ -144,17 +150,37 @@ LedDevice * LedDeviceFactory::construct(const Json::Value & deviceConfig)
device = deviceSedu;
}
else if (type == "hyperion-usbasp-ws2801")
{
LedDeviceHyperionUsbasp * deviceHyperionUsbasp = new LedDeviceHyperionUsbasp(LedDeviceHyperionUsbasp::CMD_WRITE_WS2801);
deviceHyperionUsbasp->open();
device = deviceHyperionUsbasp;
}
else if (type == "hyperion-usbasp-ws2812")
{
LedDeviceHyperionUsbasp * deviceHyperionUsbasp = new LedDeviceHyperionUsbasp(LedDeviceHyperionUsbasp::CMD_WRITE_WS2812);
deviceHyperionUsbasp->open();
device = deviceHyperionUsbasp;
}
else if (type == "philipshue")
{
const std::string output = deviceConfig["output"].asString();
const bool switchOffOnBlack = deviceConfig.get("switchOffOnBlack", true).asBool();
device = new LedDevicePhilipsHue(output, switchOffOnBlack);
}
else if (type == "test")
{
const std::string output = deviceConfig["output"].asString();
device = new LedDeviceTest(output);
}
else if (type == "ws2812b")
else if (type == "tpm2")
{
LedDeviceWs2812b * deviceWs2812b = new LedDeviceWs2812b();
deviceWs2812b->open();
const std::string output = deviceConfig["output"].asString();
const unsigned rate = deviceConfig["rate"].asInt();
device = deviceWs2812b;
LedDeviceTpm2* deviceTpm2 = new LedDeviceTpm2(output, rate);
deviceTpm2->open();
device = deviceTpm2;
}
else
{

View File

@ -0,0 +1,205 @@
// stl includes
#include <exception>
#include <cstring>
// Local Hyperion includes
#include "LedDeviceHyperionUsbasp.h"
// Static constants which define the Hyperion Usbasp device
uint16_t LedDeviceHyperionUsbasp::_usbVendorId = 0x16c0;
uint16_t LedDeviceHyperionUsbasp::_usbProductId = 0x05dc;
std::string LedDeviceHyperionUsbasp::_usbProductDescription = "Hyperion led controller";
LedDeviceHyperionUsbasp::LedDeviceHyperionUsbasp(uint8_t writeLedsCommand) :
LedDevice(),
_writeLedsCommand(writeLedsCommand),
_libusbContext(nullptr),
_deviceHandle(nullptr),
_ledCount(256)
{
}
LedDeviceHyperionUsbasp::~LedDeviceHyperionUsbasp()
{
if (_deviceHandle != nullptr)
{
libusb_release_interface(_deviceHandle, 0);
libusb_attach_kernel_driver(_deviceHandle, 0);
libusb_close(_deviceHandle);
_deviceHandle = nullptr;
}
if (_libusbContext != nullptr)
{
libusb_exit(_libusbContext);
_libusbContext = nullptr;
}
}
int LedDeviceHyperionUsbasp::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;
}
//libusb_set_debug(_libusbContext, 3);
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)
{
// try to open and initialize the device
error = testAndOpen(deviceList[i]);
if (error == 0)
{
// a device was sucessfully opened. break from list
break;
}
}
// free the device list
libusb_free_device_list(deviceList, 1);
if (_deviceHandle == nullptr)
{
std::cerr << "No " << _usbProductDescription << " has been found" << std::endl;
}
return _deviceHandle == nullptr ? -1 : 0;
}
int LedDeviceHyperionUsbasp::testAndOpen(libusb_device * device)
{
libusb_device_descriptor deviceDescriptor;
int error = libusb_get_device_descriptor(device, &deviceDescriptor);
if (error != LIBUSB_SUCCESS)
{
std::cerr << "Error while retrieving device descriptor(" << error << "): " << libusb_error_name(error) << std::endl;
return -1;
}
if (deviceDescriptor.idVendor == _usbVendorId &&
deviceDescriptor.idProduct == _usbProductId &&
deviceDescriptor.iProduct != 0 &&
getString(device, deviceDescriptor.iProduct) == _usbProductDescription)
{
// get the hardware address
int busNumber = libusb_get_bus_number(device);
int addressNumber = libusb_get_device_address(device);
std::cout << _usbProductDescription << " found: bus=" << busNumber << " address=" << addressNumber << std::endl;
try
{
_deviceHandle = openDevice(device);
std::cout << _usbProductDescription << " successfully opened" << std::endl;
return 0;
}
catch(int e)
{
_deviceHandle = nullptr;
std::cerr << "Unable to open " << _usbProductDescription << ". Searching for other device(" << e << "): " << libusb_error_name(e) << std::endl;
}
}
return -1;
}
int LedDeviceHyperionUsbasp::write(const std::vector<ColorRgb> &ledValues)
{
_ledCount = ledValues.size();
int nbytes = libusb_control_transfer(
_deviceHandle, // device handle
LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_OUT, // request type
_writeLedsCommand, // request
0, // value
0, // index
(uint8_t *) ledValues.data(), // data
(3*_ledCount) & 0xffff, // length
5000); // timeout
// Disabling interupts for a little while on the device results in a PIPE error. All seems to keep functioning though...
if(nbytes < 0 && nbytes != LIBUSB_ERROR_PIPE)
{
std::cerr << "Error while writing data to " << _usbProductDescription << " (" << libusb_error_name(nbytes) << ")" << std::endl;
return -1;
}
return 0;
}
int LedDeviceHyperionUsbasp::switchOff()
{
std::vector<ColorRgb> ledValues(_ledCount, ColorRgb::BLACK);
return write(ledValues);
}
libusb_device_handle * LedDeviceHyperionUsbasp::openDevice(libusb_device *device)
{
libusb_device_handle * handle = nullptr;
int error = libusb_open(device, &handle);
if (error != LIBUSB_SUCCESS)
{
std::cerr << "unable to open device(" << error << "): " << libusb_error_name(error) << std::endl;
throw error;
}
// detach kernel driver if it is active
if (libusb_kernel_driver_active(handle, 0) == 1)
{
error = libusb_detach_kernel_driver(handle, 0);
if (error != LIBUSB_SUCCESS)
{
std::cerr << "unable to detach kernel driver(" << error << "): " << libusb_error_name(error) << std::endl;
libusb_close(handle);
throw error;
}
}
error = libusb_claim_interface(handle, 0);
if (error != LIBUSB_SUCCESS)
{
std::cerr << "unable to claim interface(" << error << "): " << libusb_error_name(error) << std::endl;
libusb_attach_kernel_driver(handle, 0);
libusb_close(handle);
throw error;
}
return handle;
}
std::string LedDeviceHyperionUsbasp::getString(libusb_device * device, int stringDescriptorIndex)
{
libusb_device_handle * handle = nullptr;
int error = libusb_open(device, &handle);
if (error != LIBUSB_SUCCESS)
{
throw error;
}
char buffer[256];
error = libusb_get_string_descriptor_ascii(handle, stringDescriptorIndex, reinterpret_cast<unsigned char *>(buffer), sizeof(buffer));
if (error <= 0)
{
libusb_close(handle);
throw error;
}
libusb_close(handle);
return std::string(buffer, error);
}

View File

@ -0,0 +1,88 @@
#pragma once
// stl includes
#include <vector>
#include <cstdint>
#include <string>
// libusb include
#include <libusb.h>
// Hyperion includes
#include <leddevice/LedDevice.h>
///
/// LedDevice implementation for a lightpack device (http://code.google.com/p/light-pack/)
///
class LedDeviceHyperionUsbasp : public LedDevice
{
public:
// Commands to the Device
enum Commands {
CMD_WRITE_WS2801 = 10,
CMD_WRITE_WS2812 = 11
};
///
/// Constructs the LedDeviceLightpack
///
LedDeviceHyperionUsbasp(uint8_t writeLedsCommand);
///
/// Destructor of the LedDevice; closes the output device if it is open
///
virtual ~LedDeviceHyperionUsbasp();
///
/// Opens and configures the output device
///
/// @return Zero on succes else negative
///
int open();
///
/// 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);
///
/// Switch the leds off
///
/// @return Zero on success else negative
///
virtual int switchOff();
private:
///
/// Test if the device is a Hyperion Usbasp device
///
/// @return Zero on succes else negative
///
int testAndOpen(libusb_device * device);
static libusb_device_handle * openDevice(libusb_device * device);
static std::string getString(libusb_device * device, int stringDescriptorIndex);
private:
/// command to write the leds
const uint8_t _writeLedsCommand;
/// libusb context
libusb_context * _libusbContext;
/// libusb device handle
libusb_device_handle * _deviceHandle;
/// Number of leds
int _ledCount;
/// Usb device identifiers
static uint16_t _usbVendorId;
static uint16_t _usbProductId;
static std::string _usbProductDescription;
};

View File

@ -19,11 +19,12 @@ LedDeviceLpd6803::LedDeviceLpd6803(const std::string& outputDevice, const unsign
int LedDeviceLpd6803::write(const std::vector<ColorRgb> &ledValues)
{
unsigned messageLength = 4 + 2*ledValues.size() + ledValues.size()/8 + 1;
// Reconfigure if the current connfiguration does not match the required configuration
if (4 + 2*ledValues.size() != _ledBuffer.size())
if (messageLength != _ledBuffer.size())
{
// Initialise the buffer
_ledBuffer.resize(4 + 2*ledValues.size(), 0x00);
_ledBuffer.resize(messageLength, 0x00);
}
// Copy the colors from the ColorRgb vector to the Ldp6803 data vector

View File

@ -0,0 +1,291 @@
// Local-Hyperion includes
#include "LedDevicePhilipsHue.h"
// jsoncpp includes
#include <json/json.h>
// qt includes
#include <QtCore/qmath.h>
#include <QUrl>
#include <QHttpRequestHeader>
#include <QEventLoop>
#include <set>
bool operator ==(CiColor p1, CiColor p2) {
return (p1.x == p2.x) && (p1.y == p2.y) && (p1.bri == p2.bri);
}
bool operator !=(CiColor p1, CiColor p2) {
return !(p1 == p2);
}
PhilipsHueLamp::PhilipsHueLamp(unsigned int id, QString originalState, QString modelId) :
id(id), originalState(originalState) {
// Hue system model ids.
const std::set<QString> HUE_BULBS_MODEL_IDS = { "LCT001", "LCT002", "LCT003" };
const std::set<QString> LIVING_COLORS_MODEL_IDS = { "LLC001", "LLC005", "LLC006", "LLC007", "LLC011", "LLC012",
"LLC013", "LST001" };
// Find id in the sets and set the appropiate color space.
if (HUE_BULBS_MODEL_IDS.find(modelId) != HUE_BULBS_MODEL_IDS.end()) {
colorSpace.red = {0.675f, 0.322f};
colorSpace.green = {0.4091f, 0.518f};
colorSpace.blue = {0.167f, 0.04f};
} else if (LIVING_COLORS_MODEL_IDS.find(modelId) != LIVING_COLORS_MODEL_IDS.end()) {
colorSpace.red = {0.703f, 0.296f};
colorSpace.green = {0.214f, 0.709f};
colorSpace.blue = {0.139f, 0.081f};
} else {
colorSpace.red = {1.0f, 0.0f};
colorSpace.green = {0.0f, 1.0f};
colorSpace.blue = {0.0f, 0.0f};
}
// Initialize black color.
black = rgbToCiColor(0.0f, 0.0f, 0.0f);
// Initialize color with black
color = {black.x, black.y, black.bri};
}
float PhilipsHueLamp::crossProduct(CiColor p1, CiColor p2) {
return p1.x * p2.y - p1.y * p2.x;
}
bool PhilipsHueLamp::isPointInLampsReach(CiColor p) {
CiColor v1 = { colorSpace.green.x - colorSpace.red.x, colorSpace.green.y - colorSpace.red.y };
CiColor v2 = { colorSpace.blue.x - colorSpace.red.x, colorSpace.blue.y - colorSpace.red.y };
CiColor q = { p.x - colorSpace.red.x, p.y - colorSpace.red.y };
float s = crossProduct(q, v2) / crossProduct(v1, v2);
float t = crossProduct(v1, q) / crossProduct(v1, v2);
if ((s >= 0.0f) && (t >= 0.0f) && (s + t <= 1.0f)) {
return true;
}
return false;
}
CiColor PhilipsHueLamp::getClosestPointToPoint(CiColor a, CiColor b, CiColor p) {
CiColor AP = { p.x - a.x, p.y - a.y };
CiColor AB = { b.x - a.x, b.y - a.y };
float ab2 = AB.x * AB.x + AB.y * AB.y;
float ap_ab = AP.x * AB.x + AP.y * AB.y;
float t = ap_ab / ab2;
if (t < 0.0f) {
t = 0.0f;
} else if (t > 1.0f) {
t = 1.0f;
}
return {a.x + AB.x * t, a.y + AB.y * t};
}
float PhilipsHueLamp::getDistanceBetweenTwoPoints(CiColor p1, CiColor p2) {
// Horizontal difference.
float dx = p1.x - p2.x;
// Vertical difference.
float dy = p1.y - p2.y;
// Absolute value.
return sqrt(dx * dx + dy * dy);
}
CiColor PhilipsHueLamp::rgbToCiColor(float red, float green, float blue) {
// Apply gamma correction.
float r = (red > 0.04045f) ? powf((red + 0.055f) / (1.0f + 0.055f), 2.4f) : (red / 12.92f);
float g = (green > 0.04045f) ? powf((green + 0.055f) / (1.0f + 0.055f), 2.4f) : (green / 12.92f);
float b = (blue > 0.04045f) ? powf((blue + 0.055f) / (1.0f + 0.055f), 2.4f) : (blue / 12.92f);
// Convert to XYZ space.
float X = r * 0.649926f + g * 0.103455f + b * 0.197109f;
float Y = r * 0.234327f + g * 0.743075f + b * 0.022598f;
float Z = r * 0.0000000f + g * 0.053077f + b * 1.035763f;
// Convert to x,y space.
float cx = X / (X + Y + Z);
float cy = Y / (X + Y + Z);
if (isnan(cx)) {
cx = 0.0f;
}
if (isnan(cy)) {
cy = 0.0f;
}
// Brightness is simply Y in the XYZ space.
CiColor xy = { cx, cy, Y };
// Check if the given XY value is within the color reach of our lamps.
if (!isPointInLampsReach(xy)) {
// It seems the color is out of reach let's find the closes color we can produce with our lamp and send this XY value out.
CiColor pAB = getClosestPointToPoint(colorSpace.red, colorSpace.green, xy);
CiColor pAC = getClosestPointToPoint(colorSpace.blue, colorSpace.red, xy);
CiColor pBC = getClosestPointToPoint(colorSpace.green, colorSpace.blue, xy);
// Get the distances per point and see which point is closer to our Point.
float dAB = getDistanceBetweenTwoPoints(xy, pAB);
float dAC = getDistanceBetweenTwoPoints(xy, pAC);
float dBC = getDistanceBetweenTwoPoints(xy, pBC);
float lowest = dAB;
CiColor closestPoint = pAB;
if (dAC < lowest) {
lowest = dAC;
closestPoint = pAC;
}
if (dBC < lowest) {
lowest = dBC;
closestPoint = pBC;
}
// Change the xy value to a value which is within the reach of the lamp.
xy.x = closestPoint.x;
xy.y = closestPoint.y;
}
return xy;
}
LedDevicePhilipsHue::LedDevicePhilipsHue(const std::string& output, bool switchOffOnBlack) :
host(output.c_str()), username("newdeveloper"), switchOffOnBlack(switchOffOnBlack) {
http = new QHttp(host);
timer.setInterval(3000);
timer.setSingleShot(true);
connect(&timer, SIGNAL(timeout()), this, SLOT(restoreStates()));
}
LedDevicePhilipsHue::~LedDevicePhilipsHue() {
delete http;
}
int LedDevicePhilipsHue::write(const std::vector<ColorRgb> & ledValues) {
// Save light states if not done before.
if (!areStatesSaved()) {
saveStates((unsigned int) ledValues.size());
switchOn((unsigned int) ledValues.size());
}
// If there are less states saved than colors given, then maybe something went wrong before.
if (lamps.size() != ledValues.size()) {
restoreStates();
return 0;
}
// Iterate through colors and set light states.
unsigned int idx = 0;
for (const ColorRgb& color : ledValues) {
// Get lamp.
PhilipsHueLamp& lamp = lamps.at(idx);
// Scale colors from [0, 255] to [0, 1] and convert to xy space.
CiColor xy = lamp.rgbToCiColor(color.red / 255.0f, color.green / 255.0f, color.blue / 255.0f);
// Write color if color has been changed.
if (xy != lamp.color) {
// Switch on if the lamp has been previously switched off.
if (switchOffOnBlack && lamp.color == lamp.black) {
}
// Send adjust color and brightness command in JSON format.
put(getStateRoute(lamp.id),
QString("{\"xy\": [%1, %2], \"bri\": %3}").arg(xy.x).arg(xy.y).arg(qRound(xy.bri * 255.0f)));
}
// Switch lamp off if switchOffOnBlack is enabled and the lamp is currently on.
if (switchOffOnBlack) {
// From black to a color.
if (lamp.color == lamp.black && xy != lamp.black) {
put(getStateRoute(lamp.id), QString("{\"on\": true}"));
}
// From a color to black.
else if (lamp.color != lamp.black && xy == lamp.black) {
put(getStateRoute(lamp.id), QString("{\"on\": false}"));
}
}
// Remember last color.
lamp.color = xy;
// Next light id.
idx++;
}
timer.start();
return 0;
}
int LedDevicePhilipsHue::switchOff() {
timer.stop();
// If light states have been saved before, ...
if (areStatesSaved()) {
// ... restore them.
restoreStates();
}
return 0;
}
void LedDevicePhilipsHue::put(QString route, QString content) {
QString url = QString("/api/%1/%2").arg(username).arg(route);
QHttpRequestHeader header("PUT", url);
header.setValue("Host", host);
header.setValue("Accept-Encoding", "identity");
header.setValue("Connection", "keep-alive");
header.setValue("Content-Length", QString("%1").arg(content.size()));
QEventLoop loop;
// Connect requestFinished signal to quit slot of the loop.
loop.connect(http, SIGNAL(requestFinished(int, bool)), SLOT(quit()));
// Perfrom request
http->request(header, content.toAscii());
// Go into the loop until the request is finished.
loop.exec();
}
QByteArray LedDevicePhilipsHue::get(QString route) {
QString url = QString("/api/%1/%2").arg(username).arg(route);
// Event loop to block until request finished.
QEventLoop loop;
// Connect requestFinished signal to quit slot of the loop.
loop.connect(http, SIGNAL(requestFinished(int, bool)), SLOT(quit()));
// Perfrom request
http->get(url);
// Go into the loop until the request is finished.
loop.exec();
// Read all data of the response.
return http->readAll();
}
QString LedDevicePhilipsHue::getStateRoute(unsigned int lightId) {
return QString("lights/%1/state").arg(lightId);
}
QString LedDevicePhilipsHue::getRoute(unsigned int lightId) {
return QString("lights/%1").arg(lightId);
}
void LedDevicePhilipsHue::saveStates(unsigned int nLights) {
// Clear saved lamps.
lamps.clear();
// Use json parser to parse reponse.
Json::Reader reader;
Json::FastWriter writer;
// Iterate lights.
for (unsigned int i = 0; i < nLights; i++) {
// Read the response.
QByteArray response = get(getRoute(i + 1));
// Parse JSON.
Json::Value json;
if (!reader.parse(QString(response).toStdString(), json)) {
// Error occured, break loop.
break;
}
// Get state object values which are subject to change.
Json::Value state(Json::objectValue);
state["on"] = json["state"]["on"];
if (json["state"]["on"] == true) {
state["xy"] = json["state"]["xy"];
state["bri"] = json["state"]["bri"];
}
// Determine the model id.
QString modelId = QString(writer.write(json["modelid"]).c_str()).trimmed().replace("\"", "");
QString originalState = QString(writer.write(state).c_str()).trimmed();
// Save state object.
lamps.push_back(PhilipsHueLamp(i + 1, originalState, modelId));
}
}
void LedDevicePhilipsHue::switchOn(unsigned int nLights) {
for (PhilipsHueLamp lamp : lamps) {
put(getStateRoute(lamp.id), "{\"on\": true}");
}
}
void LedDevicePhilipsHue::restoreStates() {
for (PhilipsHueLamp lamp : lamps) {
put(getStateRoute(lamp.id), lamp.originalState);
}
// Clear saved light states.
lamps.clear();
}
bool LedDevicePhilipsHue::areStatesSaved() {
return !lamps.empty();
}

View File

@ -0,0 +1,218 @@
#pragma once
// STL includes
#include <string>
// Qt includes
#include <QObject>
#include <QString>
#include <QHttp>
#include <QTimer>
// Leddevice includes
#include <leddevice/LedDevice.h>
/**
* A color point in the color space of the hue system.
*/
struct CiColor {
/// X component.
float x;
/// Y component.
float y;
/// The brightness.
float bri;
};
bool operator==(CiColor p1, CiColor p2);
bool operator!=(CiColor p1, CiColor p2);
/**
* Color triangle to define an available color space for the hue lamps.
*/
struct CiColorTriangle {
CiColor red, green, blue;
};
/**
* Simple class to hold the id, the latest color, the color space and the original state.
*/
class PhilipsHueLamp {
public:
unsigned int id;
CiColor black;
CiColor color;
CiColorTriangle colorSpace;
QString originalState;
///
/// Constructs the lamp.
///
/// @param id the light id
///
/// @param originalState the json string of the original state
///
/// @param modelId the model id of the hue lamp which is used to determine the color space
///
PhilipsHueLamp(unsigned int id, QString originalState, QString modelId);
///
/// Converts an RGB color to the Hue xy color space and brightness.
/// https://github.com/PhilipsHue/PhilipsHueSDK-iOS-OSX/blob/master/ApplicationDesignNotes/RGB%20to%20xy%20Color%20conversion.md
///
/// @param red the red component in [0, 1]
///
/// @param green the green component in [0, 1]
///
/// @param blue the blue component in [0, 1]
///
/// @return color point
///
CiColor rgbToCiColor(float red, float green, float blue);
///
/// @param p the color point to check
///
/// @return true if the color point is covered by the lamp color space
///
bool isPointInLampsReach(CiColor p);
///
/// @param p1 point one
///
/// @param p2 point tow
///
/// @return the cross product between p1 and p2
///
float crossProduct(CiColor p1, CiColor p2);
///
/// @param a reference point one
///
/// @param b reference point two
///
/// @param p the point to which the closest point is to be found
///
/// @return the closest color point of p to a and b
///
CiColor getClosestPointToPoint(CiColor a, CiColor b, CiColor p);
///
/// @param p1 point one
///
/// @param p2 point tow
///
/// @return the distance between the two points
///
float getDistanceBetweenTwoPoints(CiColor p1, CiColor p2);
};
/**
* Implementation for the Philips Hue system.
*
* To use set the device to "philipshue".
* Uses the official Philips Hue API (http://developers.meethue.com).
* Framegrabber must be limited to 10 Hz / numer of lights to avoid rate limitation by the hue bridge.
* Create a new API user name "newdeveloper" on the bridge (http://developers.meethue.com/gettingstarted.html)
*
* @author ntim (github), bimsarck (github)
*/
class LedDevicePhilipsHue: public QObject, public LedDevice {
Q_OBJECT
public:
///
/// Constructs the device.
///
/// @param output the ip address of the bridge
///
/// @param switchOffOnBlack kill lights for black
///
LedDevicePhilipsHue(const std::string& output, bool switchOffOnBlack);
///
/// Destructor of this device
///
virtual ~LedDevicePhilipsHue();
///
/// Sends the given led-color values via put request to the hue system
///
/// @param ledValues The color-value per led
///
/// @return Zero on success else negative
///
virtual int write(const std::vector<ColorRgb> & ledValues);
/// Restores the original state of the leds.
virtual int switchOff();
private slots:
/// Restores the status of all lights.
void restoreStates();
private:
/// Array to save the lamps.
std::vector<PhilipsHueLamp> lamps;
/// Ip address of the bridge
QString host;
/// User name for the API ("newdeveloper")
QString username;
/// Qhttp object for sending requests.
QHttp* http;
/// Use timer to reset lights when we got into "GRABBINGMODE_OFF".
QTimer timer;
///
bool switchOffOnBlack;
///
/// Sends a HTTP GET request (blocking).
///
/// @param route the URI of the request
///
/// @return response of the request
///
QByteArray get(QString route);
///
/// Sends a HTTP PUT request (non-blocking).
///
/// @param route the URI of the request
///
/// @param content content of the request
///
void put(QString route, QString content);
///
/// @param lightId the id of the hue light (starting from 1)
///
/// @return the URI of the light state for PUT requests.
///
QString getStateRoute(unsigned int lightId);
///
/// @param lightId the id of the hue light (starting from 1)
///
/// @return the URI of the light for GET requests.
///
QString getRoute(unsigned int lightId);
///
/// Queries the status of all lights and saves it.
///
/// @param nLights the number of lights
///
void saveStates(unsigned int nLights);
///
/// Switches the leds on.
///
/// @param nLights the number of lights
///
void switchOn(unsigned int nLights);
///
/// @return true if light states have been saved.
///
bool areStatesSaved();
};

View File

@ -0,0 +1,143 @@
// STL includes
#include <cerrno>
#include <cstring>
// Local LedDevice includes
#include "LedDeviceTinkerforge.h"
static const unsigned MAX_NUM_LEDS = 320;
static const unsigned MAX_NUM_LEDS_SETTABLE = 16;
LedDeviceTinkerforge::LedDeviceTinkerforge(const std::string & host, uint16_t port, const std::string & uid, const unsigned interval) :
LedDevice(),
_host(host),
_port(port),
_uid(uid),
_interval(interval),
_ipConnection(nullptr),
_ledStrip(nullptr),
_colorChannelSize(0)
{
// empty
}
LedDeviceTinkerforge::~LedDeviceTinkerforge()
{
// Close the device (if it is opened)
if (_ipConnection != nullptr && _ledStrip != nullptr)
{
switchOff();
}
// Clean up claimed resources
delete _ipConnection;
delete _ledStrip;
}
int LedDeviceTinkerforge::open()
{
// Check if connection is already createds
if (_ipConnection != nullptr)
{
std::cout << "Attempt to open existing connection; close before opening" << std::endl;
return -1;
}
// Initialise a new connection
_ipConnection = new IPConnection;
ipcon_create(_ipConnection);
int connectionStatus = ipcon_connect(_ipConnection, _host.c_str(), _port);
if (connectionStatus < 0)
{
std::cerr << "Attempt to connect to master brick (" << _host << ":" << _port << ") failed with status " << connectionStatus << std::endl;
return -1;
}
// Create the 'LedStrip'
_ledStrip = new LEDStrip;
led_strip_create(_ledStrip, _uid.c_str(), _ipConnection);
int frameStatus = led_strip_set_frame_duration(_ledStrip, _interval);
if (frameStatus < 0)
{
std::cerr << "Attempt to connect to led strip bricklet (led_strip_set_frame_duration()) failed with status " << frameStatus << std::endl;
return -1;
}
return 0;
}
int LedDeviceTinkerforge::write(const std::vector<ColorRgb> &ledValues)
{
unsigned nrLedValues = ledValues.size();
if (nrLedValues > MAX_NUM_LEDS)
{
std::cerr << "Invalid attempt to write led values. Not more than " << MAX_NUM_LEDS << " leds are allowed." << std::endl;
return -1;
}
if (_colorChannelSize < nrLedValues)
{
_redChannel.resize(nrLedValues, uint8_t(0));
_greenChannel.resize(nrLedValues, uint8_t(0));
_blueChannel.resize(nrLedValues, uint8_t(0));
}
_colorChannelSize = nrLedValues;
auto redIt = _redChannel.begin();
auto greenIt = _greenChannel.begin();
auto blueIt = _blueChannel.begin();
for (const ColorRgb &ledValue : ledValues)
{
*redIt = ledValue.red;
++redIt;
*greenIt = ledValue.green;
++greenIt;
*blueIt = ledValue.blue;
++blueIt;
}
return transferLedData(_ledStrip, 0, _colorChannelSize, _redChannel.data(), _greenChannel.data(), _blueChannel.data());
}
int LedDeviceTinkerforge::switchOff()
{
std::fill(_redChannel.begin(), _redChannel.end(), 0);
std::fill(_greenChannel.begin(), _greenChannel.end(), 0);
std::fill(_blueChannel.begin(), _blueChannel.end(), 0);
return transferLedData(_ledStrip, 0, _colorChannelSize, _redChannel.data(), _greenChannel.data(), _blueChannel.data());
}
int LedDeviceTinkerforge::transferLedData(LEDStrip *ledStrip, unsigned index, unsigned length, uint8_t *redChannel, uint8_t *greenChannel, uint8_t *blueChannel)
{
if (length == 0 || index >= length || length > MAX_NUM_LEDS)
{
return E_INVALID_PARAMETER;
}
uint8_t reds[MAX_NUM_LEDS_SETTABLE];
uint8_t greens[MAX_NUM_LEDS_SETTABLE];
uint8_t blues[MAX_NUM_LEDS_SETTABLE];
for (unsigned i=index; i<length; i+=MAX_NUM_LEDS_SETTABLE)
{
const unsigned copyLength = (i + MAX_NUM_LEDS_SETTABLE > length) ? length - i : MAX_NUM_LEDS_SETTABLE;
memcpy(reds, redChannel + i, copyLength);
memcpy(greens, greenChannel + i, copyLength);
memcpy(blues, blueChannel + i, copyLength);
const int status = led_strip_set_rgb_values(ledStrip, i, copyLength, reds, greens, blues);
if (status != E_OK)
{
std::cerr << "Setting led values failed with status " << status << std::endl;
return status;
}
}
return E_OK;
}

View File

@ -0,0 +1,82 @@
#pragma once
// STL includes
#include <cstdio>
// Hyperion-Leddevice includes
#include <leddevice/LedDevice.h>
extern "C" {
#include <tinkerforge/ip_connection.h>
#include <tinkerforge/bricklet_led_strip.h>
}
class LedDeviceTinkerforge : public LedDevice
{
public:
LedDeviceTinkerforge(const std::string &host, uint16_t port, const std::string &uid, const unsigned interval);
virtual ~LedDeviceTinkerforge();
///
/// Attempts to open a connection to the master bricklet and the led strip bricklet.
///
/// @return Zero on succes else negative
///
int open();
///
/// Writes the colors to the led strip bricklet
///
/// @param ledValues The color value for each led
///
/// @return Zero on success else negative
///
virtual int write(const std::vector<ColorRgb> &ledValues);
///
/// Switches off the leds
///
/// @return Zero on success else negative
///
virtual int switchOff();
private:
///
/// Writes the data to the led strip blicklet
int transferLedData(LEDStrip *ledstrip, unsigned int index, unsigned int length, uint8_t *redChannel, uint8_t *greenChannel, uint8_t *blueChannel);
/// The host of the master brick
const std::string _host;
/// The port of the master brick
const uint16_t _port;
/// The uid of the led strip bricklet
const std::string _uid;
/// The interval/rate
const unsigned _interval;
/// ip connection handle
IPConnection *_ipConnection;
/// led strip handle
LEDStrip *_ledStrip;
/// buffer for red channel led data
std::vector<uint8_t> _redChannel;
/// buffer for red channel led data
std::vector<uint8_t> _greenChannel;
/// buffer for red channel led data
std::vector<uint8_t> _blueChannel;
/// buffer size of the color channels
unsigned int _colorChannelSize;
};

View File

@ -0,0 +1,42 @@
// STL includes
#include <cstring>
#include <cstdio>
#include <iostream>
// Linux includes
#include <fcntl.h>
#include <sys/ioctl.h>
// hyperion local includes
#include "LedDeviceTpm2.h"
LedDeviceTpm2::LedDeviceTpm2(const std::string& outputDevice, const unsigned baudrate) :
LedRs232Device(outputDevice, baudrate),
_ledBuffer(0)
{
// empty
}
int LedDeviceTpm2::write(const std::vector<ColorRgb> &ledValues)
{
if (_ledBuffer.size() == 0)
{
_ledBuffer.resize(5 + 3*ledValues.size());
_ledBuffer[0] = 0xC9; // block-start byte
_ledBuffer[1] = 0xDA; // DATA frame
_ledBuffer[2] = ((3 * ledValues.size()) >> 8) & 0xFF; // frame size high byte
_ledBuffer[3] = (3 * ledValues.size()) & 0xFF; // frame size low byte
_ledBuffer.back() = 0x36; // block-end byte
}
// write data
memcpy(4 + _ledBuffer.data(), ledValues.data(), ledValues.size() * 3);
return writeBytes(_ledBuffer.size(), _ledBuffer.data());
}
int LedDeviceTpm2::switchOff()
{
memset(4 + _ledBuffer.data(), 0, _ledBuffer.size() - 5);
return writeBytes(_ledBuffer.size(), _ledBuffer.data());
}

View File

@ -0,0 +1,38 @@
#pragma once
// STL includes
#include <string>
// hyperion incluse
#include "LedRs232Device.h"
///
/// Implementation of the LedDevice interface for writing to serial device using tpm2 protocol.
///
class LedDeviceTpm2 : public LedRs232Device
{
public:
///
/// Constructs the LedDevice for attached serial device using supporting tpm2 protocol
/// All LEDs in the stripe are handled as one frame
///
/// @param outputDevice The name of the output device (eg '/dev/ttyAMA0')
/// @param baudrate The used baudrate for writing to the output device
///
LedDeviceTpm2(const std::string& outputDevice, const unsigned baudrate);
///
/// 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<ColorRgb> &ledValues);
/// Switch the leds off
virtual int switchOff();
private:
/// The buffer containing the packed RGB values
std::vector<uint8_t> _ledBuffer;
};

View File

@ -1,182 +0,0 @@
// Local hyperion includes
#include "LedDeviceWs2811.h"
ws2811::SignalTiming ws2811::fromString(const std::string& signalTiming, const SignalTiming defaultValue)
{
SignalTiming result = defaultValue;
if (signalTiming == "3755" || signalTiming == "option_3755")
{
result = option_3755;
}
else if (signalTiming == "3773" || signalTiming == "option_3773")
{
result = option_3773;
}
else if (signalTiming == "2855" || signalTiming == "option_2855")
{
result = option_2855;
}
else if (signalTiming == "2882" || signalTiming == "option_2882")
{
result = option_2882;
}
return result;
}
unsigned ws2811::getBaudrate(const SpeedMode speedMode)
{
switch (speedMode)
{
case highspeed:
// Bit length: 125ns
return 8000000;
case lowspeed:
// Bit length: 250ns
return 4000000;
}
return 0;
}
inline unsigned ws2811::getLength(const SignalTiming timing, const TimeOption option)
{
switch (timing)
{
case option_3755:
// Reference: http://www.mikrocontroller.net/attachment/180459/WS2812B_preliminary.pdf
// Unit length: 125ns
switch (option)
{
case T0H:
return 3; // 400ns +-150ns
case T0L:
return 7; // 850ns +-150ns
case T1H:
return 7; // 800ns +-150ns
case T1L:
return 3; // 450ns +-150ns
}
case option_3773:
// Reference: www.adafruit.com/datasheets/WS2812.pdf
// Unit length: 125ns
switch (option)
{
case T0H:
return 3; // 350ns +-150ns
case T0L:
return 7; // 800ns +-150ns
case T1H:
return 7; // 700ns +-150ns
case T1L:
return 3; // 600ns +-150ns
}
case option_2855:
// Reference: www.adafruit.com/datasheets/WS2811.pdf
// Unit length: 250ns
switch (option)
{
case T0H:
return 2; // 500ns +-150ns
case T0L:
return 8; // 2000ns +-150ns
case T1H:
return 5; // 1200ns +-150ns
case T1L:
return 5; // 1300ns +-150ns
}
case option_2882:
// Reference: www.szparkson.net/download/WS2811.pdf
// Unit length: 250ns
switch (option)
{
case T0H:
return 2; // 500ns +-150ns
case T0L:
return 8; // 2000ns +-150ns
case T1H:
return 8; // 2000ns +-150ns
case T1L:
return 2; // 500ns +-150ns
}
default:
std::cerr << "Unknown signal timing for ws2811: " << timing << std::endl;
}
return 0;
}
uint8_t ws2811::bitToSignal(unsigned lenHigh)
{
// Sanity check on the length of the 'high' signal
assert(0 < lenHigh && lenHigh < 10);
uint8_t result = 0x00;
for (unsigned i=1; i<lenHigh; ++i)
{
result |= (1 << (8-i));
}
return result;
}
ws2811::ByteSignal ws2811::translate(SignalTiming ledOption, uint8_t byte)
{
ByteSignal result;
result.bit_1 = bitToSignal(getLength(ledOption, (byte & 0x80)?T1H:T0H));
result.bit_2 = bitToSignal(getLength(ledOption, (byte & 0x40)?T1H:T0H));
result.bit_3 = bitToSignal(getLength(ledOption, (byte & 0x20)?T1H:T0H));
result.bit_4 = bitToSignal(getLength(ledOption, (byte & 0x10)?T1H:T0H));
result.bit_5 = bitToSignal(getLength(ledOption, (byte & 0x08)?T1H:T0H));
result.bit_6 = bitToSignal(getLength(ledOption, (byte & 0x04)?T1H:T0H));
result.bit_7 = bitToSignal(getLength(ledOption, (byte & 0x02)?T1H:T0H));
result.bit_8 = bitToSignal(getLength(ledOption, (byte & 0x01)?T1H:T0H));
return result;
}
LedDeviceWs2811::LedDeviceWs2811(
const std::string & outputDevice,
const ws2811::SignalTiming signalTiming,
const ws2811::SpeedMode speedMode) :
LedRs232Device(outputDevice, ws2811::getBaudrate(speedMode))
{
fillEncodeTable(signalTiming);
}
int LedDeviceWs2811::write(const std::vector<ColorRgb> & ledValues)
{
if (_ledBuffer.size() != ledValues.size() * 3)
{
_ledBuffer.resize(ledValues.size() * 3);
}
auto bufIt = _ledBuffer.begin();
for (const ColorRgb & color : ledValues)
{
*bufIt = _byteToSignalTable[color.red ];
++bufIt;
*bufIt = _byteToSignalTable[color.green];
++bufIt;
*bufIt = _byteToSignalTable[color.blue ];
++bufIt;
}
writeBytes(_ledBuffer.size() * 3, reinterpret_cast<uint8_t *>(_ledBuffer.data()));
return 0;
}
int LedDeviceWs2811::switchOff()
{
write(std::vector<ColorRgb>(_ledBuffer.size()/3, ColorRgb::BLACK));
return 0;
}
void LedDeviceWs2811::fillEncodeTable(const ws2811::SignalTiming ledOption)
{
_byteToSignalTable.resize(256);
for (unsigned byteValue=0; byteValue<256; ++byteValue)
{
const uint8_t byteVal = uint8_t(byteValue);
_byteToSignalTable[byteValue] = ws2811::translate(ledOption, byteVal);
}
}

View File

@ -1,147 +0,0 @@
#pragma once
// STL includes
#include <cassert>
// Local hyperion includes
#include "LedRs232Device.h"
namespace ws2811
{
///
/// Enumaration of known signal timings
///
enum SignalTiming
{
option_3755,
option_3773,
option_2855,
option_2882,
not_a_signaltiming
};
///
/// Enumaration of the possible speeds on which the ws2811 can operate.
///
enum SpeedMode
{
lowspeed,
highspeed
};
///
/// Enumeration of the signal 'parts' (T 0 high, T 1 high, T 0 low, T 1 low).
///
enum TimeOption
{
T0H,
T1H,
T0L,
T1L
};
///
/// Structure holding the signal for a signle byte
///
struct ByteSignal
{
uint8_t bit_1;
uint8_t bit_2;
uint8_t bit_3;
uint8_t bit_4;
uint8_t bit_5;
uint8_t bit_6;
uint8_t bit_7;
uint8_t bit_8;
};
// Make sure the structure is exatly the length we require
static_assert(sizeof(ByteSignal) == 8, "Incorrect sizeof ByteSignal (expected 8)");
///
/// Translates a string to a signal timing
///
/// @param signalTiming The string specifying the signal timing
/// @param defaultValue The default value (used if the string does not match any known timing)
///
/// @return The SignalTiming (or not_a_signaltiming if it did not match)
///
SignalTiming fromString(const std::string& signalTiming, const SignalTiming defaultValue);
///
/// Returns the required baudrate for a specific signal-timing
///
/// @param SpeedMode The WS2811/WS2812 speed mode (WS2812b only has highspeed)
///
/// @return The required baudrate for the signal timing
///
unsigned getBaudrate(const SpeedMode speedMode);
///
/// The number of 'signal units' (bits) For the subpart of a specific timing scheme
///
/// @param timing The controller option
/// @param option The signal part
///
unsigned getLength(const SignalTiming timing, const TimeOption option);
///
/// Constructs a 'bit' based signal with defined 'high' length (and implicite defined 'low'
/// length. The signal is based on a 10bits bytes (incl. high startbit and low stopbit). The
/// total length of the high is given as parameter:<br>
/// lenHigh=7 => |-------|___| => 1 1111 1100 0 => 252 (start and stop bit are implicite)
///
/// @param lenHigh The total length of the 'high' length (incl start-bit)
/// @return The byte representing the high-low signal
///
uint8_t bitToSignal(unsigned lenHigh);
///
/// Translate a byte into signal levels for a specific WS2811 option
///
/// @param ledOption The WS2811 configuration
/// @param byte The byte to translate
///
/// @return The signal for the given byte (one byte per bit)
///
ByteSignal translate(SignalTiming ledOption, uint8_t byte);
}
class LedDeviceWs2811 : public LedRs232Device
{
public:
///
/// Constructs the LedDevice with Ws2811 attached via a serial port
///
/// @param outputDevice The name of the output device (eg '/dev/ttyS0')
/// @param signalTiming The timing scheme used by the Ws2811 chip
/// @param speedMode The speed modus of the Ws2811 chip
///
LedDeviceWs2811(const std::string& outputDevice, const ws2811::SignalTiming signalTiming, const ws2811::SpeedMode speedMode);
///
/// 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<ColorRgb> & ledValues);
/// Switch the leds off
virtual int switchOff();
private:
///
/// Fill the byte encoding table (_byteToSignalTable) for the specific timing option
///
/// @param ledOption The timing option
///
void fillEncodeTable(const ws2811::SignalTiming ledOption);
/// Translation table of byte to signal///
std::vector<ws2811::ByteSignal> _byteToSignalTable;
/// The buffer containing the packed RGB values
std::vector<ws2811::ByteSignal> _ledBuffer;
};

View File

@ -1,96 +0,0 @@
// Linux includes
#include <unistd.h>
// Local Hyperion-Leddevice includes
#include "LedDeviceWs2812b.h"
LedDeviceWs2812b::LedDeviceWs2812b() :
LedRs232Device("/dev/ttyUSB0", 2000000)
{
// empty
}
int LedDeviceWs2812b::write(const std::vector<ColorRgb> & ledValues)
{
// Ensure the size of the led-buffer
if (_ledBuffer.size() != ledValues.size()*8)
{
_ledBuffer.resize(ledValues.size()*8, ~0x24);
}
// Translate the channel of each color to a signal
uint8_t * signal_ptr = _ledBuffer.data();
for (const ColorRgb & color : ledValues)
{
signal_ptr = color2signal(color, signal_ptr);
}
const int result = writeBytes(_ledBuffer.size(), _ledBuffer.data());
// Official latch time is 50us (lets give it 50us more)
usleep(100);
return result;
}
uint8_t * LedDeviceWs2812b::color2signal(const ColorRgb & color, uint8_t * signal)
{
*signal = bits2Signal(color.red & 0x80, color.red & 0x40, color.red & 0x20);
++signal;
*signal = bits2Signal(color.red & 0x10, color.red & 0x08, color.red & 0x04);
++signal;
*signal = bits2Signal(color.red & 0x02, color.green & 0x01, color.green & 0x80);
++signal;
*signal = bits2Signal(color.green & 0x40, color.green & 0x20, color.green & 0x10);
++signal;
*signal = bits2Signal(color.green & 0x08, color.green & 0x04, color.green & 0x02);
++signal;
*signal = bits2Signal(color.green & 0x01, color.blue & 0x80, color.blue & 0x40);
++signal;
*signal = bits2Signal(color.blue & 0x20, color.blue & 0x10, color.blue & 0x08);
++signal;
*signal = bits2Signal(color.blue & 0x04, color.blue & 0x02, color.blue & 0x01);
++signal;
return signal;
}
int LedDeviceWs2812b::switchOff()
{
// Set all bytes in the signal buffer to zero
for (uint8_t & signal : _ledBuffer)
{
signal = ~0x24;
}
return writeBytes(_ledBuffer.size(), _ledBuffer.data());
}
uint8_t LedDeviceWs2812b::bits2Signal(const bool bit_1, const bool bit_2, const bool bit_3) const
{
// See https://github.com/tvdzwan/hyperion/wiki/Ws2812b for the explanation of the given
// translations
// Bit index(default):1 2 3
// | | |
// default value (1) 00 100 10 (0)
//
// Reversed value (1) 01 001 00 (0)
// | | |
// Bit index (rev): 3 2 1
uint8_t result = 0x24;
if(bit_1)
{
result |= 0x01;
}
if (bit_2)
{
result |= 0x08;
}
if (bit_3)
{
result |= 0x40;
}
return ~result;
}

View File

@ -1,60 +0,0 @@
#pragma once
// Hyperion leddevice includes
#include "LedRs232Device.h"
///
/// The LedDevice for controlling a string of WS2812B leds. These are controlled over the mini-UART
/// of the RPi (/dev/ttyAMA0).
///
class LedDeviceWs2812b : public LedRs232Device
{
public:
///
/// Constructs the device (all required parameters are hardcoded)
///
LedDeviceWs2812b();
///
/// Write the color data the the WS2812B led string
///
/// @param ledValues The color data
/// @return Zero on succes else negative
///
virtual int write(const std::vector<ColorRgb> & ledValues);
///
/// Write zero to all leds(that have been written by a previous write operation)
///
/// @return Zero on succes else negative
///
virtual int switchOff();
private:
///
/// Translate a color to the signal bits. The resulting bits are written to the given memory.
///
/// @param color The color to translate
/// @param signal The pointer at the beginning of the signal to write
/// @return The pointer at the end of the written signal
///
uint8_t * color2signal(const ColorRgb & color, uint8_t * signal);
///
/// Translates three bits to a single byte
///
/// @param bit1 The value of the first bit (1=true, zero=false)
/// @param bit2 The value of the second bit (1=true, zero=false)
/// @param bit3 The value of the third bit (1=true, zero=false)
///
/// @return The output-byte for the given two bit
///
uint8_t bits2Signal(const bool bit1, const bool bit2, const bool bit3) const;
///
/// The output buffer for writing bytes to the output
///
std::vector<uint8_t> _ledBuffer;
};

View File

@ -4,17 +4,21 @@
#include <cstdio>
#include <iostream>
// Qt includes
#include <QTimer>
// Serial includes
#include <serial/serial.h>
// Local Hyperion includes
#include "LedRs232Device.h"
LedRs232Device::LedRs232Device(const std::string& outputDevice, const unsigned baudrate) :
mDeviceName(outputDevice),
mBaudRate_Hz(baudrate),
_rs232Port()
LedRs232Device::LedRs232Device(const std::string& outputDevice, const unsigned baudrate, int delayAfterConnect_ms) :
_deviceName(outputDevice),
_baudRate_Hz(baudrate),
_delayAfterConnect_ms(delayAfterConnect_ms),
_rs232Port(),
_blockedForDelay(false)
{
// empty
}
@ -31,10 +35,17 @@ int LedRs232Device::open()
{
try
{
std::cout << "Opening UART: " << mDeviceName << std::endl;
_rs232Port.setPort(mDeviceName);
_rs232Port.setBaudrate(mBaudRate_Hz);
std::cout << "Opening UART: " << _deviceName << std::endl;
_rs232Port.setPort(_deviceName);
_rs232Port.setBaudrate(_baudRate_Hz);
_rs232Port.open();
if (_delayAfterConnect_ms > 0)
{
_blockedForDelay = true;
QTimer::singleShot(_delayAfterConnect_ms, this, SLOT(unblockAfterDelay()));
std::cout << "Device blocked for " << _delayAfterConnect_ms << " ms" << std::endl;
}
}
catch (const std::exception& e)
{
@ -47,6 +58,11 @@ int LedRs232Device::open()
int LedRs232Device::writeBytes(const unsigned size, const uint8_t * data)
{
if (_blockedForDelay)
{
return 0;
}
if (!_rs232Port.isOpen())
{
return -1;
@ -95,3 +111,9 @@ int LedRs232Device::writeBytes(const unsigned size, const uint8_t * data)
return 0;
}
void LedRs232Device::unblockAfterDelay()
{
std::cout << "Device unblocked" << std::endl;
_blockedForDelay = false;
}

View File

@ -1,5 +1,7 @@
#pragma once
#include <QObject>
// Serial includes
#include <serial/serial.h>
@ -9,8 +11,10 @@
///
/// The LedRs232Device implements an abstract base-class for LedDevices using a RS232-device.
///
class LedRs232Device : public LedDevice
class LedRs232Device : public QObject, public LedDevice
{
Q_OBJECT
public:
///
/// Constructs the LedDevice attached to a RS232-device
@ -18,7 +22,7 @@ public:
/// @param[in] outputDevice The name of the output device (eg '/etc/ttyS0')
/// @param[in] baudrate The used baudrate for writing to the output device
///
LedRs232Device(const std::string& outputDevice, const unsigned baudrate);
LedRs232Device(const std::string& outputDevice, const unsigned baudrate, int delayAfterConnect_ms = 0);
///
/// Destructor of the LedDevice; closes the output device if it is open
@ -43,12 +47,22 @@ protected:
*/
int writeBytes(const unsigned size, const uint8_t *data);
private slots:
/// Unblock the device after a connection delay
void unblockAfterDelay();
private:
/// The name of the output device
const std::string mDeviceName;
const std::string _deviceName;
/// The used baudrate of the output device
const int mBaudRate_Hz;
const int _baudRate_Hz;
/// Sleep after the connect before continuing
const int _delayAfterConnect_ms;
/// The RS232 serial-device
serial::Serial _rs232Port;
bool _blockedForDelay;
};

View File

@ -44,9 +44,5 @@ add_library(protoserver
target_link_libraries(protoserver
hyperion
hyperion-utils
${PROTOBUF_LIBRARIES})
qt4_use_modules(protoserver
Core
Gui
Network)
${PROTOBUF_LIBRARIES}
${QT_LIBRARIES})

View File

@ -11,6 +11,7 @@ add_library(hyperion-utils
${CURRENT_HEADER_DIR}/ColorRgba.h
${CURRENT_SOURCE_DIR}/ColorRgba.cpp
${CURRENT_HEADER_DIR}/Image.h
${CURRENT_HEADER_DIR}/Sleep.h
${CURRENT_HEADER_DIR}/HsvTransform.h
${CURRENT_SOURCE_DIR}/HsvTransform.cpp

View File

@ -17,6 +17,10 @@
// {"id":668,"jsonrpc":"2.0","method":"XBMC.GetInfoBooleans","params":{"booleans":["System.ScreenSaverActive"]}}
// {"id":668,"jsonrpc":"2.0","result":{"System.ScreenSaverActive":false}}
// Request stereoscopicmode example:
// {"jsonrpc":"2.0","method":"GUI.GetProperties","params":{"properties":["stereoscopicmode"]},"id":669}
// {"id":669,"jsonrpc":"2.0","result":{"stereoscopicmode":{"label":"Nebeneinander","mode":"split_vertical"}}}
XBMCVideoChecker::XBMCVideoChecker(const std::string & address, uint16_t port, bool grabVideo, bool grabPhoto, bool grabAudio, bool grabMenu, bool grabScreensaver, bool enable3DDetection) :
QObject(),
_address(QString::fromStdString(address)),
@ -24,6 +28,8 @@ XBMCVideoChecker::XBMCVideoChecker(const std::string & address, uint16_t port, b
_activePlayerRequest(R"({"id":666,"jsonrpc":"2.0","method":"Player.GetActivePlayers"})"),
_currentPlayingItemRequest(R"({"id":667,"jsonrpc":"2.0","method":"Player.GetItem","params":{"playerid":%1,"properties":["file"]}})"),
_checkScreensaverRequest(R"({"id":668,"jsonrpc":"2.0","method":"XBMC.GetInfoBooleans","params":{"booleans":["System.ScreenSaverActive"]}})"),
_getStereoscopicMode(R"({"jsonrpc":"2.0","method":"GUI.GetProperties","params":{"properties":["stereoscopicmode"]},"id":669})"),
_getXbmcVersion(R"({"jsonrpc":"2.0","method":"Application.GetProperties","params":{"properties":["version"]},"id":670})"),
_socket(),
_grabVideo(grabVideo),
_grabPhoto(grabPhoto),
@ -33,7 +39,8 @@ XBMCVideoChecker::XBMCVideoChecker(const std::string & address, uint16_t port, b
_enable3DDetection(enable3DDetection),
_previousScreensaverMode(false),
_previousGrabbingMode(GRABBINGMODE_INVALID),
_previousVideoMode(VIDEO_2D)
_previousVideoMode(VIDEO_2D),
_xbmcVersion(0)
{
// setup socket
connect(&_socket, SIGNAL(readyRead()), this, SLOT(receiveReply()));
@ -116,24 +123,32 @@ void XBMCVideoChecker::receiveReply()
}
else if (reply.contains("\"id\":667"))
{
// result of Player.GetItem
// TODO: what if the filename contains a '"'. In Json this should have been escaped
QRegExp regex("\"file\":\"((?!\").)*\"");
int pos = regex.indexIn(reply);
if (pos > 0)
if (_xbmcVersion >= 13)
{
QStringRef filename = QStringRef(&reply, pos+8, regex.matchedLength()-9);
if (filename.contains("3DSBS", Qt::CaseInsensitive) || filename.contains("HSBS", Qt::CaseInsensitive))
// check of active stereoscopicmode
_socket.write(_getStereoscopicMode.toUtf8());
}
else
{
// result of Player.GetItem
// TODO: what if the filename contains a '"'. In Json this should have been escaped
QRegExp regex("\"file\":\"((?!\").)*\"");
int pos = regex.indexIn(reply);
if (pos > 0)
{
setVideoMode(VIDEO_3DSBS);
}
else if (filename.contains("3DTAB", Qt::CaseInsensitive) || filename.contains("HTAB", Qt::CaseInsensitive))
{
setVideoMode(VIDEO_3DTAB);
}
else
{
setVideoMode(VIDEO_2D);
QStringRef filename = QStringRef(&reply, pos+8, regex.matchedLength()-9);
if (filename.contains("3DSBS", Qt::CaseInsensitive) || filename.contains("HSBS", Qt::CaseInsensitive))
{
setVideoMode(VIDEO_3DSBS);
}
else if (filename.contains("3DTAB", Qt::CaseInsensitive) || filename.contains("HTAB", Qt::CaseInsensitive))
{
setVideoMode(VIDEO_3DTAB);
}
else
{
setVideoMode(VIDEO_2D);
}
}
}
}
@ -142,6 +157,41 @@ void XBMCVideoChecker::receiveReply()
// result of System.ScreenSaverActive
bool active = reply.contains("\"System.ScreenSaverActive\":true");
setScreensaverMode(!_grabScreensaver && active);
// check here xbmc version
if (_socket.state() == QTcpSocket::ConnectedState)
{
if (_xbmcVersion == 0)
{
_socket.write(_getXbmcVersion.toUtf8());
}
}
}
else if (reply.contains("\"id\":669"))
{
QRegExp regex("\"mode\":\"(split_vertical|split_horizontal)\"");
int pos = regex.indexIn(reply);
if (pos > 0)
{
QString sMode = regex.cap(1);
if (sMode == "split_vertical")
{
setVideoMode(VIDEO_3DSBS);
}
else if (sMode == "split_horizontal")
{
setVideoMode(VIDEO_3DTAB);
}
}
}
else if (reply.contains("\"id\":670"))
{
QRegExp regex("\"major\":(\\d+)");
int pos = regex.indexIn(reply);
if (pos > 0)
{
_xbmcVersion = regex.cap(1).toInt();
}
}
}

View File

@ -28,11 +28,7 @@ add_executable(hyperion-remote
${hyperion-remote_HEADERS}
${hyperion-remote_SOURCES})
qt4_use_modules(hyperion-remote
Core
Gui
Network)
target_link_libraries(hyperion-remote
jsoncpp
getoptPlusPlus)
getoptPlusPlus
${QT_LIBRARIES})

View File

@ -1,5 +1,8 @@
#pragma once
// STL includes
#include <algorithm>
// Qt includes
#include <QColor>
#include <QImage>

View File

@ -48,8 +48,8 @@ int main(int argc, char * argv[])
IntParameter & argDuration = parameters.add<IntParameter> ('d', "duration" , "Specify how long the leds should be switched on in millseconds [default: infinity]");
ColorParameter & argColor = parameters.add<ColorParameter> ('c', "color" , "Set all leds to a constant color (either RRGGBB hex value or a color name. The color may be repeated multiple time like: RRGGBBRRGGBB)");
ImageParameter & argImage = parameters.add<ImageParameter> ('i', "image" , "Set the leds to the colors according to the given image file");
StringParameter & argEffect = parameters.add<StringParameter> ('e', "effect" , "Enable the effect with the given name");
StringParameter & argEffectArgs = parameters.add<StringParameter> (0x0, "effectArgs", "Arguments to use in combination with the specified effect. Should be a Json object string.");
StringParameter & argEffect = parameters.add<StringParameter> ('e', "effect" , "Enable the effect with the given name");
StringParameter & argEffectArgs = parameters.add<StringParameter> (0x0, "effectArgs", "Arguments to use in combination with the specified effect. Should be a Json object string.");
SwitchParameter<> & argServerInfo = parameters.add<SwitchParameter<> >('l', "list" , "List server info");
SwitchParameter<> & argClear = parameters.add<SwitchParameter<> >('x', "clear" , "Clear data for the priority channel provided by the -p option");
SwitchParameter<> & argClearAll = parameters.add<SwitchParameter<> >(0x0, "clearall" , "Clear data for all active priority channels");
@ -83,13 +83,13 @@ int main(int argc, char * argv[])
bool colorTransform = argSaturation.isSet() || argValue.isSet() || argThreshold.isSet() || argGamma.isSet() || argBlacklevel.isSet() || argWhitelevel.isSet();
// check that exactly one command was given
int commandCount = count({argColor.isSet(), argImage.isSet(), argEffect.isSet(), argServerInfo.isSet(), argClear.isSet(), argClearAll.isSet(), colorTransform});
int commandCount = count({argColor.isSet(), argImage.isSet(), argEffect.isSet(), argServerInfo.isSet(), argClear.isSet(), argClearAll.isSet(), colorTransform});
if (commandCount != 1)
{
std::cerr << (commandCount == 0 ? "No command found." : "Multiple commands found.") << " Provide exactly one of the following options:" << std::endl;
std::cerr << " " << argColor.usageLine() << std::endl;
std::cerr << " " << argImage.usageLine() << std::endl;
std::cerr << " " << argEffect.usageLine() << std::endl;
std::cerr << " " << argEffect.usageLine() << std::endl;
std::cerr << " " << argServerInfo.usageLine() << std::endl;
std::cerr << " " << argClear.usageLine() << std::endl;
std::cerr << " " << argClearAll.usageLine() << std::endl;
@ -116,11 +116,11 @@ int main(int argc, char * argv[])
{
connection.setImage(argImage.getValue(), argPriority.getValue(), argDuration.getValue());
}
else if (argEffect.isSet())
{
connection.setEffect(argEffect.getValue(), argEffectArgs.getValue(), argPriority.getValue(), argDuration.getValue());
}
else if (argServerInfo.isSet())
else if (argEffect.isSet())
{
connection.setEffect(argEffect.getValue(), argEffectArgs.getValue(), argPriority.getValue(), argDuration.getValue());
}
else if (argServerInfo.isSet())
{
QString info = connection.getServerInfo();
std::cout << "Server info:\n" << info.toStdString() << std::endl;

View File

@ -14,18 +14,22 @@ include_directories(
${QT_INCLUDES}
)
set(Hyperion_V4L2_HEADERS
V4L2Grabber.h
ProtoConnection.h
set(Hyperion_V4L2_QT_HEADERS
ImageHandler.h
ScreenshotHandler.h
)
set(Hyperion_V4L2_HEADERS
VideoStandardParameter.h
PixelFormatParameter.h
ProtoConnection.h
)
set(Hyperion_V4L2_SOURCES
hyperion-v4l2.cpp
V4L2Grabber.cpp
ProtoConnection.cpp
ImageHandler.cpp
ScreenshotHandler.cpp
)
set(Hyperion_V4L2_PROTOS
@ -36,22 +40,23 @@ protobuf_generate_cpp(Hyperion_V4L2_PROTO_SRCS Hyperion_V4L2_PROTO_HDRS
${Hyperion_V4L2_PROTOS}
)
QT4_WRAP_CPP(Hyperion_V4L2_MOC_SOURCES ${Hyperion_V4L2_QT_HEADERS})
add_executable(hyperion-v4l2
${Hyperion_V4L2_HEADERS}
${Hyperion_V4L2_SOURCES}
${Hyperion_V4L2_QT_HEADERS}
${Hyperion_V4L2_MOC_SOURCES}
${Hyperion_V4L2_PROTO_SRCS}
${Hyperion_V4L2_PROTO_HDRS}
)
target_link_libraries(hyperion-v4l2
v4l2-grabber
getoptPlusPlus
blackborder
hyperion-utils
${PROTOBUF_LIBRARIES}
pthread
${QT_LIBRARIES}
)
qt4_use_modules(hyperion-v4l2
Core
Gui
Network)

View File

@ -1,41 +1,18 @@
// hyperion-v4l2 includes
#include "ImageHandler.h"
ImageHandler::ImageHandler(const std::string & address, int priority, double signalThreshold, bool skipProtoReply) :
ImageHandler::ImageHandler(const std::string & address, int priority, bool skipProtoReply) :
_priority(priority),
_connection(address),
_signalThreshold(signalThreshold),
_signalProcessor(100, 50, 0, uint8_t(std::min(255, std::max(0, int(255*signalThreshold)))))
_connection(address)
{
_connection.setSkipReply(skipProtoReply);
}
ImageHandler::~ImageHandler()
{
}
void ImageHandler::receiveImage(const Image<ColorRgb> & image)
{
// check if we should do signal detection
if (_signalThreshold < 0)
{
_connection.setImage(image, _priority, 1000);
}
else
{
if (_signalProcessor.process(image))
{
std::cout << "Signal state = " << (_signalProcessor.getCurrentBorder().unknown ? "off" : "on") << std::endl;
}
// consider an unknown border as no signal
// send the image to Hyperion if we have a signal
if (!_signalProcessor.getCurrentBorder().unknown)
{
_connection.setImage(image, _priority, 1000);
}
}
_connection.setImage(image, _priority, 1000);
}
void ImageHandler::imageCallback(void *arg, const Image<ColorRgb> &image)
{
ImageHandler * handler = static_cast<ImageHandler *>(arg);
handler->receiveImage(image);
}

View File

@ -1,34 +1,31 @@
// blackborder includes
#include <blackborder/BlackBorderProcessor.h>
// Qt includes
#include <QObject>
// hyperion-v4l includes
// hyperion includes
#include <utils/Image.h>
#include <utils/ColorRgb.h>
// hyperion v4l2 includes
#include "ProtoConnection.h"
/// This class handles callbacks from the V4L2 grabber
class ImageHandler
class ImageHandler : public QObject
{
public:
ImageHandler(const std::string & address, int priority, double signalThreshold, bool skipProtoReply);
Q_OBJECT
public:
ImageHandler(const std::string & address, int priority, bool skipProtoReply);
virtual ~ImageHandler();
public slots:
/// Handle a single image
/// @param image The image to process
void receiveImage(const Image<ColorRgb> & image);
/// static function used to direct callbacks to a ImageHandler object
/// @param arg This should be an ImageHandler instance
/// @param image The image to process
static void imageCallback(void * arg, const Image<ColorRgb> & image);
private:
/// Priority for calls to Hyperion
const int _priority;
/// Hyperion proto connection object
ProtoConnection _connection;
/// Threshold used for signal detection
double _signalThreshold;
/// Blackborder detector which is used as a signal detector (unknown border = no signal)
hyperion::BlackBorderProcessor _signalProcessor;
};

View File

@ -0,0 +1,43 @@
// getoptPlusPLus includes
#include <getoptPlusPlus/getoptpp.h>
// grabber includes
#include <grabber/PixelFormat.h>
using namespace vlofgren;
/// Data parameter for the pixel format
typedef vlofgren::PODParameter<PixelFormat> PixelFormatParameter;
namespace vlofgren {
/// Translates a string (as passed on the commandline) to a pixel format
///
/// @param[in] s The string (as passed on the commandline)
/// @return The pixel format
/// @throws Parameter::ParameterRejected If the string did not result in a pixel format
template<>
PixelFormat PixelFormatParameter::validate(const std::string& s) throw (Parameter::ParameterRejected)
{
QString input = QString::fromStdString(s).toLower();
if (input == "yuyv")
{
return PIXELFORMAT_YUYV;
}
else if (input == "uyvy")
{
return PIXELFORMAT_UYVY;
}
else if (input == "rgb32")
{
return PIXELFORMAT_RGB32;
}
else if (input == "no-change")
{
return PIXELFORMAT_NO_CHANGE;
}
throw Parameter::ParameterRejected("Invalid value for pixel format. Valid values are: YUYV, UYVY, RGB32, and NO-CHANGE");
return PIXELFORMAT_NO_CHANGE;
}
}

View File

@ -0,0 +1,25 @@
// Qt includes
#include <QImage>
#include <QCoreApplication>
// hyperion-v4l2 includes
#include "ScreenshotHandler.h"
ScreenshotHandler::ScreenshotHandler(const std::string & filename) :
_filename(filename)
{
}
ScreenshotHandler::~ScreenshotHandler()
{
}
void ScreenshotHandler::receiveImage(const Image<ColorRgb> & image)
{
// store as PNG
QImage pngImage((const uint8_t *) image.memptr(), image.width(), image.height(), 3*image.width(), QImage::Format_RGB888);
pngImage.save(_filename.c_str());
// Quit the application after the first image
QCoreApplication::quit();
}

View File

@ -0,0 +1,24 @@
// Qt includes
#include <QObject>
// hyperionincludes
#include <utils/Image.h>
#include <utils/ColorRgb.h>
/// This class handles callbacks from the V4L2 grabber
class ScreenshotHandler : public QObject
{
Q_OBJECT
public:
ScreenshotHandler(const std::string & filename);
virtual ~ScreenshotHandler();
public slots:
/// Handle a single image
/// @param image The image to process
void receiveImage(const Image<ColorRgb> & image);
private:
const std::string _filename;
};

View File

@ -1,10 +1,13 @@
// getoptPlusPLus includes
#include <getoptPlusPlus/getoptpp.h>
// grabber includes
#include <grabber/VideoStandard.h>
using namespace vlofgren;
/// Data parameter for the video standard
typedef vlofgren::PODParameter<V4L2Grabber::VideoStandard> VideoStandardParameter;
typedef vlofgren::PODParameter<VideoStandard> VideoStandardParameter;
namespace vlofgren {
/// Translates a string (as passed on the commandline) to a color standard
@ -13,24 +16,24 @@ namespace vlofgren {
/// @return The color standard
/// @throws Parameter::ParameterRejected If the string did not result in a video standard
template<>
V4L2Grabber::VideoStandard VideoStandardParameter::validate(const std::string& s) throw (Parameter::ParameterRejected)
VideoStandard VideoStandardParameter::validate(const std::string& s) throw (Parameter::ParameterRejected)
{
QString input = QString::fromStdString(s).toLower();
if (input == "pal")
{
return V4L2Grabber::PAL;
return VIDEOSTANDARD_PAL;
}
else if (input == "ntsc")
{
return V4L2Grabber::NTSC;
return VIDEOSTANDARD_NTSC;
}
else if (input == "no-change")
{
return V4L2Grabber::NO_CHANGE;
return VIDEOSTANDARD_NO_CHANGE;
}
throw Parameter::ParameterRejected("Invalid value for video standard. Valid values are: PAL, NTSC, and NO-CHANGE");
return V4L2Grabber::NO_CHANGE;
return VIDEOSTANDARD_NO_CHANGE;
}
}

View File

@ -4,7 +4,7 @@
#include <clocale>
// QT includes
#include <QImage>
#include <QCoreApplication>
// getoptPlusPLus includes
#include <getoptPlusPlus/getoptpp.h>
@ -12,11 +12,15 @@
// blackborder includes
#include <blackborder/BlackBorderProcessor.h>
// grabber includes
#include "grabber/V4L2Grabber.h"
// hyperion-v4l2 includes
#include "V4L2Grabber.h"
#include "ProtoConnection.h"
#include "VideoStandardParameter.h"
#include "PixelFormatParameter.h"
#include "ImageHandler.h"
#include "ScreenshotHandler.h"
using namespace vlofgren;
@ -30,8 +34,14 @@ void saveScreenshot(void *, const Image<ColorRgb> & image)
int main(int argc, char** argv)
{
QCoreApplication app(argc, argv);
// force the locale
setlocale(LC_ALL, "C");
QLocale::setDefault(QLocale::c());
// register the image type to use in signals
qRegisterMetaType<Image<ColorRgb>>("Image<ColorRgb>");
try
{
@ -41,6 +51,7 @@ int main(int argc, char** argv)
StringParameter & argDevice = parameters.add<StringParameter> ('d', "device", "The device to use [default=/dev/video0]");
VideoStandardParameter & argVideoStandard = parameters.add<VideoStandardParameter>('v', "video-standard", "The used video standard. Valid values are PAL or NTSC (optional)");
PixelFormatParameter & argPixelFormat = parameters.add<PixelFormatParameter> (0x0, "pixel-format", "The use pixel format. Valid values are YUYV, UYVY, and RGB32 (optional)");
IntParameter & argInput = parameters.add<IntParameter> (0x0, "input", "Input channel (optional)");
IntParameter & argWidth = parameters.add<IntParameter> (0x0, "width", "Try to set the width of the video input (optional)");
IntParameter & argHeight = parameters.add<IntParameter> (0x0, "height", "Try to set the height of the video input (optional)");
@ -54,6 +65,9 @@ int main(int argc, char** argv)
IntParameter & argFrameDecimation = parameters.add<IntParameter> ('f', "frame-decimator", "Decimation factor for the video frames [default=1]");
SwitchParameter<> & argScreenshot = parameters.add<SwitchParameter<>> (0x0, "screenshot", "Take a single screenshot, save it to file and quit");
DoubleParameter & argSignalThreshold = parameters.add<DoubleParameter> ('t', "signal-threshold", "The signal threshold for detecting the presence of a signal. Value should be between 0.0 and 1.0.");
DoubleParameter & argRedSignalThreshold = parameters.add<DoubleParameter> (0x0, "red-threshold", "The red signal threshold. Value should be between 0.0 and 1.0. (overrides --signal-threshold)");
DoubleParameter & argGreenSignalThreshold = parameters.add<DoubleParameter> (0x0, "green-threshold", "The green signal threshold. Value should be between 0.0 and 1.0. (overrides --signal-threshold)");
DoubleParameter & argBlueSignalThreshold = parameters.add<DoubleParameter> (0x0, "blue-threshold", "The blue signal threshold. Value should be between 0.0 and 1.0. (overrides --signal-threshold)");
SwitchParameter<> & arg3DSBS = parameters.add<SwitchParameter<>> (0x0, "3DSBS", "Interpret the incoming video stream as 3D side-by-side");
SwitchParameter<> & arg3DTAB = parameters.add<SwitchParameter<>> (0x0, "3DTAB", "Interpret the incoming video stream as 3D top-and-bottom");
StringParameter & argAddress = parameters.add<StringParameter> ('a', "address", "Set the address of the hyperion server [default: 127.0.0.1:19445]");
@ -63,7 +77,8 @@ int main(int argc, char** argv)
// set defaults
argDevice.setDefault("/dev/video0");
argVideoStandard.setDefault(V4L2Grabber::NO_CHANGE);
argVideoStandard.setDefault(VIDEOSTANDARD_NO_CHANGE);
argPixelFormat.setDefault(PIXELFORMAT_NO_CHANGE);
argInput.setDefault(-1);
argWidth.setDefault(-1);
argHeight.setDefault(-1);
@ -95,12 +110,20 @@ int main(int argc, char** argv)
argDevice.getValue(),
argInput.getValue(),
argVideoStandard.getValue(),
argPixelFormat.getValue(),
argWidth.getValue(),
argHeight.getValue(),
std::max(1, argFrameDecimation.getValue()),
std::max(1, argSizeDecimation.getValue()),
std::max(1, argSizeDecimation.getValue()));
// set signal detection
grabber.setSignalThreshold(
std::min(1.0, std::max(0.0, argRedSignalThreshold.isSet() ? argRedSignalThreshold.getValue() : argSignalThreshold.getValue())),
std::min(1.0, std::max(0.0, argGreenSignalThreshold.isSet() ? argGreenSignalThreshold.getValue() : argSignalThreshold.getValue())),
std::min(1.0, std::max(0.0, argBlueSignalThreshold.isSet() ? argBlueSignalThreshold.getValue() : argSignalThreshold.getValue())),
50);
// set cropping values
grabber.setCropping(
std::max(0, argCropLeft.getValue()),
@ -111,31 +134,30 @@ int main(int argc, char** argv)
// set 3D mode if applicable
if (arg3DSBS.isSet())
{
grabber.set3D(V4L2Grabber::MODE_3DSBS);
grabber.set3D(VIDEO_3DSBS);
}
else if (arg3DTAB.isSet())
{
grabber.set3D(V4L2Grabber::MODE_3DTAB);
grabber.set3D(VIDEO_3DTAB);
}
// start the grabber
grabber.start();
// run the grabber
if (argScreenshot.isSet())
{
grabber.setCallback(&saveScreenshot, nullptr);
grabber.capture(1);
ScreenshotHandler handler("screenshot.png");
QObject::connect(&grabber, SIGNAL(newFrame(Image<ColorRgb>)), &handler, SLOT(receiveImage(Image<ColorRgb>)));
grabber.start();
QCoreApplication::exec();
grabber.stop();
}
else
{
ImageHandler handler(argAddress.getValue(), argPriority.getValue(), argSignalThreshold.getValue(), argSkipReply.isSet());
grabber.setCallback(&ImageHandler::imageCallback, &handler);
grabber.capture();
ImageHandler handler(argAddress.getValue(), argPriority.getValue(), argSkipReply.isSet());
QObject::connect(&grabber, SIGNAL(newFrame(Image<ColorRgb>)), &handler, SLOT(receiveImage(Image<ColorRgb>)));
grabber.start();
QCoreApplication::exec();
grabber.stop();
}
// stop the grabber
grabber.stop();
}
catch (const std::runtime_error & e)
{

View File

@ -8,8 +8,13 @@ target_link_libraries(hyperiond
effectengine
jsonserver
protoserver
boblightserver)
boblightserver
)
if (ENABLE_DISPMANX)
target_link_libraries(hyperiond dispmanx-grabber)
endif (ENABLE_DISPMANX)
if (ENABLE_V4L2)
target_link_libraries(hyperiond v4l2-grabber)
endif (ENABLE_V4L2)

View File

@ -19,7 +19,12 @@
#ifdef ENABLE_DISPMANX
// Dispmanx grabber includes
#include <dispmanx-grabber/DispmanxWrapper.h>
#include <grabber/DispmanxWrapper.h>
#endif
#ifdef ENABLE_V4L2
// v4l2 grabber
#include <grabber/V4L2Wrapper.h>
#endif
// XBMC Video checker includes
@ -107,14 +112,31 @@ int main(int argc, char** argv)
const std::string effectName = effectConfig["effect"].asString();
const unsigned duration_ms = effectConfig["duration_ms"].asUInt();
const int priority = 0;
if (hyperion.setEffect(effectName, priority, duration_ms) == 0)
if (effectConfig.isMember("args"))
{
std::cout << "Boot sequence(" << effectName << ") created and started" << std::endl;
const Json::Value effectConfigArgs = effectConfig["args"];
if (hyperion.setEffect(effectName, effectConfigArgs, priority, duration_ms) == 0)
{
std::cout << "Boot sequence(" << effectName << ") with user-defined arguments created and started" << std::endl;
}
else
{
std::cout << "Failed to start boot sequence: " << effectName << " with user-defined arguments" << std::endl;
}
}
else
{
std::cout << "Failed to start boot sequence: " << effectName << std::endl;
if (hyperion.setEffect(effectName, priority, duration_ms) == 0)
{
std::cout << "Boot sequence(" << effectName << ") created and started" << std::endl;
}
else
{
std::cout << "Failed to start boot sequence: " << effectName << std::endl;
}
}
}
@ -165,6 +187,43 @@ int main(int argc, char** argv)
}
#endif
#ifdef ENABLE_V4L2
// construct and start the v4l2 grabber if the configuration is present
V4L2Wrapper * v4l2Grabber = nullptr;
if (config.isMember("grabber-v4l2"))
{
const Json::Value & grabberConfig = config["grabber-v4l2"];
v4l2Grabber = new V4L2Wrapper(
grabberConfig.get("device", "/dev/video0").asString(),
grabberConfig.get("input", 0).asInt(),
parseVideoStandard(grabberConfig.get("standard", "no-change").asString()),
parsePixelFormat(grabberConfig.get("pixelFormat", "no-change").asString()),
grabberConfig.get("width", -1).asInt(),
grabberConfig.get("height", -1).asInt(),
grabberConfig.get("frameDecimation", 2).asInt(),
grabberConfig.get("sizeDecimation", 8).asInt(),
grabberConfig.get("redSignalThreshold", 0.0).asDouble(),
grabberConfig.get("greenSignalThreshold", 0.0).asDouble(),
grabberConfig.get("blueSignalThreshold", 0.0).asDouble(),
&hyperion,
grabberConfig.get("priority", 800).asInt());
v4l2Grabber->set3D(parse3DMode(grabberConfig.get("mode", "2D").asString()));
v4l2Grabber->setCropping(
grabberConfig.get("cropLeft", 0).asInt(),
grabberConfig.get("cropRight", 0).asInt(),
grabberConfig.get("cropTop", 0).asInt(),
grabberConfig.get("cropBottom", 0).asInt());
v4l2Grabber->start();
std::cout << "V4l2 grabber created and started" << std::endl;
}
#else
if (config.isMember("grabber-v4l2"))
{
std::cerr << "The v4l2 grabber can not be instantiated, becuse it has been left out from the build" << std::endl;
}
#endif
// Create Json server if configuration is present
JsonServer * jsonServer = nullptr;
if (config.isMember("jsonServer"))
@ -199,6 +258,9 @@ int main(int argc, char** argv)
// Delete all component
#ifdef ENABLE_DISPMANX
delete dispmanx;
#endif
#ifdef ENABLE_V4L2
delete v4l2Grabber;
#endif
delete xbmcVideoChecker;
delete jsonServer;

View File

@ -52,20 +52,3 @@ target_link_libraries(test_qregexp
add_executable(test_qtscreenshot TestQtScreenshot.cpp)
target_link_libraries(test_qtscreenshot
${QT_LIBRARIES})
add_executable(determineWs2811Timing DetermineWs2811Timing.cpp)
add_executable(test_rs232highspeed
TestRs232HighSpeed.cpp
../libsrc/leddevice/LedRs232Device.cpp
../libsrc/leddevice/LedDeviceWs2812b.cpp)
target_link_libraries(test_rs232highspeed
serialport)
if(NOT APPLE AND UNIX)
include_directories(/usr/include)
add_executable(test_uartHighSpeed TestUartHighSpeed.cpp)
add_executable(test_nonUniformWs2812b TestNonUniformWs2812b.cpp)
add_executable(test_nonInvWs2812b TestNonInvWs2812b.cpp)
endif()

View File

@ -1,62 +0,0 @@
// STl includes
#include <iostream>
#include <cmath>
bool requiredTiming(const int tHigh_ns, const int tLow_ns, const int error_ns, const int nrBits)
{
std::cout << "=== " << nrBits << " bits case ===== " << std::endl;
double bitLength_ns = (tHigh_ns + tLow_ns)/double(nrBits);
double baudrate_Hz = 1.0 / bitLength_ns * 1e9;
std::cout << "Required bit length: " << bitLength_ns << "ns => baudrate = " << baudrate_Hz << std::endl;
double highBitsExact = tHigh_ns/bitLength_ns;
int highBits = std::round(highBitsExact);
double lowBitsExact = tLow_ns/bitLength_ns;
int lowBits = std::round(lowBitsExact);
std::cout << "Bit division: high=" << highBits << "(" << highBitsExact << "); low=" << lowBits << "(" << lowBitsExact << ")" << std::endl;
double highBitsError = std::fabs(highBitsExact - highBits);
double lowBitsError = std::fabs(highBitsExact - highBits);
double highError_ns = highBitsError * bitLength_ns;
double lowError_ns = lowBitsError * bitLength_ns;
if (highError_ns > error_ns || lowError_ns > error_ns)
{
std::cerr << "Timing error outside specs: " << highError_ns << "; " << lowError_ns << " > " << error_ns << std::endl;
}
else
{
std::cout << "Timing within margins: " << highError_ns << "; " << lowError_ns << " < " << error_ns << std::endl;
}
return true;
}
int main()
{
// 10bits
requiredTiming(400, 850, 150, 10); // Zero
requiredTiming(800, 450, 150, 10); // One
// 6bits
requiredTiming(400, 850, 150, 6); // Zero
requiredTiming(800, 450, 150, 6); // One
// 5bits
requiredTiming(400, 850, 150, 5); // Zero
requiredTiming(800, 450, 150, 5); // One
requiredTiming(650, 600, 150, 5); // One
// 4bits
requiredTiming(400, 850, 150, 4); // Zero
requiredTiming(800, 450, 150, 4); // One
// 3bits
requiredTiming(400, 850, 150, 3); // Zero
requiredTiming(800, 450, 150, 3); // One
return 0;
}

View File

@ -1,209 +0,0 @@
// STL includes
#include <cstdint>
#include <vector>
#include <iostream>
#include <unistd.h> //Used for UART
#include <fcntl.h> //Used for UART
#include <termios.h> //Used for UART
#include <sys/ioctl.h>
std::vector<uint8_t> encode(const std::vector<uint8_t> & data);
void split(const uint8_t byte, uint8_t & out1, uint8_t & out2);
uint8_t encode(const bool bit1, const bool bit2, const bool bit3);
void print(uint8_t byte)
{
for (int i=0; i<8; ++i)
{
if (byte & (1 << i))
{
std::cout << '1';
}
else
{
std::cout << '0';
}
}
}
void printClockSignal(const std::vector<uint8_t> & signal)
{
bool prevBit = true;
bool nextBit = true;
for (uint8_t byte : signal)
{
for (int i=-1; i<9; ++i)
{
if (i == -1) // Start bit
nextBit = false;
else if (i == 8) // Stop bit
nextBit = true;
else
nextBit = byte & (1 << i);
if (!prevBit && nextBit)
{
std::cout << ' ';
}
if (nextBit)
std::cout << '1';
else
std::cout << '0';
prevBit = nextBit;
}
}
}
int main()
{
const std::vector<uint8_t> data(9, 0x00);
std::vector<uint8_t> encData = encode(data);
for (uint8_t encByte : encData)
{
std::cout << "0 ";
print(encByte);
std::cout << " 1";
}
std::cout << std::endl;
printClockSignal(encData);
std::cout << std::endl;
//OPEN THE UART
// int uart0_filestream = open("/dev/ttyAMA0", O_WRONLY | O_NOCTTY | O_NDELAY);
int uart0_filestream = open("/dev/ttyUSB0", O_WRONLY | O_NOCTTY | O_NDELAY);
if (uart0_filestream == -1)
{
//ERROR - CAN'T OPEN SERIAL PORT
printf("Error - Unable to open UART. Ensure it is not in use by another application\n");
return -1;
}
// Configure the port
struct termios options;
tcgetattr(uart0_filestream, &options);
options.c_cflag = B2500000 | CS8 | CLOCAL;
options.c_iflag = IGNPAR;
options.c_oflag = 0;
options.c_lflag = 0;
tcflush(uart0_filestream, TCIFLUSH);
tcsetattr(uart0_filestream, TCSANOW, &options);
getchar();
const int breakLength_ms = 1;
encData = std::vector<uint8_t>(128, 0x00);
write(uart0_filestream, encData.data(), encData.size());
tcsendbreak(uart0_filestream, breakLength_ms);
//tcdrain(uart0_filestream);
// res = write(uart0_filestream, encData.data(), encData.size());
// (void)res;
close(uart0_filestream);
return 0;
}
std::vector<uint8_t> encode(const std::vector<uint8_t> & data)
{
std::vector<uint8_t> result;
uint8_t previousByte = 0x00;
uint8_t nextByte = 0x00;
for (unsigned iData=0; iData<data.size(); iData+=3)
{
const uint8_t byte1 = data[iData];
const uint8_t byte2 = data[iData+1];
const uint8_t byte3 = data[iData+2];
uint8_t encByte;
encByte = encode(byte1 & 0x80, byte1 & 0x40, byte1 & 0x20);
std::cout << "Encoded byte 1: "; print(encByte); std::cout << std::endl;
split(encByte, previousByte, nextByte);
result.push_back(previousByte);
previousByte = nextByte;
encByte = encode(byte1 & 0x10, byte1 & 0x08, byte1 & 0x04);
split(encByte, previousByte, nextByte);
result.push_back(previousByte);
previousByte = nextByte;
encByte = encode(byte1 & 0x02, byte1 & 0x01, byte2 & 0x80);
split(encByte, previousByte, nextByte);
result.push_back(previousByte);
previousByte = nextByte;
encByte = encode(byte2 & 0x40, byte2 & 0x20, byte2 & 0x10);
split(encByte, previousByte, nextByte);
result.push_back(previousByte);
previousByte = nextByte;
encByte = encode(byte2 & 0x08, byte2 & 0x04, byte2 & 0x02);
split(encByte, previousByte, nextByte);
result.push_back(previousByte);
previousByte = nextByte;
encByte = encode(byte2 & 0x01, byte3 & 0x80, byte3 & 0x40);
split(encByte, previousByte, nextByte);
result.push_back(previousByte);
previousByte = nextByte;
encByte = encode(byte3 & 0x20, byte3 & 0x10, byte3 & 0x08);
split(encByte, previousByte, nextByte);
result.push_back(previousByte);
previousByte = nextByte;
encByte = encode(byte3 & 0x04, byte3 & 0x02, byte3 & 0x01);
split(encByte, previousByte, nextByte);
result.push_back(previousByte);
previousByte = nextByte;
}
result.push_back(previousByte);
return result;
}
void split(const uint8_t byte, uint8_t & out1, uint8_t & out2)
{
out1 |= (byte & 0x0F) << 4;
out2 = (byte & 0xF0) >> 4;
}
uint8_t encode(const bool bit1, const bool bit2, const bool bit3)
{
if (bit2)
{
uint8_t result = 0x19; // 0--1 01 10-1
if (bit1) result |= 0x02;
// else result &= ~0x02;
if (bit3) result |= 0x60;
// else result &= ~0x60;
return result;
}
else
{
uint8_t result = 0x21;// 0x21 (0-10 01 0--1)
if (bit1) result |= 0x06;
// else result &= ~0x06;
if (bit3) result |= 0x40;
// else result &= ~0x40;
return result;
}
}

View File

@ -1,188 +0,0 @@
// STL includes
#include <cstdint>
#include <vector>
#include <iostream>
#include <unistd.h> //Used for UART
#include <fcntl.h> //Used for UART
#include <termios.h> //Used for UART
#include <sys/ioctl.h>
std::vector<uint8_t> encode(const std::vector<uint8_t> & data);
uint8_t encode(const bool bit1, const bool bit2, const bool bit3);
void printClockSignal(const std::vector<uint8_t> & signal)
{
bool prevBit = true;
bool nextBit = true;
for (uint8_t byte : signal)
{
for (int i=-1; i<9; ++i)
{
if (i == -1) // Start bit
nextBit = true;
else if (i == 8) // Stop bit
nextBit = false;
else
nextBit = ~byte & (1 << i);
if (!prevBit && nextBit)
{
std::cout << ' ';
}
if (nextBit)
std::cout << '1';
else
std::cout << '0';
prevBit = nextBit;
}
}
}
int main()
{
const std::vector<uint8_t> white{0xff,0xff,0xff, 0xff,0xff,0xff, 0xff,0xff,0xff};
const std::vector<uint8_t> green{0xff, 0x00, 0x00};
const std::vector<uint8_t> red {0x00, 0xff, 0x00};
const std::vector<uint8_t> blue {0x00, 0x00, 0xff};
const std::vector<uint8_t> cyan {0xff, 0x00, 0xff};
const std::vector<uint8_t> mix {0x55, 0x55, 0x55};
const std::vector<uint8_t> black{0x00, 0x00, 0x00};
const std::vector<uint8_t> gray{0x01, 0x01, 0x01};
// printClockSignal(encode(mix));std::cout << std::endl;
//OPEN THE UART
// int uart0_filestream = open("/dev/ttyAMA0", O_WRONLY | O_NOCTTY | O_NDELAY);
int uart0_filestream = open("/dev/ttyUSB0", O_WRONLY | O_NOCTTY | O_NDELAY);
if (uart0_filestream == -1)
{
//ERROR - CAN'T OPEN SERIAL PORT
printf("Error - Unable to open UART. Ensure it is not in use by another application\n");
return -1;
}
// Configure the port
struct termios options;
tcgetattr(uart0_filestream, &options);
options.c_cflag = B2500000 | CS8 | CLOCAL;
options.c_iflag = IGNPAR;
options.c_oflag = 0;
options.c_lflag = 0;
tcflush(uart0_filestream, TCIFLUSH);
tcsetattr(uart0_filestream, TCSANOW, &options);
{
getchar();
const std::vector<uint8_t> encGreenData = encode(green);
const std::vector<uint8_t> encBlueData = encode(blue);
const std::vector<uint8_t> encRedData = encode(red);
const std::vector<uint8_t> encGrayData = encode(gray);
const std::vector<uint8_t> encBlackData = encode(black);
//std::cout << "Writing GREEN ("; printClockSignal(encode(green)); std::cout << ")" << std::endl;
// const std::vector<uint8_t> garbage {0x0f};
// write(uart0_filestream, garbage.data(), garbage.size());
// write(uart0_filestream, encGreenData.data(), encGreenData.size());
// write(uart0_filestream, encRedData.data(), encRedData.size());
// write(uart0_filestream, encBlueData.data(), encBlueData.size());
// write(uart0_filestream, encGrayData.data(), encGrayData.size());
// write(uart0_filestream, encBlackData.data(), encBlackData.size());
// }
// {
// getchar();
const std::vector<uint8_t> encData = encode(white);
std::cout << "Writing WHITE ("; printClockSignal(encode(white)); std::cout << ")" << std::endl;
// const std::vector<uint8_t> garbage {0x0f};
// write(uart0_filestream, garbage.data(), garbage.size());
write(uart0_filestream, encData.data(), encData.size());
write(uart0_filestream, encData.data(), encData.size());
write(uart0_filestream, encData.data(), encData.size());
}
{
getchar();
const std::vector<uint8_t> encData = encode(green);
std::cout << "Writing GREEN ("; printClockSignal(encode(green)); std::cout << ")" << std::endl;
write(uart0_filestream, encData.data(), encData.size());
}
{
getchar();
const std::vector<uint8_t> encData = encode(red);
std::cout << "Writing RED ("; printClockSignal(encode(red)); std::cout << ")" << std::endl;
write(uart0_filestream, encData.data(), encData.size());
}
{
getchar();
const std::vector<uint8_t> encData = encode(blue);
std::cout << "Writing BLUE ("; printClockSignal(encode(blue)); std::cout << ")" << std::endl;
write(uart0_filestream, encData.data(), encData.size());
}
{
getchar();
const std::vector<uint8_t> encData = encode(cyan);
std::cout << "Writing CYAN? ("; printClockSignal(encode(cyan)); std::cout << ")" << std::endl;
write(uart0_filestream, encData.data(), encData.size());
}
{
getchar();
const std::vector<uint8_t> encData = encode(mix);
std::cout << "Writing MIX ("; printClockSignal(encode(mix)); std::cout << ")" << std::endl;
write(uart0_filestream, encData.data(), encData.size());
}
{
getchar();
const std::vector<uint8_t> encData = encode(black);
std::cout << "Writing BLACK ("; printClockSignal(encode(black)); std::cout << ")" << std::endl;
write(uart0_filestream, encData.data(), encData.size());
write(uart0_filestream, encData.data(), encData.size());
write(uart0_filestream, encData.data(), encData.size());
write(uart0_filestream, encData.data(), encData.size());
}
close(uart0_filestream);
return 0;
}
std::vector<uint8_t> encode(const std::vector<uint8_t> & data)
{
std::vector<uint8_t> result;
for (size_t iByte=0; iByte<data.size(); iByte+=3)
{
const uint8_t byte1 = data[iByte];
const uint8_t byte2 = data[iByte+1];
const uint8_t byte3 = data[iByte+2];
result.push_back(encode(byte1 & 0x80, byte1 & 0x40, byte1 & 0x20));
result.push_back(encode(byte1 & 0x10, byte1 & 0x08, byte1 & 0x04));
result.push_back(encode(byte1 & 0x02, byte1 & 0x01, byte2 & 0x80));
result.push_back(encode(byte2 & 0x40, byte2 & 0x20, byte2 & 0x10));
result.push_back(encode(byte2 & 0x08, byte2 & 0x04, byte2 & 0x02));
result.push_back(encode(byte2 & 0x01, byte3 & 0x80, byte3 & 0x40));
result.push_back(encode(byte3 & 0x20, byte3 & 0x10, byte3 & 0x08));
result.push_back(encode(byte3 & 0x04, byte3 & 0x02, byte3 & 0x01));
}
return result;
}
uint8_t encode(const bool bit1, const bool bit2, const bool bit3)
{
uint8_t result = 0x44; // 0100 0100
if (bit1)
result |= 0x01; // 0000 0001
if (bit2)
result |= 0x18; // 0001 1000
if (bit3)
result |= 0x80; // 1000 0000
return ~result;
}

View File

@ -1,260 +0,0 @@
// STL includes
#include <iostream>
#include <random>
// Serialport includes
#include <serial/serial.h>
int testSerialPortLib();
int testHyperionDevice(int argc, char** argv);
int testWs2812bDevice();
int main(int argc, char** argv)
{
// if (argc == 1)
// {
// return testSerialPortLib();
// }
// else
// {
// return testHyperionDevice(argc, argv);
// }
return testWs2812bDevice();
}
int testSerialPortLib()
{
serial::Serial rs232Port("/dev/ttyAMA0", 4000000);
std::default_random_engine generator;
std::uniform_int_distribution<int> distribution(1,2);
std::vector<uint8_t> data;
for (int i=0; i<9; ++i)
{
int coinFlip = distribution(generator);
if (coinFlip == 1)
{
data.push_back(0xCE);
data.push_back(0xCE);
data.push_back(0xCE);
data.push_back(0xCE);
}
else
{
data.push_back(0x8C);
data.push_back(0x8C);
data.push_back(0x8C);
data.push_back(0x8C);
}
}
std::cout << "Type 'c' to continue, 'q' or 'x' to quit: ";
while (true)
{
char c = getchar();
if (c == 'q' || c == 'x')
{
break;
}
if (c != 'c')
{
continue;
}
rs232Port.flushOutput();
rs232Port.write(data);
rs232Port.flush();
data.clear();
for (int i=0; i<9; ++i)
{
int coinFlip = distribution(generator);
if (coinFlip == 1)
{
data.push_back(0xCE);
data.push_back(0xCE);
data.push_back(0xCE);
data.push_back(0xCE);
}
else
{
data.push_back(0x8C);
data.push_back(0x8C);
data.push_back(0x8C);
data.push_back(0x8C);
}
}
}
try
{
rs232Port.close();
}
catch (const std::runtime_error& excp)
{
std::cout << "Caught exception: " << excp.what() << std::endl;
return -1;
}
return 0;
}
#include "../libsrc/leddevice/LedRs232Device.h"
class TestDevice : public LedRs232Device
{
public:
TestDevice() :
LedRs232Device("/dev/ttyAMA0", 4000000)
{
open();
}
int write(const std::vector<ColorRgb> &ledValues)
{
std::vector<uint8_t> bytes(ledValues.size() * 3 * 4);
uint8_t * bytePtr = bytes.data();
for (ColorRgb color : ledValues)
{
byte2Signal(color.green, bytePtr);
bytePtr += 4;
byte2Signal(color.red, bytePtr);
bytePtr += 4;
byte2Signal(color.blue, bytePtr);
bytePtr += 4;
}
writeBytes(bytes.size(), bytes.data());
return 0;
}
int switchOff() { return 0; };
void writeTestSequence(const std::vector<uint8_t> & data)
{
writeBytes(data.size(), data.data());
}
void byte2Signal(const uint8_t byte, uint8_t * output)
{
output[0] = bits2Signal(byte & 0x80, byte & 0x40);
output[1] = bits2Signal(byte & 0x20, byte & 0x10);
output[2] = bits2Signal(byte & 0x08, byte & 0x04);
output[3] = bits2Signal(byte & 0x02, byte & 0x01);
}
uint8_t bits2Signal(const bool bit1, const bool bit2)
{
if (bit1)
{
if (bit2)
{
return 0x8C;
}
else
{
return 0xCC;
}
}
else
{
if (bit2)
{
return 0x8E;
}
else
{
return 0xCE;
}
}
return 0x00;
}
};
int testHyperionDevice(int argc, char** argv)
{
TestDevice rs232Device;
if (argc > 1 && strncmp(argv[1], "off", 3) == 0)
{
rs232Device.write(std::vector<ColorRgb>(150, {0, 0, 0}));
return 0;
}
int loopCnt = 0;
std::cout << "Type 'c' to continue, 'q' or 'x' to quit: ";
while (true)
{
char c = getchar();
if (c == 'q' || c == 'x')
{
break;
}
if (c != 'c')
{
continue;
}
rs232Device.write(std::vector<ColorRgb>(loopCnt, {255, 255, 255}));
++loopCnt;
}
rs232Device.write(std::vector<ColorRgb>(150, {0, 0, 0}));
return 0;
}
#include "../libsrc/leddevice/LedDeviceWs2812b.h"
#include <unistd.h>
int testWs2812bDevice()
{
LedDeviceWs2812b device;
device.open();
std::cout << "Type 'c' to continue, 'q' or 'x' to quit: ";
int loopCnt = 0;
while (true)
{
// char c = getchar();
// if (c == 'q' || c == 'x')
// {
// break;
// }
// if (c != 'c')
// {
// continue;
// }
if (loopCnt%4 == 0)
device.write(std::vector<ColorRgb>(25, {255, 0, 0}));
else if (loopCnt%4 == 1)
device.write(std::vector<ColorRgb>(25, {0, 255, 0}));
else if (loopCnt%4 == 2)
device.write(std::vector<ColorRgb>(25, {0, 0, 255}));
else if (loopCnt%4 == 3)
device.write(std::vector<ColorRgb>(25, {17, 188, 66}));
++loopCnt;
usleep(200000);
if (loopCnt > 200)
{
break;
}
}
device.write(std::vector<ColorRgb>(150, {0, 0, 0}));
device.switchOff();
return 0;
}

View File

@ -1,387 +0,0 @@
#include <random>
#include <iostream>
#include <stdio.h>
#include <unistd.h> //Used for UART
#include <fcntl.h> //Used for UART
#include <termios.h> //Used for UART
#include <sys/ioctl.h>
#include <linux/serial.h>
#include <csignal>
#include <cstdint>
#include <bitset>
#include <vector>
#include <pthread.h>
#include <sched.h>
void set_realtime_priority() {
int ret;
// We'll operate on the currently running thread.
pthread_t this_thread = pthread_self();
// struct sched_param is used to store the scheduling priority
struct sched_param params;
// We'll set the priority to the maximum.
params.sched_priority = sched_get_priority_max(SCHED_FIFO);
std::cout << "Trying to set thread realtime prio = " << params.sched_priority << std::endl;
// Attempt to set thread real-time priority to the SCHED_FIFO policy
ret = pthread_setschedparam(this_thread, SCHED_FIFO, &params);
if (ret != 0) {
// Print the error
std::cout << "Unsuccessful in setting thread realtime prio (erno=" << ret << ")" << std::endl;
return;
}
// Now verify the change in thread priority
int policy = 0;
ret = pthread_getschedparam(this_thread, &policy, &params);
if (ret != 0) {
std::cout << "Couldn't retrieve real-time scheduling paramers" << std::endl;
return;
}
// Check the correct policy was applied
if(policy != SCHED_FIFO) {
std::cout << "Scheduling is NOT SCHED_FIFO!" << std::endl;
} else {
std::cout << "SCHED_FIFO OK" << std::endl;
}
// Print thread scheduling priority
std::cout << "Thread priority is " << params.sched_priority << std::endl;
}
struct ColorSignal
{
uint8_t green_1;
uint8_t green_2;
uint8_t green_3;
uint8_t green_4;
uint8_t red_1;
uint8_t red_2;
uint8_t red_3;
uint8_t red_4;
uint8_t blue_1;
uint8_t blue_2;
uint8_t blue_3;
uint8_t blue_4;
};
static ColorSignal RED_Signal = { 0xCE, 0xCE, 0xCE, 0xCE,
0xCE, 0x8C, 0x8C, 0x8C,
0xCE, 0xCE, 0xCE, 0xCE };
static ColorSignal GREEN_Signal = { 0xCE, 0x8C, 0x8C, 0x8C,
0xCE, 0xCE, 0xCE, 0xCE,
0xCE, 0xCE, 0xCE, 0xCE };
static ColorSignal BLUE_Signal = { 0xCE, 0xCE, 0xCE, 0xCE,
0xCE, 0xCE, 0xCE, 0xCE,
0xCE, 0x8C, 0x8C, 0x8C};
static ColorSignal BLACK_Signal = { 0xCE, 0xCE, 0xCE, 0xCE,
0xCE, 0xCE, 0xCE, 0xCE,
0xCE, 0xCE, 0xCE, 0xCE};
static volatile bool _running;
void signal_handler(int signum)
{
_running = false;
}
void test3bitsEncoding();
int main()
{
if (true)
{
test3bitsEncoding();
return 0;
}
_running = true;
signal(SIGTERM, &signal_handler);
//-------------------------
//----- SETUP USART 0 -----
//-------------------------
//At bootup, pins 8 and 10 are already set to UART0_TXD, UART0_RXD (ie the alt0 function) respectively
int uart0_filestream = -1;
//OPEN THE UART
//The flags (defined in fcntl.h):
// Access modes (use 1 of these):
// O_RDONLY - Open for reading only.
// O_RDWR - Open for reading and writing.
// O_WRONLY - Open for writing only.
//
// O_NDELAY / O_NONBLOCK (same function) - Enables nonblocking mode. When set read requests on the file can return immediately with a failure status
// if there is no input immediately available (instead of blocking). Likewise, write requests can also return
// immediately with a failure status if the output can't be written immediately.
//
// O_NOCTTY - When set and path identifies a terminal device, open() shall not cause the terminal device to become the controlling terminal for the process.
uart0_filestream = open("/dev/ttyAMA0", O_WRONLY | O_NOCTTY | O_NDELAY); //Open in non blocking read/write mode
if (uart0_filestream == -1)
{
//ERROR - CAN'T OPEN SERIAL PORT
printf("Error - Unable to open UART. Ensure it is not in use by another application\n");
}
// if (0)
{
//CONFIGURE THE UART
//The flags (defined in /usr/include/termios.h - see http://pubs.opengroup.org/onlinepubs/007908799/xsh/termios.h.html):
// Baud rate:- B1200, B2400, B4800, B9600, B19200, B38400, B57600, B115200, B230400, B460800, B500000, B576000, B921600, B1000000, B1152000, B1500000, B2000000, B2500000, B3000000, B3500000, B4000000
// CSIZE:- CS5, CS6, CS7, CS8
// CLOCAL - Ignore modem status lines
// CREAD - Enable receiver
// IGNPAR = Ignore characters with parity errors
// ICRNL - Map CR to NL on input (Use for ASCII comms where you want to auto correct end of line characters - don't use for bianry comms!)
// PARENB - Parity enable
// PARODD - Odd parity (else even)
struct termios options;
tcgetattr(uart0_filestream, &options);
options.c_cflag = B4000000 | CS8 | CLOCAL; //<Set baud rate
options.c_iflag = IGNPAR;
options.c_oflag = 0;
options.c_lflag = 0;
cfmakeraw(&options);
std::cout << "options.c_cflag = " << options.c_cflag << std::endl;
std::cout << "options.c_iflag = " << options.c_iflag << std::endl;
std::cout << "options.c_oflag = " << options.c_oflag << std::endl;
std::cout << "options.c_lflag = " << options.c_lflag << std::endl;
tcflush(uart0_filestream, TCIFLUSH);
tcsetattr(uart0_filestream, TCSANOW, &options);
// Let's verify configured options
tcgetattr(uart0_filestream, &options);
std::cout << "options.c_cflag = " << options.c_cflag << std::endl;
std::cout << "options.c_iflag = " << options.c_iflag << std::endl;
std::cout << "options.c_oflag = " << options.c_oflag << std::endl;
std::cout << "options.c_lflag = " << options.c_lflag << std::endl;
}
{
struct serial_struct ser;
if (-1 == ioctl(uart0_filestream, TIOCGSERIAL, &ser))
{
std::cerr << "Failed to obtian 'serial_struct' for setting custom baudrate" << std::endl;
}
std::cout << "Current divisor: " << ser.custom_divisor << " ( = " << ser.baud_base << " / 4000000" << std::endl;
// set custom divisor
ser.custom_divisor = ser.baud_base / 8000000;
// update flags
ser.flags &= ~ASYNC_SPD_MASK;
ser.flags |= ASYNC_SPD_CUST;
std::cout << "Current divisor: " << ser.custom_divisor << " ( = " << ser.baud_base << " / 8000000" << std::endl;
if (-1 == ioctl(uart0_filestream, TIOCSSERIAL, &ser))
{
std::cerr << "Failed to configure 'serial_struct' for setting custom baudrate" << std::endl;
}
// Check result
if (-1 == ioctl(uart0_filestream, TIOCGSERIAL, &ser))
{
std::cerr << "Failed to obtian 'serial_struct' for setting custom baudrate" << std::endl;
}
std::cout << "Current divisor: " << ser.custom_divisor << " ( = " << ser.baud_base << " / 4000000" << std::endl;
}
if (uart0_filestream < 0)
{
std::cerr << "Opening the device has failed" << std::endl;
return -1;
}
//----- TX BYTES -----
std::vector<ColorSignal> signalData(10, RED_Signal);
int loopCnt = 0;
std::cout << "Type 'c' to continue, 'q' or 'x' to quit: ";
while (_running)
{
char c = getchar();
if (c == 'q' || c == 'x')
{
break;
}
if (c != 'c')
{
continue;
}
set_realtime_priority();
for (int iRun=0; iRun<10; ++iRun)
{
// tcflush(uart0_filestream, TCOFLUSH);
write(uart0_filestream, signalData.data(), signalData.size()*sizeof(ColorSignal));
tcdrain(uart0_filestream);
usleep(100000);
++loopCnt;
if (loopCnt%3 == 2)
signalData = std::vector<ColorSignal>(10, GREEN_Signal);
else if(loopCnt%3 == 1)
signalData = std::vector<ColorSignal>(10, BLUE_Signal);
else if(loopCnt%3 == 0)
signalData = std::vector<ColorSignal>(10, RED_Signal);
}
}
signalData = std::vector<ColorSignal>(50, BLACK_Signal);
write(uart0_filestream, signalData.data(), signalData.size()*sizeof(ColorSignal));
//----- CLOSE THE UART -----
close(uart0_filestream);
std::cout << "Program finished" << std::endl;
return 0;
}
std::vector<uint8_t> bit3Encode(const std::vector<uint8_t> & bytes);
uint8_t bit3Encode(const bool bit_1, const bool bit_2, const bool bit_3);
void test3bitsEncoding()
{
//OPEN THE UART
// int uart0_filestream = open("/dev/ttyAMA0", O_WRONLY | O_NOCTTY | O_NDELAY);
int uart0_filestream = open("/dev/ttyUSB0", O_WRONLY | O_NOCTTY | O_NDELAY);
if (uart0_filestream == -1)
{
//ERROR - CAN'T OPEN SERIAL PORT
printf("Error - Unable to open UART. Ensure it is not in use by another application\n");
return;
}
// Configure the port
struct termios options;
tcgetattr(uart0_filestream, &options);
options.c_cflag = B2500000 | CS7 | CLOCAL;
options.c_iflag = IGNPAR;
options.c_oflag = 0;
options.c_lflag = 0;
tcflush(uart0_filestream, TCIFLUSH);
tcsetattr(uart0_filestream, TCSANOW, &options);
std::vector<uint8_t> colorRed;
for (unsigned i=0; i<10; ++i)
{
colorRed.push_back(0x00);
colorRed.push_back(0xFF);
colorRed.push_back(0x00);
}
std::vector<uint8_t> colorGreen;
for (unsigned i=0; i<10; ++i)
{
colorGreen.push_back(0xFF);
colorGreen.push_back(0x00);
colorGreen.push_back(0x00);
}
std::vector<uint8_t> colorBlue;
for (unsigned i=0; i<10; ++i)
{
colorBlue.push_back(0x00);
colorBlue.push_back(0x00);
colorBlue.push_back(0xFF);
}
std::vector<uint8_t> colorBlack;
for (unsigned i=0; i<10; ++i)
{
colorBlack.push_back(0x00);
colorBlack.push_back(0x00);
colorBlack.push_back(0x00);
}
const std::vector<uint8_t> colorRedSignal = bit3Encode(colorRed);
const std::vector<uint8_t> colorGreenSignal = bit3Encode(colorGreen);
const std::vector<uint8_t> colorBlueSignal = bit3Encode(colorBlue);
const std::vector<uint8_t> colorBlackSignal = bit3Encode(colorBlack);
for (unsigned i=0; i<100; ++i)
{
size_t res;
res = write(uart0_filestream, colorRedSignal.data(), colorRedSignal.size());
(void)res;
usleep(100000);
res = write(uart0_filestream, colorGreenSignal.data(), colorGreenSignal.size());
(void)res;
usleep(100000);
res = write(uart0_filestream, colorBlueSignal.data(), colorBlueSignal.size());
(void)res;
usleep(100000);
}
size_t res = write(uart0_filestream, colorBlackSignal.data(), colorBlackSignal.size());
(void)res;
//----- CLOSE THE UART -----
res = close(uart0_filestream);
(void)res;
std::cout << "Program finished" << std::endl;
}
std::vector<uint8_t> bit3Encode(const std::vector<uint8_t> & bytes)
{
std::vector<uint8_t> result;
for (unsigned iByte=0; iByte<bytes.size(); iByte+=3)
{
const uint8_t & byte1 = bytes[iByte];
const uint8_t & byte2 = bytes[iByte + 1];
const uint8_t & byte3 = bytes[iByte + 2];
result.push_back(bit3Encode(byte1 & 0x80, byte1 & 0x40, byte1 & 0x20));
result.push_back(bit3Encode(byte1 & 0x10, byte1 & 0x08, byte1 & 0x04));
result.push_back(bit3Encode(byte1 & 0x02, byte1 & 0x01, byte2 & 0x80));
result.push_back(bit3Encode(byte2 & 0x40, byte2 & 0x20, byte2 & 0x10));
result.push_back(bit3Encode(byte2 & 0x08, byte2 & 0x04, byte2 & 0x02));
result.push_back(bit3Encode(byte2 & 0x01, byte3 & 0x80, byte3 & 0x40));
result.push_back(bit3Encode(byte3 & 0x20, byte3 & 0x10, byte3 & 0x08));
result.push_back(bit3Encode(byte3 & 0x04, byte3 & 0x02, byte3 & 0x01));
}
return result;
}
uint8_t bit3Encode(const bool bit_1, const bool bit_2, const bool bit_3)
{
// Bit index(default):1 2 3
// | | |
// default value (1) 00 100 10 (0)
//
// Reversed value (1) 01 001 00 (0)
// | | |
// Bit index (rev): 3 2 1
uint8_t result = 0x24;
if(bit_1)
{
result |= 0x01;
}
if (bit_2)
{
result |= 0x08;
}
if (bit_3)
{
result |= 0x40;
}
return ~result;
}

View File

@ -10,7 +10,7 @@
#include <getoptPlusPlus/getoptpp.h>
// Dispmanx grabber includes
#include <dispmanx-grabber/DispmanxFrameGrabber.h>
#include <grabber/dispmanx/DispmanxFrameGrabber.h>
using namespace vlofgren;