mirror of
https://github.com/hyperion-project/hyperion.ng.git
synced 2023-10-10 13:36:59 +02:00
Merge remote-tracking branch 'origin/master'
Former-commit-id: b4eb2e7a1c78d107cb7bc69a04b1cff694f6e308
This commit is contained in:
commit
f12bffb44f
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,3 +1,5 @@
|
|||||||
/*.user
|
/*.user
|
||||||
/build
|
/build
|
||||||
/build-x86
|
/build-x86
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
@ -7,19 +7,22 @@ cmake_minimum_required(VERSION 2.8)
|
|||||||
#set(CMAKE_TOOLCHAIN_FILE /opt/raspberrypi/Toolchain-RaspberryPi.cmake)
|
#set(CMAKE_TOOLCHAIN_FILE /opt/raspberrypi/Toolchain-RaspberryPi.cmake)
|
||||||
|
|
||||||
# set the build options
|
# set the build options
|
||||||
option (ENABLE_DISPMANX "Enable the RPi dispmanx grabber" ON)
|
option(ENABLE_DISPMANX "Enable the RPi dispmanx grabber" ON)
|
||||||
option (ENABLE_SPIDEV "Enable the SPIDEV device" ON)
|
|
||||||
|
|
||||||
message(STATUS "ENABLE_DISPMANX = " ${ENABLE_DISPMANX})
|
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})
|
message(STATUS "ENABLE_V4L2 = " ${ENABLE_V4L2})
|
||||||
|
|
||||||
|
option(ENABLE_TINKERFORGE "Enable the TINKERFORGE device" ON)
|
||||||
|
message(STATUS "ENABLE_TINKERFORGE = " ${ENABLE_TINKERFORGE})
|
||||||
|
|
||||||
# Createt the configuration file
|
# Createt the configuration file
|
||||||
# configure a header file to pass some of the CMake settings
|
# configure a header file to pass some of the CMake settings
|
||||||
# to the source code
|
# 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}")
|
include_directories("${PROJECT_BINARY_DIR}")
|
||||||
|
|
||||||
# Add project specific cmake modules (find, etc)
|
# 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(libusb-1.0 REQUIRED)
|
||||||
find_package(Threads REQUIRED)
|
find_package(Threads REQUIRED)
|
||||||
|
|
||||||
|
if (ENABLE_TINKERFORGE)
|
||||||
|
#find_package(libtinkerforge-1.0 REQUIRED)
|
||||||
|
endif (ENABLE_TINKERFORGE)
|
||||||
|
|
||||||
include(${QT_USE_FILE})
|
include(${QT_USE_FILE})
|
||||||
add_definitions(${QT_DEFINITIONS})
|
add_definitions(${QT_DEFINITIONS})
|
||||||
# TODO[TvdZ]: This linking directory should only be added if we are cross compiling
|
# TODO[TvdZ]: This linking directory should only be added if we are cross compiling
|
||||||
|
@ -8,3 +8,6 @@
|
|||||||
|
|
||||||
// Define to enable the spi-device
|
// Define to enable the spi-device
|
||||||
#cmakedefine ENABLE_SPIDEV
|
#cmakedefine ENABLE_SPIDEV
|
||||||
|
|
||||||
|
// Define to enable the spi-device
|
||||||
|
#cmakedefine ENABLE_TINKERFORGE
|
||||||
|
@ -7,22 +7,27 @@ DAEMON=hyperiond
|
|||||||
DAEMONOPTS="/etc/hyperion.config.json"
|
DAEMONOPTS="/etc/hyperion.config.json"
|
||||||
DAEMON_PATH="/usr/bin"
|
DAEMON_PATH="/usr/bin"
|
||||||
|
|
||||||
NAME=$DEAMON
|
NAME=$DAEMON
|
||||||
DESC="Hyperion ambilight server"
|
DESC="Hyperion ambilight server"
|
||||||
PIDFILE=/var/run/$NAME.pid
|
PIDFILE=/var/run/$NAME.pid
|
||||||
SCRIPTNAME=/etc/init.d/$NAME
|
SCRIPTNAME=/etc/init.d/$NAME
|
||||||
|
|
||||||
case "$1" in
|
case "$1" in
|
||||||
start)
|
start)
|
||||||
printf "%-50s" "Starting $NAME..."
|
if [ $(pgrep -l $NAME |wc -l) = 1 ]
|
||||||
cd $DAEMON_PATH
|
then
|
||||||
PID=`$DAEMON $DAEMONOPTS > /dev/null 2>&1 & echo $!`
|
printf "%-50s\n" "Already running..."
|
||||||
#echo "Saving PID" $PID " to " $PIDFILE
|
exit 1
|
||||||
if [ -z $PID ]; then
|
|
||||||
printf "%s\n" "Fail"
|
|
||||||
else
|
else
|
||||||
echo $PID > $PIDFILE
|
printf "%-50s" "Starting $NAME..."
|
||||||
printf "%s\n" "Ok"
|
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
|
fi
|
||||||
;;
|
;;
|
||||||
status)
|
status)
|
||||||
@ -39,24 +44,31 @@ status)
|
|||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
stop)
|
stop)
|
||||||
printf "%-50s" "Stopping $NAME"
|
if [ -f $PIDFILE ]
|
||||||
PID=`cat $PIDFILE`
|
then
|
||||||
cd $DAEMON_PATH
|
printf "%-50s" "Stopping $NAME"
|
||||||
if [ -f $PIDFILE ]; then
|
PID=`cat $PIDFILE`
|
||||||
kill -HUP $PID
|
cd $DAEMON_PATH
|
||||||
printf "%s\n" "Ok"
|
if [ -f $PIDFILE ]; then
|
||||||
rm -f $PIDFILE
|
kill -HUP $PID
|
||||||
|
printf "%s\n" "Ok"
|
||||||
|
rm -f $PIDFILE
|
||||||
|
else
|
||||||
|
printf "%s\n" "pidfile not found"
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
printf "%s\n" "pidfile not found"
|
printf "%-50s\n" "No PID file $NAME not running?"
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
|
|
||||||
restart)
|
restart)
|
||||||
$0 stop
|
$0 stop
|
||||||
$0 start
|
$0 start
|
||||||
;;
|
;;
|
||||||
|
|
||||||
*)
|
*)
|
||||||
echo "Usage: $0 {status|start|stop|restart}"
|
echo "Usage: $0 {status|start|stop|restart}"
|
||||||
exit 1
|
exit 1
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
exit 0
|
||||||
|
@ -34,13 +34,13 @@ fi
|
|||||||
echo 'Downloading hyperion'
|
echo 'Downloading hyperion'
|
||||||
if [ $IS_OPENELEC -eq 1 ]; then
|
if [ $IS_OPENELEC -eq 1 ]; then
|
||||||
# OpenELEC has a readonly file system. Use alternative location
|
# 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 -L --get https://raw.githubusercontent.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.deps.openelec-rpi.tar.gz | tar -C /storage/hyperion/bin -xz
|
||||||
|
|
||||||
# modify the default config to have a correct effect path
|
# modify the default config to have a correct effect path
|
||||||
sed -i 's:/opt:/storage:g' /storage/hyperion/config/hyperion.config.json
|
sed -i 's:/opt:/storage:g' /storage/hyperion/config/hyperion.config.json
|
||||||
else
|
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
|
fi
|
||||||
|
|
||||||
# create links to the binaries
|
# create links to the binaries
|
||||||
@ -68,9 +68,9 @@ fi
|
|||||||
if [ $USE_INITCTL -eq 1 ]; then
|
if [ $USE_INITCTL -eq 1 ]; then
|
||||||
echo 'Installing initctl script'
|
echo 'Installing initctl script'
|
||||||
if [ $IS_RASPBMC -eq 1 ]; then
|
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
|
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
|
fi
|
||||||
elif [ $USE_SERVICE -eq 1 ]; then
|
elif [ $USE_SERVICE -eq 1 ]; then
|
||||||
echo 'Installing startup script in init.d'
|
echo 'Installing startup script in init.d'
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
{
|
{
|
||||||
"name" : "MyPi",
|
"name" : "MyPi",
|
||||||
"type" : "adalight",
|
"type" : "adalight",
|
||||||
"output" : "/dev/ttyUSB0",
|
"output" : "/dev/ttyUSB0",
|
||||||
"rate" : 115200,
|
"rate" : 115200,
|
||||||
"colorOrder" : "rgb"
|
"colorOrder" : "rgb"
|
||||||
},
|
},
|
||||||
@ -363,7 +363,7 @@
|
|||||||
{
|
{
|
||||||
"paths" :
|
"paths" :
|
||||||
[
|
[
|
||||||
"/opt/hyperion/effects"
|
"/home/dincs/projects/hyperion/effects"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
||||||
|
5
dependencies/build/CMakeLists.txt
vendored
5
dependencies/build/CMakeLists.txt
vendored
@ -1,5 +1,6 @@
|
|||||||
|
|
||||||
add_subdirectory(jsoncpp)
|
|
||||||
add_subdirectory(getoptPlusPlus)
|
add_subdirectory(getoptPlusPlus)
|
||||||
add_subdirectory(serial)
|
|
||||||
add_subdirectory(hidapi)
|
add_subdirectory(hidapi)
|
||||||
|
add_subdirectory(jsoncpp)
|
||||||
|
add_subdirectory(serial)
|
||||||
|
add_subdirectory(tinkerforge)
|
||||||
|
@ -280,7 +280,6 @@ void PresettableUniquelySwitchable::preset() {
|
|||||||
template<>
|
template<>
|
||||||
PODParameter<string>::PODParameter(char shortOption, const char *longOption,
|
PODParameter<string>::PODParameter(char shortOption, const char *longOption,
|
||||||
const char* description) : CommonParameter<PresettableUniquelySwitchable>(shortOption, longOption, description) {
|
const char* description) : CommonParameter<PresettableUniquelySwitchable>(shortOption, longOption, description) {
|
||||||
preset();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
14
dependencies/build/tinkerforge/CMakeLists.txt
vendored
Normal file
14
dependencies/build/tinkerforge/CMakeLists.txt
vendored
Normal 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)
|
373
dependencies/build/tinkerforge/bricklet_led_strip.c
vendored
Normal file
373
dependencies/build/tinkerforge/bricklet_led_strip.c
vendored
Normal 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;
|
||||||
|
}
|
2013
dependencies/build/tinkerforge/ip_connection.c
vendored
Normal file
2013
dependencies/build/tinkerforge/ip_connection.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
301
dependencies/include/tinkerforge/bricklet_led_strip.h
vendored
Normal file
301
dependencies/include/tinkerforge/bricklet_led_strip.h
vendored
Normal 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
|
630
dependencies/include/tinkerforge/ip_connection.h
vendored
Normal file
630
dependencies/include/tinkerforge/ip_connection.h
vendored
Normal 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
|
BIN
dependencies/tinkerforge_c_bindings_2_0_13.zip
vendored
Normal file
BIN
dependencies/tinkerforge_c_bindings_2_0_13.zip
vendored
Normal file
Binary file not shown.
@ -1 +1 @@
|
|||||||
f3afa39f64294a9ce71af8e424e4bab72d2b29cb
|
9546e335179f732ff68ea9bc47020a19e4b6f44c
|
@ -4,6 +4,7 @@
|
|||||||
"args" :
|
"args" :
|
||||||
{
|
{
|
||||||
"speed" : 1.0,
|
"speed" : 1.0,
|
||||||
"fadeFactor" : 0.7
|
"fadeFactor" : 0.7,
|
||||||
|
"color" : [255,0,0]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,9 +2,10 @@ import hyperion
|
|||||||
import time
|
import time
|
||||||
import colorsys
|
import colorsys
|
||||||
|
|
||||||
# Get the rotation time
|
# Get the parameters
|
||||||
speed = float(hyperion.args.get('speed', 1.0))
|
speed = float(hyperion.args.get('speed', 1.0))
|
||||||
fadeFactor = float(hyperion.args.get('fadeFactor', 0.7))
|
fadeFactor = float(hyperion.args.get('fadeFactor', 0.7))
|
||||||
|
color = hyperion.args.get('color', (255,0,0))
|
||||||
|
|
||||||
# Check parameters
|
# Check parameters
|
||||||
speed = max(0.0001, speed)
|
speed = max(0.0001, speed)
|
||||||
@ -13,7 +14,9 @@ fadeFactor = max(0.0, min(fadeFactor, 1.0))
|
|||||||
# Initialize the led data
|
# Initialize the led data
|
||||||
width = 25
|
width = 25
|
||||||
imageData = bytearray(width * (0,0,0))
|
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
|
# Calculate the sleep time and rotation increment
|
||||||
increment = 1
|
increment = 1
|
||||||
@ -41,9 +44,13 @@ while not hyperion.abort():
|
|||||||
# Fade the old data
|
# Fade the old data
|
||||||
for j in range(width):
|
for j in range(width):
|
||||||
imageData[3*j] = int(fadeFactor * imageData[3*j])
|
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
|
# 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
|
# Sleep for a while
|
||||||
time.sleep(sleepTime)
|
time.sleep(sleepTime)
|
||||||
|
16
effects/mood-blobs-cold.json
Normal file
16
effects/mood-blobs-cold.json
Normal 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
|
||||||
|
}
|
||||||
|
}
|
16
effects/mood-blobs-full.json
Normal file
16
effects/mood-blobs-full.json
Normal 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
|
||||||
|
}
|
||||||
|
}
|
16
effects/mood-blobs-warm.json
Normal file
16
effects/mood-blobs-warm.json
Normal 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
|
||||||
|
}
|
||||||
|
}
|
@ -6,14 +6,31 @@ import math
|
|||||||
# Get the parameters
|
# Get the parameters
|
||||||
rotationTime = float(hyperion.args.get('rotationTime', 20.0))
|
rotationTime = float(hyperion.args.get('rotationTime', 20.0))
|
||||||
color = hyperion.args.get('color', (0,0,255))
|
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))
|
blobs = int(hyperion.args.get('blobs', 5))
|
||||||
reverse = bool(hyperion.args.get('reverse', False))
|
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
|
# Check parameters
|
||||||
rotationTime = max(0.1, rotationTime)
|
rotationTime = max(0.1, rotationTime)
|
||||||
hueChange = max(0.0, min(abs(hueChange), .5))
|
hueChange = max(0.0, min(abs(hueChange), .5))
|
||||||
blobs = max(1, blobs)
|
blobs = max(1, blobs)
|
||||||
|
baseColorChangeRate = max(0, baseColorChangeRate) # > 0
|
||||||
|
|
||||||
# Calculate the color data
|
# Calculate the color data
|
||||||
baseHsv = colorsys.rgb_to_hsv(color[0]/255.0, color[1]/255.0, color[2]/255.0)
|
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
|
sleepTime = 0.1
|
||||||
amplitudePhaseIncrement = blobs * math.pi * sleepTime / rotationTime
|
amplitudePhaseIncrement = blobs * math.pi * sleepTime / rotationTime
|
||||||
colorDataIncrement = 3
|
colorDataIncrement = 3
|
||||||
|
baseColorChangeRate /= sleepTime
|
||||||
|
|
||||||
# Switch direction if needed
|
# Switch direction if needed
|
||||||
if reverse:
|
if reverse:
|
||||||
@ -39,23 +57,58 @@ colors = bytearray(hyperion.ledCount * (0,0,0))
|
|||||||
# Start the write data loop
|
# Start the write data loop
|
||||||
amplitudePhase = 0.0
|
amplitudePhase = 0.0
|
||||||
rotateColors = False
|
rotateColors = False
|
||||||
|
baseColorChangeStepCount = 0
|
||||||
|
baseHSVValue = baseHsv[0]
|
||||||
|
numberOfRotates = 0
|
||||||
|
|
||||||
while not hyperion.abort():
|
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)
|
|
||||||
|
|
||||||
# set colors
|
# move the basecolor
|
||||||
hyperion.setColor(colors)
|
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)
|
||||||
|
|
||||||
# increment the phase
|
baseHSVValue = (baseHSVValue + baseColorChangeIncreaseValue) % 1.0
|
||||||
amplitudePhase = (amplitudePhase + amplitudePhaseIncrement) % (2*math.pi)
|
|
||||||
|
|
||||||
if rotateColors:
|
# update color values
|
||||||
colorData = colorData[-colorDataIncrement:] + colorData[:-colorDataIncrement]
|
colorData = bytearray()
|
||||||
rotateColors = not rotateColors
|
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])))
|
||||||
|
|
||||||
# sleep for a while
|
# set correct rotation after reinitialisation of the array
|
||||||
time.sleep(sleepTime)
|
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)
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
// Utils includes
|
// Utils includes
|
||||||
@ -126,7 +125,7 @@ namespace hyperion
|
|||||||
inline bool isBlack(const Pixel_T & color)
|
inline bool isBlack(const Pixel_T & color)
|
||||||
{
|
{
|
||||||
// Return the simple compare of the color against black
|
// 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:
|
private:
|
||||||
|
@ -55,5 +55,5 @@ private:
|
|||||||
|
|
||||||
std::list<Effect *> _activeEffects;
|
std::list<Effect *> _activeEffects;
|
||||||
|
|
||||||
PyThreadState * _mainThreadState;
|
PyThreadState * _mainThreadState;
|
||||||
};
|
};
|
||||||
|
@ -73,7 +73,7 @@ private:
|
|||||||
const int _updateInterval_ms;
|
const int _updateInterval_ms;
|
||||||
/// The timeout of the led colors [ms]
|
/// The timeout of the led colors [ms]
|
||||||
const int _timeout_ms;
|
const int _timeout_ms;
|
||||||
/// The priority of the led colors [ms]
|
/// The priority of the led colors
|
||||||
const int _priority;
|
const int _priority;
|
||||||
|
|
||||||
/// The timer for generating events with the specified update rate
|
/// The timer for generating events with the specified update rate
|
36
include/grabber/PixelFormat.h
Normal file
36
include/grabber/PixelFormat.h
Normal 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;
|
||||||
|
}
|
@ -4,31 +4,30 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
// Qt includes
|
||||||
|
#include <QObject>
|
||||||
|
#include <QSocketNotifier>
|
||||||
|
|
||||||
// util includes
|
// util includes
|
||||||
#include <utils/Image.h>
|
#include <utils/Image.h>
|
||||||
#include <utils/ColorRgb.h>
|
#include <utils/ColorRgb.h>
|
||||||
|
#include <utils/VideoMode.h>
|
||||||
|
|
||||||
|
// grabber includes
|
||||||
|
#include <grabber/VideoStandard.h>
|
||||||
|
#include <grabber/PixelFormat.h>
|
||||||
|
|
||||||
/// Capture class for V4L2 devices
|
/// Capture class for V4L2 devices
|
||||||
///
|
///
|
||||||
/// @see http://linuxtv.org/downloads/v4l-dvb-apis/capture-example.html
|
/// @see http://linuxtv.org/downloads/v4l-dvb-apis/capture-example.html
|
||||||
class V4L2Grabber
|
class V4L2Grabber : public QObject
|
||||||
{
|
{
|
||||||
public:
|
Q_OBJECT
|
||||||
typedef void (*ImageCallback)(void * arg, const Image<ColorRgb> & image);
|
|
||||||
|
|
||||||
enum VideoStandard {
|
|
||||||
PAL, NTSC, NO_CHANGE
|
|
||||||
};
|
|
||||||
|
|
||||||
enum Mode3D {
|
|
||||||
MODE_NONE, MODE_3DSBS, MODE_3DTAB
|
|
||||||
};
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
V4L2Grabber(
|
V4L2Grabber(const std::string & device,
|
||||||
const std::string & device,
|
|
||||||
int input,
|
int input,
|
||||||
VideoStandard videoStandard,
|
VideoStandard videoStandard, PixelFormat pixelFormat,
|
||||||
int width,
|
int width,
|
||||||
int height,
|
int height,
|
||||||
int frameDecimation,
|
int frameDecimation,
|
||||||
@ -36,21 +35,29 @@ public:
|
|||||||
int verticalPixelDecimation);
|
int verticalPixelDecimation);
|
||||||
virtual ~V4L2Grabber();
|
virtual ~V4L2Grabber();
|
||||||
|
|
||||||
|
public slots:
|
||||||
void setCropping(int cropLeft,
|
void setCropping(int cropLeft,
|
||||||
int cropRight,
|
int cropRight,
|
||||||
int cropTop,
|
int cropTop,
|
||||||
int cropBottom);
|
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 start();
|
||||||
|
|
||||||
void capture(int frameCount = -1);
|
|
||||||
|
|
||||||
void stop();
|
void stop();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void newFrame(const Image<ColorRgb> & image);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
int read_frame();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void open_device();
|
void open_device();
|
||||||
|
|
||||||
@ -70,8 +77,6 @@ private:
|
|||||||
|
|
||||||
void stop_capturing();
|
void stop_capturing();
|
||||||
|
|
||||||
int read_frame();
|
|
||||||
|
|
||||||
bool process_image(const void *p, int size);
|
bool process_image(const void *p, int size);
|
||||||
|
|
||||||
void process_image(const uint8_t *p);
|
void process_image(const uint8_t *p);
|
||||||
@ -100,9 +105,10 @@ private:
|
|||||||
int _fileDescriptor;
|
int _fileDescriptor;
|
||||||
std::vector<buffer> _buffers;
|
std::vector<buffer> _buffers;
|
||||||
|
|
||||||
uint32_t _pixelFormat;
|
PixelFormat _pixelFormat;
|
||||||
int _width;
|
int _width;
|
||||||
int _height;
|
int _height;
|
||||||
|
int _frameByteSize;
|
||||||
int _cropLeft;
|
int _cropLeft;
|
||||||
int _cropRight;
|
int _cropRight;
|
||||||
int _cropTop;
|
int _cropTop;
|
||||||
@ -110,11 +116,14 @@ private:
|
|||||||
int _frameDecimation;
|
int _frameDecimation;
|
||||||
int _horizontalPixelDecimation;
|
int _horizontalPixelDecimation;
|
||||||
int _verticalPixelDecimation;
|
int _verticalPixelDecimation;
|
||||||
|
int _noSignalCounterThreshold;
|
||||||
|
|
||||||
Mode3D _mode3D;
|
ColorRgb _noSignalThresholdColor;
|
||||||
|
|
||||||
|
VideoMode _mode3D;
|
||||||
|
|
||||||
int _currentFrame;
|
int _currentFrame;
|
||||||
|
int _noSignalCounter;
|
||||||
|
|
||||||
ImageCallback _callback;
|
QSocketNotifier * _streamNotifier;
|
||||||
void * _callbackArg;
|
|
||||||
};
|
};
|
74
include/grabber/V4L2Wrapper.h
Normal file
74
include/grabber/V4L2Wrapper.h
Normal 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;
|
||||||
|
};
|
31
include/grabber/VideoStandard.h
Normal file
31
include/grabber/VideoStandard.h
Normal 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;
|
||||||
|
}
|
@ -37,6 +37,9 @@ public:
|
|||||||
///
|
///
|
||||||
void setSize(const unsigned width, const unsigned height);
|
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
|
/// 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.
|
/// if required and call the image-to-leds mapping to determine the mean color per led.
|
||||||
@ -142,7 +145,7 @@ private:
|
|||||||
const LedString _ledString;
|
const LedString _ledString;
|
||||||
|
|
||||||
/// Flag the enables(true)/disabled(false) blackborder detector
|
/// Flag the enables(true)/disabled(false) blackborder detector
|
||||||
const bool _enableBlackBorderRemoval;
|
bool _enableBlackBorderRemoval;
|
||||||
|
|
||||||
/// The processor for black border detection
|
/// The processor for black border detection
|
||||||
hyperion::BlackBorderProcessor * _borderProcessor;
|
hyperion::BlackBorderProcessor * _borderProcessor;
|
||||||
|
@ -48,3 +48,16 @@ inline std::ostream& operator<<(std::ostream& os, const ColorRgb& color)
|
|||||||
os << "{" << unsigned(color.red) << "," << unsigned(color.green) << "," << unsigned(color.blue) << "}";
|
os << "{" << unsigned(color.red) << "," << unsigned(color.green) << "," << unsigned(color.blue) << "}";
|
||||||
return os;
|
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);
|
||||||
|
}
|
||||||
|
@ -13,6 +13,18 @@ public:
|
|||||||
|
|
||||||
typedef Pixel_T pixel_type;
|
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
|
/// Constructor for an image with specified width and height
|
||||||
///
|
///
|
||||||
@ -22,8 +34,8 @@ public:
|
|||||||
Image(const unsigned width, const unsigned height) :
|
Image(const unsigned width, const unsigned height) :
|
||||||
_width(width),
|
_width(width),
|
||||||
_height(height),
|
_height(height),
|
||||||
_pixels(new Pixel_T[width*height + 1]),
|
_pixels(new Pixel_T[width * height + 1]),
|
||||||
_endOfPixels(_pixels + width*height)
|
_endOfPixels(_pixels + width * height)
|
||||||
{
|
{
|
||||||
memset(_pixels, 0, (_width*_height+1)*sizeof(Pixel_T));
|
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) :
|
Image(const unsigned width, const unsigned height, const Pixel_T background) :
|
||||||
_width(width),
|
_width(width),
|
||||||
_height(height),
|
_height(height),
|
||||||
_pixels(new Pixel_T[width*height + 1]),
|
_pixels(new Pixel_T[width * height + 1]),
|
||||||
_endOfPixels(_pixels + width*height)
|
_endOfPixels(_pixels + width * height)
|
||||||
{
|
{
|
||||||
std::fill(_pixels, _endOfPixels, background);
|
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
|
/// Destructor
|
||||||
///
|
///
|
||||||
|
10
include/utils/Sleep.h
Normal file
10
include/utils/Sleep.h
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QThread>
|
||||||
|
|
||||||
|
class Sleep : protected QThread {
|
||||||
|
public:
|
||||||
|
static inline void msleep(unsigned long msecs) {
|
||||||
|
QThread::msleep(msecs);
|
||||||
|
}
|
||||||
|
};
|
@ -1,5 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enumeration of the possible modes in which video can be playing (2D, 3D)
|
* Enumeration of the possible modes in which video can be playing (2D, 3D)
|
||||||
*/
|
*/
|
||||||
@ -9,3 +12,21 @@ enum VideoMode
|
|||||||
VIDEO_3DSBS,
|
VIDEO_3DSBS,
|
||||||
VIDEO_3DTAB
|
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;
|
||||||
|
}
|
||||||
|
@ -38,7 +38,7 @@ public:
|
|||||||
/// @param grabPhoto Whether or not to grab when the XBMC photo player is playing
|
/// @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 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 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
|
/// @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);
|
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
|
/// The JSON-RPC message to check the screensaver
|
||||||
const QString _checkScreensaverRequest;
|
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
|
/// The QT TCP Socket with connection to XBMC
|
||||||
QTcpSocket _socket;
|
QTcpSocket _socket;
|
||||||
|
|
||||||
@ -111,7 +117,7 @@ private:
|
|||||||
/// Flag indicating whether or not to grab when XBMC is playing nothing (in menu)
|
/// Flag indicating whether or not to grab when XBMC is playing nothing (in menu)
|
||||||
const bool _grabMenu;
|
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;
|
const bool _grabScreensaver;
|
||||||
|
|
||||||
/// Flag indicating wheter or not to enable the detection of 3D movies playing
|
/// Flag indicating wheter or not to enable the detection of 3D movies playing
|
||||||
@ -125,4 +131,7 @@ private:
|
|||||||
|
|
||||||
/// Previous emitted video mode
|
/// Previous emitted video mode
|
||||||
VideoMode _previousVideoMode;
|
VideoMode _previousVideoMode;
|
||||||
|
|
||||||
|
/// XBMC version number
|
||||||
|
int _xbmcVersion;
|
||||||
};
|
};
|
||||||
|
@ -12,7 +12,4 @@ add_subdirectory(leddevice)
|
|||||||
add_subdirectory(utils)
|
add_subdirectory(utils)
|
||||||
add_subdirectory(xbmcvideochecker)
|
add_subdirectory(xbmcvideochecker)
|
||||||
add_subdirectory(effectengine)
|
add_subdirectory(effectengine)
|
||||||
|
add_subdirectory(grabber)
|
||||||
if (ENABLE_DISPMANX)
|
|
||||||
add_subdirectory(dispmanx-grabber)
|
|
||||||
endif (ENABLE_DISPMANX)
|
|
||||||
|
@ -28,9 +28,5 @@ add_library(boblightserver
|
|||||||
|
|
||||||
target_link_libraries(boblightserver
|
target_link_libraries(boblightserver
|
||||||
hyperion
|
hyperion
|
||||||
hyperion-utils)
|
hyperion-utils
|
||||||
|
${QT_LIBRARIES})
|
||||||
qt4_use_modules(boblightserver
|
|
||||||
Core
|
|
||||||
Gui
|
|
||||||
Network)
|
|
||||||
|
@ -19,9 +19,39 @@ PyMethodDef Effect::effectMethods[] = {
|
|||||||
{NULL, NULL, 0, NULL}
|
{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(),
|
QThread(),
|
||||||
|
_mainThreadState(mainThreadState),
|
||||||
_priority(priority),
|
_priority(priority),
|
||||||
_timeout(timeout),
|
_timeout(timeout),
|
||||||
_script(script),
|
_script(script),
|
||||||
@ -34,6 +64,9 @@ Effect::Effect(int priority, int timeout, const std::string & script, const Json
|
|||||||
{
|
{
|
||||||
_colors.resize(_imageProcessor->getLedCount(), ColorRgb::BLACK);
|
_colors.resize(_imageProcessor->getLedCount(), ColorRgb::BLACK);
|
||||||
|
|
||||||
|
// disable the black border detector for effects
|
||||||
|
_imageProcessor->enableBalckBorderDetector(false);
|
||||||
|
|
||||||
// connect the finished signal
|
// connect the finished signal
|
||||||
connect(this, SIGNAL(finished()), this, SLOT(effectFinished()));
|
connect(this, SIGNAL(finished()), this, SLOT(effectFinished()));
|
||||||
}
|
}
|
||||||
@ -44,20 +77,26 @@ Effect::~Effect()
|
|||||||
|
|
||||||
void Effect::run()
|
void Effect::run()
|
||||||
{
|
{
|
||||||
|
// switch to the main thread state and acquire the GIL
|
||||||
|
PyEval_RestoreThread(_mainThreadState);
|
||||||
|
|
||||||
// Initialize a new thread state
|
// Initialize a new thread state
|
||||||
PyEval_AcquireLock(); // Get the GIL
|
|
||||||
_interpreterThreadState = Py_NewInterpreter();
|
_interpreterThreadState = Py_NewInterpreter();
|
||||||
|
|
||||||
// add methods extra builtin methods to the interpreter
|
// import the buildtin Hyperion module
|
||||||
PyObject * thisCapsule = PyCapsule_New(this, nullptr, nullptr);
|
PyObject * module = PyImport_ImportModule("hyperion");
|
||||||
PyObject * module = Py_InitModule4("hyperion", effectMethods, nullptr, thisCapsule, PYTHON_API_VERSION);
|
|
||||||
|
// 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
|
// add ledCount variable to the interpreter
|
||||||
PyObject_SetAttrString(module, "ledCount", Py_BuildValue("i", _imageProcessor->getLedCount()));
|
PyObject_SetAttrString(module, "ledCount", Py_BuildValue("i", _imageProcessor->getLedCount()));
|
||||||
|
|
||||||
// add a args variable to the interpreter
|
// add a args variable to the interpreter
|
||||||
PyObject_SetAttrString(module, "args", json2python(_args));
|
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
|
// Set the end time if applicable
|
||||||
if (_timeout > 0)
|
if (_timeout > 0)
|
||||||
@ -119,19 +158,23 @@ PyObject *Effect::json2python(const Json::Value &json) const
|
|||||||
return Py_BuildValue("s", json.asCString());
|
return Py_BuildValue("s", json.asCString());
|
||||||
case Json::objectValue:
|
case Json::objectValue:
|
||||||
{
|
{
|
||||||
PyObject * obj = PyDict_New();
|
PyObject * dict= PyDict_New();
|
||||||
for (Json::Value::iterator i = json.begin(); i != json.end(); ++i)
|
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:
|
case Json::arrayValue:
|
||||||
{
|
{
|
||||||
PyObject * list = PyList_New(json.size());
|
PyObject * list = PyList_New(json.size());
|
||||||
for (Json::Value::iterator i = json.begin(); i != json.end(); ++i)
|
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;
|
return list;
|
||||||
}
|
}
|
||||||
@ -144,7 +187,7 @@ PyObject *Effect::json2python(const Json::Value &json) const
|
|||||||
PyObject* Effect::wrapSetColor(PyObject *self, PyObject *args)
|
PyObject* Effect::wrapSetColor(PyObject *self, PyObject *args)
|
||||||
{
|
{
|
||||||
// get the effect
|
// get the effect
|
||||||
Effect * effect = getEffect(self);
|
Effect * effect = getEffect();
|
||||||
|
|
||||||
// check if we have aborted already
|
// check if we have aborted already
|
||||||
if (effect->_abortRequested)
|
if (effect->_abortRequested)
|
||||||
@ -229,7 +272,7 @@ PyObject* Effect::wrapSetColor(PyObject *self, PyObject *args)
|
|||||||
PyObject* Effect::wrapSetImage(PyObject *self, PyObject *args)
|
PyObject* Effect::wrapSetImage(PyObject *self, PyObject *args)
|
||||||
{
|
{
|
||||||
// get the effect
|
// get the effect
|
||||||
Effect * effect = getEffect(self);
|
Effect * effect = getEffect();
|
||||||
|
|
||||||
// check if we have aborted already
|
// check if we have aborted already
|
||||||
if (effect->_abortRequested)
|
if (effect->_abortRequested)
|
||||||
@ -292,7 +335,7 @@ PyObject* Effect::wrapSetImage(PyObject *self, PyObject *args)
|
|||||||
|
|
||||||
PyObject* Effect::wrapAbort(PyObject *self, PyObject *)
|
PyObject* Effect::wrapAbort(PyObject *self, PyObject *)
|
||||||
{
|
{
|
||||||
Effect * effect = getEffect(self);
|
Effect * effect = getEffect();
|
||||||
|
|
||||||
// Test if the effect has reached it end time
|
// Test if the effect has reached it end time
|
||||||
if (effect->_timeout > 0 && QDateTime::currentMSecsSinceEpoch() > effect->_endTime)
|
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);
|
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
|
// extract the module from the runtime
|
||||||
return reinterpret_cast<Effect *>(PyCapsule_GetPointer(self, nullptr));
|
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;
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ class Effect : public QThread
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
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 ~Effect();
|
||||||
|
|
||||||
virtual void run();
|
virtual void run();
|
||||||
@ -23,6 +23,9 @@ public:
|
|||||||
|
|
||||||
bool isAbortRequested() const;
|
bool isAbortRequested() const;
|
||||||
|
|
||||||
|
/// This function registers the extension module in Python
|
||||||
|
static void registerHyperionExtensionModule();
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void abort();
|
void abort();
|
||||||
|
|
||||||
@ -38,13 +41,22 @@ private:
|
|||||||
PyObject * json2python(const Json::Value & json) const;
|
PyObject * json2python(const Json::Value & json) const;
|
||||||
|
|
||||||
// Wrapper methods for Python interpreter extra buildin methods
|
// Wrapper methods for Python interpreter extra buildin methods
|
||||||
static PyMethodDef effectMethods[];
|
static PyMethodDef effectMethods[];
|
||||||
static PyObject* wrapSetColor(PyObject *self, PyObject *args);
|
static PyObject* wrapSetColor(PyObject *self, PyObject *args);
|
||||||
static PyObject* wrapSetImage(PyObject *self, PyObject *args);
|
static PyObject* wrapSetImage(PyObject *self, PyObject *args);
|
||||||
static PyObject* wrapAbort(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:
|
private:
|
||||||
|
PyThreadState * _mainThreadState;
|
||||||
|
|
||||||
const int _priority;
|
const int _priority;
|
||||||
|
|
||||||
const int _timeout;
|
const int _timeout;
|
||||||
|
@ -54,6 +54,7 @@ EffectEngine::EffectEngine(Hyperion * hyperion, const Json::Value & jsonEffectCo
|
|||||||
|
|
||||||
// initialize the python interpreter
|
// initialize the python interpreter
|
||||||
std::cout << "Initializing Python interpreter" << std::endl;
|
std::cout << "Initializing Python interpreter" << std::endl;
|
||||||
|
Effect::registerHyperionExtensionModule();
|
||||||
Py_InitializeEx(0);
|
Py_InitializeEx(0);
|
||||||
PyEval_InitThreads(); // Create the GIL
|
PyEval_InitThreads(); // Create the GIL
|
||||||
_mainThreadState = PyEval_SaveThread();
|
_mainThreadState = PyEval_SaveThread();
|
||||||
@ -151,7 +152,7 @@ int EffectEngine::runEffectScript(const std::string &script, const Json::Value &
|
|||||||
channelCleared(priority);
|
channelCleared(priority);
|
||||||
|
|
||||||
// create the effect
|
// 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(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*)));
|
connect(effect, SIGNAL(effectFinished(Effect*)), this, SLOT(effectFinished(Effect*)));
|
||||||
_activeEffects.push_back(effect);
|
_activeEffects.push_back(effect);
|
||||||
|
8
libsrc/grabber/CMakeLists.txt
Normal file
8
libsrc/grabber/CMakeLists.txt
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
if (ENABLE_DISPMANX)
|
||||||
|
add_subdirectory(dispmanx)
|
||||||
|
endif (ENABLE_DISPMANX)
|
||||||
|
|
||||||
|
if (ENABLE_V4L2)
|
||||||
|
add_subdirectory(v4l2)
|
||||||
|
endif (ENABLE_V4L2)
|
@ -4,8 +4,8 @@ find_package(BCM REQUIRED)
|
|||||||
include_directories(${BCM_INCLUDE_DIRS})
|
include_directories(${BCM_INCLUDE_DIRS})
|
||||||
|
|
||||||
# Define the current source locations
|
# Define the current source locations
|
||||||
SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/dispmanx-grabber)
|
SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/grabber)
|
||||||
SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/dispmanx-grabber)
|
SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/grabber/dispmanx)
|
||||||
|
|
||||||
# Group the headers that go through the MOC compiler
|
# Group the headers that go through the MOC compiler
|
||||||
SET(DispmanxGrabberQT_HEADERS
|
SET(DispmanxGrabberQT_HEADERS
|
@ -7,8 +7,8 @@
|
|||||||
#include <hyperion/ImageProcessorFactory.h>
|
#include <hyperion/ImageProcessorFactory.h>
|
||||||
#include <hyperion/ImageProcessor.h>
|
#include <hyperion/ImageProcessor.h>
|
||||||
|
|
||||||
// Local-dispmanx includes
|
// Dispmanx grabber includes
|
||||||
#include <dispmanx-grabber/DispmanxWrapper.h>
|
#include <grabber/DispmanxWrapper.h>
|
||||||
#include "DispmanxFrameGrabber.h"
|
#include "DispmanxFrameGrabber.h"
|
||||||
|
|
||||||
|
|
32
libsrc/grabber/v4l2/CMakeLists.txt
Normal file
32
libsrc/grabber/v4l2/CMakeLists.txt
Normal 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}
|
||||||
|
)
|
@ -14,7 +14,7 @@
|
|||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
#include <linux/videodev2.h>
|
#include <linux/videodev2.h>
|
||||||
|
|
||||||
#include "V4L2Grabber.h"
|
#include "grabber/V4L2Grabber.h"
|
||||||
|
|
||||||
#define CLEAR(x) memset(&(x), 0, sizeof(x))
|
#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(
|
V4L2Grabber::V4L2Grabber(const std::string & device,
|
||||||
const std::string & device,
|
|
||||||
int input,
|
int input,
|
||||||
VideoStandard videoStandard,
|
VideoStandard videoStandard,
|
||||||
|
PixelFormat pixelFormat,
|
||||||
int width,
|
int width,
|
||||||
int height,
|
int height,
|
||||||
int frameDecimation,
|
int frameDecimation,
|
||||||
@ -49,9 +49,10 @@ V4L2Grabber::V4L2Grabber(
|
|||||||
_ioMethod(IO_METHOD_MMAP),
|
_ioMethod(IO_METHOD_MMAP),
|
||||||
_fileDescriptor(-1),
|
_fileDescriptor(-1),
|
||||||
_buffers(),
|
_buffers(),
|
||||||
_pixelFormat(0),
|
_pixelFormat(pixelFormat),
|
||||||
_width(width),
|
_width(width),
|
||||||
_height(height),
|
_height(height),
|
||||||
|
_frameByteSize(-1),
|
||||||
_cropLeft(0),
|
_cropLeft(0),
|
||||||
_cropRight(0),
|
_cropRight(0),
|
||||||
_cropTop(0),
|
_cropTop(0),
|
||||||
@ -59,10 +60,12 @@ V4L2Grabber::V4L2Grabber(
|
|||||||
_frameDecimation(std::max(1, frameDecimation)),
|
_frameDecimation(std::max(1, frameDecimation)),
|
||||||
_horizontalPixelDecimation(std::max(1, horizontalPixelDecimation)),
|
_horizontalPixelDecimation(std::max(1, horizontalPixelDecimation)),
|
||||||
_verticalPixelDecimation(std::max(1, verticalPixelDecimation)),
|
_verticalPixelDecimation(std::max(1, verticalPixelDecimation)),
|
||||||
_mode3D(MODE_NONE),
|
_noSignalCounterThreshold(50),
|
||||||
|
_noSignalThresholdColor(ColorRgb{0,0,0}),
|
||||||
|
_mode3D(VIDEO_2D),
|
||||||
_currentFrame(0),
|
_currentFrame(0),
|
||||||
_callback(nullptr),
|
_noSignalCounter(0),
|
||||||
_callbackArg(nullptr)
|
_streamNotifier(nullptr)
|
||||||
{
|
{
|
||||||
open_device();
|
open_device();
|
||||||
init_device(videoStandard, input);
|
init_device(videoStandard, input);
|
||||||
@ -70,6 +73,8 @@ V4L2Grabber::V4L2Grabber(
|
|||||||
|
|
||||||
V4L2Grabber::~V4L2Grabber()
|
V4L2Grabber::~V4L2Grabber()
|
||||||
{
|
{
|
||||||
|
// stop if the grabber was not stopped
|
||||||
|
stop();
|
||||||
uninit_device();
|
uninit_device();
|
||||||
close_device();
|
close_device();
|
||||||
}
|
}
|
||||||
@ -82,66 +87,39 @@ void V4L2Grabber::setCropping(int cropLeft, int cropRight, int cropTop, int crop
|
|||||||
_cropBottom = cropBottom;
|
_cropBottom = cropBottom;
|
||||||
}
|
}
|
||||||
|
|
||||||
void V4L2Grabber::set3D(Mode3D mode)
|
void V4L2Grabber::set3D(VideoMode mode)
|
||||||
{
|
{
|
||||||
_mode3D = mode;
|
_mode3D = mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
void V4L2Grabber::setCallback(V4L2Grabber::ImageCallback callback, void *arg)
|
void V4L2Grabber::setSignalThreshold(double redSignalThreshold, double greenSignalThreshold, double blueSignalThreshold, int noSignalCounterThreshold)
|
||||||
{
|
{
|
||||||
_callback = callback;
|
_noSignalThresholdColor.red = uint8_t(255*redSignalThreshold);
|
||||||
_callbackArg = arg;
|
_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()
|
void V4L2Grabber::start()
|
||||||
{
|
{
|
||||||
start_capturing();
|
if (_streamNotifier != nullptr && !_streamNotifier->isEnabled())
|
||||||
}
|
|
||||||
|
|
||||||
void V4L2Grabber::capture(int frameCount)
|
|
||||||
{
|
|
||||||
for (int count = 0; count < frameCount || frameCount < 0; ++count)
|
|
||||||
{
|
{
|
||||||
for (;;)
|
_streamNotifier->setEnabled(true);
|
||||||
{
|
start_capturing();
|
||||||
// the set of file descriptors for select
|
std::cout << "V4L2 grabber started" << std::endl;
|
||||||
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. */
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void V4L2Grabber::stop()
|
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()
|
void V4L2Grabber::open_device()
|
||||||
@ -170,6 +148,11 @@ void V4L2Grabber::open_device()
|
|||||||
oss << "Cannot open '" << _deviceName << "'";
|
oss << "Cannot open '" << _deviceName << "'";
|
||||||
throw_errno_exception(oss.str());
|
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()
|
void V4L2Grabber::close_device()
|
||||||
@ -178,6 +161,12 @@ void V4L2Grabber::close_device()
|
|||||||
throw_errno_exception("close");
|
throw_errno_exception("close");
|
||||||
|
|
||||||
_fileDescriptor = -1;
|
_fileDescriptor = -1;
|
||||||
|
|
||||||
|
if (_streamNotifier != nullptr)
|
||||||
|
{
|
||||||
|
delete _streamNotifier;
|
||||||
|
_streamNotifier = nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void V4L2Grabber::init_read(unsigned int buffer_size)
|
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
|
// set the video standard if needed
|
||||||
switch (videoStandard)
|
switch (videoStandard)
|
||||||
{
|
{
|
||||||
case PAL:
|
case VIDEOSTANDARD_PAL:
|
||||||
{
|
{
|
||||||
v4l2_std_id std_id = V4L2_STD_PAL;
|
v4l2_std_id std_id = V4L2_STD_PAL;
|
||||||
if (-1 == xioctl(VIDIOC_S_STD, &std_id))
|
if (-1 == xioctl(VIDIOC_S_STD, &std_id))
|
||||||
@ -368,7 +357,7 @@ void V4L2Grabber::init_device(VideoStandard videoStandard, int input)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case NTSC:
|
case VIDEOSTANDARD_NTSC:
|
||||||
{
|
{
|
||||||
v4l2_std_id std_id = V4L2_STD_NTSC;
|
v4l2_std_id std_id = V4L2_STD_NTSC;
|
||||||
if (-1 == xioctl(VIDIOC_S_STD, &std_id))
|
if (-1 == xioctl(VIDIOC_S_STD, &std_id))
|
||||||
@ -377,7 +366,7 @@ void V4L2Grabber::init_device(VideoStandard videoStandard, int input)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case NO_CHANGE:
|
case VIDEOSTANDARD_NO_CHANGE:
|
||||||
default:
|
default:
|
||||||
// No change to device settings
|
// No change to device settings
|
||||||
break;
|
break;
|
||||||
@ -393,17 +382,25 @@ void V4L2Grabber::init_device(VideoStandard videoStandard, int input)
|
|||||||
throw_errno_exception("VIDIOC_G_FMT");
|
throw_errno_exception("VIDIOC_G_FMT");
|
||||||
}
|
}
|
||||||
|
|
||||||
// check pixel format
|
// set the requested pixel format
|
||||||
switch (fmt.fmt.pix.pixelformat)
|
switch (_pixelFormat)
|
||||||
{
|
{
|
||||||
case V4L2_PIX_FMT_UYVY:
|
case PIXELFORMAT_UYVY:
|
||||||
case V4L2_PIX_FMT_YUYV:
|
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY;
|
||||||
_pixelFormat = fmt.fmt.pix.pixelformat;
|
|
||||||
break;
|
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:
|
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 || _height > 0)
|
||||||
{
|
{
|
||||||
if (_width > 0)
|
if (_width > 0)
|
||||||
@ -415,19 +412,19 @@ void V4L2Grabber::init_device(VideoStandard videoStandard, int input)
|
|||||||
{
|
{
|
||||||
fmt.fmt.pix.height = _height;
|
fmt.fmt.pix.height = _height;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// set the settings
|
// set the settings
|
||||||
if (-1 == xioctl(VIDIOC_S_FMT, &fmt))
|
if (-1 == xioctl(VIDIOC_S_FMT, &fmt))
|
||||||
{
|
{
|
||||||
throw_errno_exception("VIDIOC_S_FMT");
|
throw_errno_exception("VIDIOC_S_FMT");
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the format settings again
|
// get the format settings again
|
||||||
// (the size may not have been accepted without an error)
|
// (the size may not have been accepted without an error)
|
||||||
if (-1 == xioctl(VIDIOC_G_FMT, &fmt))
|
if (-1 == xioctl(VIDIOC_G_FMT, &fmt))
|
||||||
{
|
{
|
||||||
throw_errno_exception("VIDIOC_G_FMT");
|
throw_errno_exception("VIDIOC_G_FMT");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// store width & height
|
// store width & height
|
||||||
@ -437,6 +434,28 @@ void V4L2Grabber::init_device(VideoStandard videoStandard, int input)
|
|||||||
// print the eventually used width and height
|
// print the eventually used width and height
|
||||||
std::cout << "V4L2 width=" << _width << " height=" << _height << std::endl;
|
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) {
|
switch (_ioMethod) {
|
||||||
case IO_METHOD_READ:
|
case IO_METHOD_READ:
|
||||||
init_read(fmt.fmt.pix.sizeimage);
|
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...
|
// 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
|
else
|
||||||
{
|
{
|
||||||
@ -674,10 +693,10 @@ void V4L2Grabber::process_image(const uint8_t * data)
|
|||||||
|
|
||||||
switch (_mode3D)
|
switch (_mode3D)
|
||||||
{
|
{
|
||||||
case MODE_3DSBS:
|
case VIDEO_3DSBS:
|
||||||
width = _width/2;
|
width = _width/2;
|
||||||
break;
|
break;
|
||||||
case MODE_3DTAB:
|
case VIDEO_3DTAB:
|
||||||
height = _height/2;
|
height = _height/2;
|
||||||
break;
|
break;
|
||||||
default:
|
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)
|
for (int xSource = _cropLeft + _horizontalPixelDecimation/2, xDest = 0; xSource < width - _cropRight; xSource += _horizontalPixelDecimation, ++xDest)
|
||||||
{
|
{
|
||||||
int index = (_width * ySource + xSource) * 2;
|
ColorRgb & rgb = image(xDest, yDest);
|
||||||
uint8_t y = 0;
|
|
||||||
uint8_t u = 0;
|
|
||||||
uint8_t v = 0;
|
|
||||||
|
|
||||||
switch (_pixelFormat)
|
switch (_pixelFormat)
|
||||||
{
|
{
|
||||||
case V4L2_PIX_FMT_UYVY:
|
case PIXELFORMAT_UYVY:
|
||||||
y = data[index+1];
|
{
|
||||||
u = (xSource%2 == 0) ? data[index ] : data[index-2];
|
int index = (_width * ySource + xSource) * 2;
|
||||||
v = (xSource%2 == 0) ? data[index+2] : data[index ];
|
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;
|
break;
|
||||||
case V4L2_PIX_FMT_YUYV:
|
case PIXELFORMAT_YUYV:
|
||||||
y = data[index];
|
{
|
||||||
u = (xSource%2 == 0) ? data[index+1] : data[index-1];
|
int index = (_width * ySource + xSource) * 2;
|
||||||
v = (xSource%2 == 0) ? data[index+3] : data[index+1];
|
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;
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
117
libsrc/grabber/v4l2/V4L2Wrapper.cpp
Normal file
117
libsrc/grabber/v4l2/V4L2Wrapper.cpp
Normal 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();
|
||||||
|
}
|
@ -43,6 +43,11 @@ void ImageProcessor::setSize(const unsigned width, const unsigned height)
|
|||||||
_imageToLeds = new ImageToLedsMap(width, height, 0, 0, _ledString.leds());
|
_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
|
bool ImageProcessor::getScanParameters(size_t led, double &hscanBegin, double &hscanEnd, double &vscanBegin, double &vscanEnd) const
|
||||||
{
|
{
|
||||||
if (led < _ledString.leds().size())
|
if (led < _ledString.leds().size())
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
|
|
||||||
|
// STL includes
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
// Hyperion includes
|
// Hyperion includes
|
||||||
#include <hyperion/ImageProcessorFactory.h>
|
#include <hyperion/ImageProcessorFactory.h>
|
||||||
#include <hyperion/ImageProcessor.h>
|
#include <hyperion/ImageProcessor.h>
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
// STL includes
|
// STL includes
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
@ -33,17 +32,36 @@ ImageToLedsMap::ImageToLedsMap(
|
|||||||
|
|
||||||
for (const Led& led : leds)
|
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
|
// Compute the index boundaries for this led
|
||||||
const unsigned minX_idx = xOffset + unsigned(std::round((actualWidth-1) * led.minX_frac));
|
unsigned minX_idx = xOffset + unsigned(std::round(actualWidth * led.minX_frac));
|
||||||
const unsigned maxX_idx = xOffset + unsigned(std::round((actualWidth-1) * led.maxX_frac));
|
unsigned maxX_idx = xOffset + unsigned(std::round(actualWidth * led.maxX_frac));
|
||||||
const unsigned minY_idx = yOffset + unsigned(std::round((actualHeight-1) * led.minY_frac));
|
unsigned minY_idx = yOffset + unsigned(std::round(actualHeight * led.minY_frac));
|
||||||
const unsigned maxY_idx = yOffset + unsigned(std::round((actualHeight-1) * led.maxY_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
|
// Add all the indices in the above defined rectangle to the indices for this led
|
||||||
std::vector<unsigned> ledColors;
|
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);
|
ledColors.push_back(y*width + x);
|
||||||
}
|
}
|
||||||
|
@ -37,9 +37,5 @@ add_library(jsonserver
|
|||||||
target_link_libraries(jsonserver
|
target_link_libraries(jsonserver
|
||||||
hyperion
|
hyperion
|
||||||
hyperion-utils
|
hyperion-utils
|
||||||
jsoncpp)
|
jsoncpp
|
||||||
|
${QT_LIBRARIES})
|
||||||
qt4_use_modules(jsonserver
|
|
||||||
Core
|
|
||||||
Gui
|
|
||||||
Network)
|
|
||||||
|
@ -16,12 +16,12 @@
|
|||||||
"required" : false
|
"required" : false
|
||||||
},
|
},
|
||||||
"saturationGain" : {
|
"saturationGain" : {
|
||||||
"type" : "double",
|
"type" : "number",
|
||||||
"required" : false,
|
"required" : false,
|
||||||
"minimum" : 0.0
|
"minimum" : 0.0
|
||||||
},
|
},
|
||||||
"valueGain" : {
|
"valueGain" : {
|
||||||
"type" : "double",
|
"type" : "number",
|
||||||
"required" : false,
|
"required" : false,
|
||||||
"minimum" : 0.0
|
"minimum" : 0.0
|
||||||
},
|
},
|
||||||
@ -29,7 +29,7 @@
|
|||||||
"type": "array",
|
"type": "array",
|
||||||
"required": false,
|
"required": false,
|
||||||
"items" : {
|
"items" : {
|
||||||
"type": "double",
|
"type": "number",
|
||||||
"minimum": 0.0,
|
"minimum": 0.0,
|
||||||
"maximum": 1.0
|
"maximum": 1.0
|
||||||
},
|
},
|
||||||
@ -40,7 +40,7 @@
|
|||||||
"type": "array",
|
"type": "array",
|
||||||
"required": false,
|
"required": false,
|
||||||
"items" : {
|
"items" : {
|
||||||
"type": "double",
|
"type": "number",
|
||||||
"minimum": 0.0
|
"minimum": 0.0
|
||||||
},
|
},
|
||||||
"minItems": 3,
|
"minItems": 3,
|
||||||
@ -50,7 +50,7 @@
|
|||||||
"type": "array",
|
"type": "array",
|
||||||
"required": false,
|
"required": false,
|
||||||
"items" : {
|
"items" : {
|
||||||
"type": "double"
|
"type": "number"
|
||||||
},
|
},
|
||||||
"minItems": 3,
|
"minItems": 3,
|
||||||
"maxItems": 3
|
"maxItems": 3
|
||||||
@ -59,7 +59,7 @@
|
|||||||
"type": "array",
|
"type": "array",
|
||||||
"required": false,
|
"required": false,
|
||||||
"items" : {
|
"items" : {
|
||||||
"type": "double"
|
"type": "number"
|
||||||
},
|
},
|
||||||
"minItems": 3,
|
"minItems": 3,
|
||||||
"maxItems": 3
|
"maxItems": 3
|
||||||
|
43
libsrc/leddevice/CMakeLists.txt
Normal file → Executable file
43
libsrc/leddevice/CMakeLists.txt
Normal file → Executable file
@ -8,28 +8,28 @@ find_package(libusb-1.0 REQUIRED)
|
|||||||
find_package(Threads REQUIRED)
|
find_package(Threads REQUIRED)
|
||||||
|
|
||||||
include_directories(
|
include_directories(
|
||||||
../../include/hidapi
|
../../include/hidapi
|
||||||
${LIBUSB_1_INCLUDE_DIRS}) # for Lightpack device
|
${LIBUSB_1_INCLUDE_DIRS}) # for Lightpack device
|
||||||
|
|
||||||
# Group the headers that go through the MOC compiler
|
# Group the headers that go through the MOC compiler
|
||||||
SET(Leddevice_QT_HEADERS
|
SET(Leddevice_QT_HEADERS
|
||||||
|
${CURRENT_SOURCE_DIR}/LedRs232Device.h
|
||||||
${CURRENT_SOURCE_DIR}/LedDeviceAdalight.h
|
${CURRENT_SOURCE_DIR}/LedDeviceAdalight.h
|
||||||
|
${CURRENT_SOURCE_DIR}/LedDevicePhilipsHue.h
|
||||||
)
|
)
|
||||||
|
|
||||||
SET(Leddevice_HEADERS
|
SET(Leddevice_HEADERS
|
||||||
${CURRENT_HEADER_DIR}/LedDevice.h
|
${CURRENT_HEADER_DIR}/LedDevice.h
|
||||||
${CURRENT_HEADER_DIR}/LedDeviceFactory.h
|
${CURRENT_HEADER_DIR}/LedDeviceFactory.h
|
||||||
|
|
||||||
${CURRENT_SOURCE_DIR}/LedRs232Device.h
|
|
||||||
|
|
||||||
${CURRENT_SOURCE_DIR}/LedDeviceLightpack.h
|
${CURRENT_SOURCE_DIR}/LedDeviceLightpack.h
|
||||||
${CURRENT_SOURCE_DIR}/LedDeviceMultiLightpack.h
|
${CURRENT_SOURCE_DIR}/LedDeviceMultiLightpack.h
|
||||||
${CURRENT_SOURCE_DIR}/LedDevicePaintpack.h
|
${CURRENT_SOURCE_DIR}/LedDevicePaintpack.h
|
||||||
${CURRENT_SOURCE_DIR}/LedDevicePiBlaster.h
|
${CURRENT_SOURCE_DIR}/LedDevicePiBlaster.h
|
||||||
${CURRENT_SOURCE_DIR}/LedDeviceSedu.h
|
${CURRENT_SOURCE_DIR}/LedDeviceSedu.h
|
||||||
${CURRENT_SOURCE_DIR}/LedDeviceTest.h
|
${CURRENT_SOURCE_DIR}/LedDeviceTest.h
|
||||||
${CURRENT_SOURCE_DIR}/LedDeviceWs2812b.h
|
${CURRENT_SOURCE_DIR}/LedDeviceHyperionUsbasp.h
|
||||||
${CURRENT_SOURCE_DIR}/LedDeviceWs2811.h
|
${CURRENT_SOURCE_DIR}/LedDeviceTpm2.h
|
||||||
)
|
)
|
||||||
|
|
||||||
SET(Leddevice_SOURCES
|
SET(Leddevice_SOURCES
|
||||||
@ -44,8 +44,9 @@ SET(Leddevice_SOURCES
|
|||||||
${CURRENT_SOURCE_DIR}/LedDevicePiBlaster.cpp
|
${CURRENT_SOURCE_DIR}/LedDevicePiBlaster.cpp
|
||||||
${CURRENT_SOURCE_DIR}/LedDeviceSedu.cpp
|
${CURRENT_SOURCE_DIR}/LedDeviceSedu.cpp
|
||||||
${CURRENT_SOURCE_DIR}/LedDeviceTest.cpp
|
${CURRENT_SOURCE_DIR}/LedDeviceTest.cpp
|
||||||
${CURRENT_SOURCE_DIR}/LedDeviceWs2811.cpp
|
${CURRENT_SOURCE_DIR}/LedDeviceHyperionUsbasp.cpp
|
||||||
${CURRENT_SOURCE_DIR}/LedDeviceWs2812b.cpp
|
${CURRENT_SOURCE_DIR}/LedDevicePhilipsHue.cpp
|
||||||
|
${CURRENT_SOURCE_DIR}/LedDeviceTpm2.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
if(ENABLE_SPIDEV)
|
if(ENABLE_SPIDEV)
|
||||||
@ -67,6 +68,17 @@ if(ENABLE_SPIDEV)
|
|||||||
)
|
)
|
||||||
endif(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})
|
QT4_WRAP_CPP(Leddevice_HEADERS_MOC ${Leddevice_QT_HEADERS})
|
||||||
|
|
||||||
@ -78,12 +90,17 @@ add_library(leddevice
|
|||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(leddevice
|
target_link_libraries(leddevice
|
||||||
hyperion-utils
|
hyperion-utils
|
||||||
serialport
|
serialport
|
||||||
${LIBUSB_1_LIBRARIES} #apt-get install libusb-1.0-0-dev
|
${LIBUSB_1_LIBRARIES} #apt-get install libusb-1.0-0-dev
|
||||||
${CMAKE_THREAD_LIBS_INIT}
|
${CMAKE_THREAD_LIBS_INIT}
|
||||||
${QT_LIBRARIES}
|
${QT_LIBRARIES}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if(ENABLE_TINKERFORGE)
|
||||||
|
target_link_libraries(leddevice tinkerforge)
|
||||||
|
endif()
|
||||||
|
|
||||||
if(APPLE)
|
if(APPLE)
|
||||||
target_link_libraries(leddevice hidapi-mac)
|
target_link_libraries(leddevice hidapi-mac)
|
||||||
else()
|
else()
|
||||||
|
@ -11,8 +11,8 @@
|
|||||||
// hyperion local includes
|
// hyperion local includes
|
||||||
#include "LedDeviceAdalight.h"
|
#include "LedDeviceAdalight.h"
|
||||||
|
|
||||||
LedDeviceAdalight::LedDeviceAdalight(const std::string& outputDevice, const unsigned baudrate) :
|
LedDeviceAdalight::LedDeviceAdalight(const std::string& outputDevice, const unsigned baudrate, int delayAfterConnect_ms) :
|
||||||
LedRs232Device(outputDevice, baudrate),
|
LedRs232Device(outputDevice, baudrate, delayAfterConnect_ms),
|
||||||
_ledBuffer(0),
|
_ledBuffer(0),
|
||||||
_timer()
|
_timer()
|
||||||
{
|
{
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
///
|
///
|
||||||
/// Implementation of the LedDevice interface for writing to an Adalight led device.
|
/// Implementation of the LedDevice interface for writing to an Adalight led device.
|
||||||
///
|
///
|
||||||
class LedDeviceAdalight : public QObject, public LedRs232Device
|
class LedDeviceAdalight : public LedRs232Device
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
@ -23,7 +23,7 @@ public:
|
|||||||
/// @param outputDevice The name of the output device (eg '/dev/ttyS0')
|
/// @param outputDevice The name of the output device (eg '/dev/ttyS0')
|
||||||
/// @param baudrate The used baudrate for writing to the output device
|
/// @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
|
/// Writes the led color values to the led-device
|
||||||
|
70
libsrc/leddevice/LedDeviceFactory.cpp
Normal file → Executable file
70
libsrc/leddevice/LedDeviceFactory.cpp
Normal file → Executable file
@ -1,3 +1,6 @@
|
|||||||
|
// Stl includes
|
||||||
|
#include <string>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
// Build configuration
|
// Build configuration
|
||||||
#include <HyperionConfig.h>
|
#include <HyperionConfig.h>
|
||||||
@ -13,6 +16,10 @@
|
|||||||
#include "LedDeviceWs2801.h"
|
#include "LedDeviceWs2801.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef ENABLE_TINKERFORGE
|
||||||
|
#include "LedDeviceTinkerforge.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "LedDeviceAdalight.h"
|
#include "LedDeviceAdalight.h"
|
||||||
#include "LedDeviceLightpack.h"
|
#include "LedDeviceLightpack.h"
|
||||||
#include "LedDeviceMultiLightpack.h"
|
#include "LedDeviceMultiLightpack.h"
|
||||||
@ -20,8 +27,9 @@
|
|||||||
#include "LedDevicePiBlaster.h"
|
#include "LedDevicePiBlaster.h"
|
||||||
#include "LedDeviceSedu.h"
|
#include "LedDeviceSedu.h"
|
||||||
#include "LedDeviceTest.h"
|
#include "LedDeviceTest.h"
|
||||||
#include "LedDeviceWs2811.h"
|
#include "LedDeviceHyperionUsbasp.h"
|
||||||
#include "LedDeviceWs2812b.h"
|
#include "LedDevicePhilipsHue.h"
|
||||||
|
#include "LedDeviceTpm2.h"
|
||||||
|
|
||||||
LedDevice * LedDeviceFactory::construct(const Json::Value & deviceConfig)
|
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 std::string output = deviceConfig["output"].asString();
|
||||||
const unsigned rate = deviceConfig["rate"].asInt();
|
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();
|
deviceAdalight->open();
|
||||||
|
|
||||||
device = deviceAdalight;
|
device = deviceAdalight;
|
||||||
@ -84,23 +93,20 @@ LedDevice * LedDeviceFactory::construct(const Json::Value & deviceConfig)
|
|||||||
device = deviceWs2801;
|
device = deviceWs2801;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
// else if (type == "ws2811")
|
#ifdef ENABLE_TINKERFORGE
|
||||||
// {
|
else if (type=="tinkerforge")
|
||||||
// const std::string output = deviceConfig["output"].asString();
|
{
|
||||||
// const std::string outputSpeed = deviceConfig["output"].asString();
|
const std::string host = deviceConfig.get("output", "127.0.0.1").asString();
|
||||||
// const std::string timingOption = deviceConfig["timingOption"].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;
|
LedDeviceTinkerforge* deviceTinkerforge = new LedDeviceTinkerforge(host, port, uid, rate);
|
||||||
// if (outputSpeed != "high" && outputSpeed != "low")
|
deviceTinkerforge->open();
|
||||||
// {
|
|
||||||
// std::cerr << "Incorrect speed-mode selected for WS2811: " << outputSpeed << " != {'high', 'low'}" << std::endl;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// LedDeviceWs2811 * deviceWs2811 = new LedDeviceWs2811(output, ws2811::fromString(timingOption, ws2811::option_2855), speedMode);
|
device = deviceTinkerforge;
|
||||||
// deviceWs2811->open();
|
}
|
||||||
|
#endif
|
||||||
// device = deviceWs2811;
|
|
||||||
// }
|
|
||||||
else if (type == "lightpack")
|
else if (type == "lightpack")
|
||||||
{
|
{
|
||||||
const std::string output = deviceConfig.get("output", "").asString();
|
const std::string output = deviceConfig.get("output", "").asString();
|
||||||
@ -144,17 +150,37 @@ LedDevice * LedDeviceFactory::construct(const Json::Value & deviceConfig)
|
|||||||
|
|
||||||
device = deviceSedu;
|
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")
|
else if (type == "test")
|
||||||
{
|
{
|
||||||
const std::string output = deviceConfig["output"].asString();
|
const std::string output = deviceConfig["output"].asString();
|
||||||
device = new LedDeviceTest(output);
|
device = new LedDeviceTest(output);
|
||||||
}
|
}
|
||||||
else if (type == "ws2812b")
|
else if (type == "tpm2")
|
||||||
{
|
{
|
||||||
LedDeviceWs2812b * deviceWs2812b = new LedDeviceWs2812b();
|
const std::string output = deviceConfig["output"].asString();
|
||||||
deviceWs2812b->open();
|
const unsigned rate = deviceConfig["rate"].asInt();
|
||||||
|
|
||||||
device = deviceWs2812b;
|
LedDeviceTpm2* deviceTpm2 = new LedDeviceTpm2(output, rate);
|
||||||
|
deviceTpm2->open();
|
||||||
|
device = deviceTpm2;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
205
libsrc/leddevice/LedDeviceHyperionUsbasp.cpp
Normal file
205
libsrc/leddevice/LedDeviceHyperionUsbasp.cpp
Normal 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);
|
||||||
|
}
|
88
libsrc/leddevice/LedDeviceHyperionUsbasp.h
Normal file
88
libsrc/leddevice/LedDeviceHyperionUsbasp.h
Normal 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;
|
||||||
|
};
|
@ -19,11 +19,12 @@ LedDeviceLpd6803::LedDeviceLpd6803(const std::string& outputDevice, const unsign
|
|||||||
|
|
||||||
int LedDeviceLpd6803::write(const std::vector<ColorRgb> &ledValues)
|
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
|
// 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
|
// 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
|
// Copy the colors from the ColorRgb vector to the Ldp6803 data vector
|
||||||
|
291
libsrc/leddevice/LedDevicePhilipsHue.cpp
Executable file
291
libsrc/leddevice/LedDevicePhilipsHue.cpp
Executable 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();
|
||||||
|
}
|
218
libsrc/leddevice/LedDevicePhilipsHue.h
Executable file
218
libsrc/leddevice/LedDevicePhilipsHue.h
Executable 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();
|
||||||
|
|
||||||
|
};
|
143
libsrc/leddevice/LedDeviceTinkerforge.cpp
Normal file
143
libsrc/leddevice/LedDeviceTinkerforge.cpp
Normal 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;
|
||||||
|
}
|
82
libsrc/leddevice/LedDeviceTinkerforge.h
Normal file
82
libsrc/leddevice/LedDeviceTinkerforge.h
Normal 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;
|
||||||
|
|
||||||
|
};
|
42
libsrc/leddevice/LedDeviceTpm2.cpp
Normal file
42
libsrc/leddevice/LedDeviceTpm2.cpp
Normal 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());
|
||||||
|
}
|
38
libsrc/leddevice/LedDeviceTpm2.h
Normal file
38
libsrc/leddevice/LedDeviceTpm2.h
Normal 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;
|
||||||
|
};
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
@ -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;
|
|
||||||
};
|
|
@ -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;
|
|
||||||
}
|
|
@ -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;
|
|
||||||
};
|
|
@ -4,17 +4,21 @@
|
|||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
|
// Qt includes
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
// Serial includes
|
// Serial includes
|
||||||
#include <serial/serial.h>
|
#include <serial/serial.h>
|
||||||
|
|
||||||
// Local Hyperion includes
|
// Local Hyperion includes
|
||||||
#include "LedRs232Device.h"
|
#include "LedRs232Device.h"
|
||||||
|
|
||||||
|
LedRs232Device::LedRs232Device(const std::string& outputDevice, const unsigned baudrate, int delayAfterConnect_ms) :
|
||||||
LedRs232Device::LedRs232Device(const std::string& outputDevice, const unsigned baudrate) :
|
_deviceName(outputDevice),
|
||||||
mDeviceName(outputDevice),
|
_baudRate_Hz(baudrate),
|
||||||
mBaudRate_Hz(baudrate),
|
_delayAfterConnect_ms(delayAfterConnect_ms),
|
||||||
_rs232Port()
|
_rs232Port(),
|
||||||
|
_blockedForDelay(false)
|
||||||
{
|
{
|
||||||
// empty
|
// empty
|
||||||
}
|
}
|
||||||
@ -31,10 +35,17 @@ int LedRs232Device::open()
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
std::cout << "Opening UART: " << mDeviceName << std::endl;
|
std::cout << "Opening UART: " << _deviceName << std::endl;
|
||||||
_rs232Port.setPort(mDeviceName);
|
_rs232Port.setPort(_deviceName);
|
||||||
_rs232Port.setBaudrate(mBaudRate_Hz);
|
_rs232Port.setBaudrate(_baudRate_Hz);
|
||||||
_rs232Port.open();
|
_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)
|
catch (const std::exception& e)
|
||||||
{
|
{
|
||||||
@ -47,6 +58,11 @@ int LedRs232Device::open()
|
|||||||
|
|
||||||
int LedRs232Device::writeBytes(const unsigned size, const uint8_t * data)
|
int LedRs232Device::writeBytes(const unsigned size, const uint8_t * data)
|
||||||
{
|
{
|
||||||
|
if (_blockedForDelay)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (!_rs232Port.isOpen())
|
if (!_rs232Port.isOpen())
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
@ -95,3 +111,9 @@ int LedRs232Device::writeBytes(const unsigned size, const uint8_t * data)
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LedRs232Device::unblockAfterDelay()
|
||||||
|
{
|
||||||
|
std::cout << "Device unblocked" << std::endl;
|
||||||
|
_blockedForDelay = false;
|
||||||
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
// Serial includes
|
// Serial includes
|
||||||
#include <serial/serial.h>
|
#include <serial/serial.h>
|
||||||
|
|
||||||
@ -9,8 +11,10 @@
|
|||||||
///
|
///
|
||||||
/// The LedRs232Device implements an abstract base-class for LedDevices using a RS232-device.
|
/// 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:
|
public:
|
||||||
///
|
///
|
||||||
/// Constructs the LedDevice attached to a RS232-device
|
/// 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] outputDevice The name of the output device (eg '/etc/ttyS0')
|
||||||
/// @param[in] baudrate The used baudrate for writing to the output device
|
/// @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
|
/// 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);
|
int writeBytes(const unsigned size, const uint8_t *data);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
/// Unblock the device after a connection delay
|
||||||
|
void unblockAfterDelay();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// The name of the output device
|
/// The name of the output device
|
||||||
const std::string mDeviceName;
|
const std::string _deviceName;
|
||||||
|
|
||||||
/// The used baudrate of the output device
|
/// 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
|
/// The RS232 serial-device
|
||||||
serial::Serial _rs232Port;
|
serial::Serial _rs232Port;
|
||||||
|
|
||||||
|
bool _blockedForDelay;
|
||||||
};
|
};
|
||||||
|
@ -44,9 +44,5 @@ add_library(protoserver
|
|||||||
target_link_libraries(protoserver
|
target_link_libraries(protoserver
|
||||||
hyperion
|
hyperion
|
||||||
hyperion-utils
|
hyperion-utils
|
||||||
${PROTOBUF_LIBRARIES})
|
${PROTOBUF_LIBRARIES}
|
||||||
|
${QT_LIBRARIES})
|
||||||
qt4_use_modules(protoserver
|
|
||||||
Core
|
|
||||||
Gui
|
|
||||||
Network)
|
|
||||||
|
@ -11,6 +11,7 @@ add_library(hyperion-utils
|
|||||||
${CURRENT_HEADER_DIR}/ColorRgba.h
|
${CURRENT_HEADER_DIR}/ColorRgba.h
|
||||||
${CURRENT_SOURCE_DIR}/ColorRgba.cpp
|
${CURRENT_SOURCE_DIR}/ColorRgba.cpp
|
||||||
${CURRENT_HEADER_DIR}/Image.h
|
${CURRENT_HEADER_DIR}/Image.h
|
||||||
|
${CURRENT_HEADER_DIR}/Sleep.h
|
||||||
|
|
||||||
${CURRENT_HEADER_DIR}/HsvTransform.h
|
${CURRENT_HEADER_DIR}/HsvTransform.h
|
||||||
${CURRENT_SOURCE_DIR}/HsvTransform.cpp
|
${CURRENT_SOURCE_DIR}/HsvTransform.cpp
|
||||||
|
@ -17,6 +17,10 @@
|
|||||||
// {"id":668,"jsonrpc":"2.0","method":"XBMC.GetInfoBooleans","params":{"booleans":["System.ScreenSaverActive"]}}
|
// {"id":668,"jsonrpc":"2.0","method":"XBMC.GetInfoBooleans","params":{"booleans":["System.ScreenSaverActive"]}}
|
||||||
// {"id":668,"jsonrpc":"2.0","result":{"System.ScreenSaverActive":false}}
|
// {"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) :
|
XBMCVideoChecker::XBMCVideoChecker(const std::string & address, uint16_t port, bool grabVideo, bool grabPhoto, bool grabAudio, bool grabMenu, bool grabScreensaver, bool enable3DDetection) :
|
||||||
QObject(),
|
QObject(),
|
||||||
_address(QString::fromStdString(address)),
|
_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"})"),
|
_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"]}})"),
|
_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"]}})"),
|
_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(),
|
_socket(),
|
||||||
_grabVideo(grabVideo),
|
_grabVideo(grabVideo),
|
||||||
_grabPhoto(grabPhoto),
|
_grabPhoto(grabPhoto),
|
||||||
@ -33,7 +39,8 @@ XBMCVideoChecker::XBMCVideoChecker(const std::string & address, uint16_t port, b
|
|||||||
_enable3DDetection(enable3DDetection),
|
_enable3DDetection(enable3DDetection),
|
||||||
_previousScreensaverMode(false),
|
_previousScreensaverMode(false),
|
||||||
_previousGrabbingMode(GRABBINGMODE_INVALID),
|
_previousGrabbingMode(GRABBINGMODE_INVALID),
|
||||||
_previousVideoMode(VIDEO_2D)
|
_previousVideoMode(VIDEO_2D),
|
||||||
|
_xbmcVersion(0)
|
||||||
{
|
{
|
||||||
// setup socket
|
// setup socket
|
||||||
connect(&_socket, SIGNAL(readyRead()), this, SLOT(receiveReply()));
|
connect(&_socket, SIGNAL(readyRead()), this, SLOT(receiveReply()));
|
||||||
@ -116,24 +123,32 @@ void XBMCVideoChecker::receiveReply()
|
|||||||
}
|
}
|
||||||
else if (reply.contains("\"id\":667"))
|
else if (reply.contains("\"id\":667"))
|
||||||
{
|
{
|
||||||
// result of Player.GetItem
|
if (_xbmcVersion >= 13)
|
||||||
// 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)
|
|
||||||
{
|
{
|
||||||
QStringRef filename = QStringRef(&reply, pos+8, regex.matchedLength()-9);
|
// check of active stereoscopicmode
|
||||||
if (filename.contains("3DSBS", Qt::CaseInsensitive) || filename.contains("HSBS", Qt::CaseInsensitive))
|
_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);
|
QStringRef filename = QStringRef(&reply, pos+8, regex.matchedLength()-9);
|
||||||
}
|
if (filename.contains("3DSBS", Qt::CaseInsensitive) || filename.contains("HSBS", Qt::CaseInsensitive))
|
||||||
else if (filename.contains("3DTAB", Qt::CaseInsensitive) || filename.contains("HTAB", Qt::CaseInsensitive))
|
{
|
||||||
{
|
setVideoMode(VIDEO_3DSBS);
|
||||||
setVideoMode(VIDEO_3DTAB);
|
}
|
||||||
}
|
else if (filename.contains("3DTAB", Qt::CaseInsensitive) || filename.contains("HTAB", Qt::CaseInsensitive))
|
||||||
else
|
{
|
||||||
{
|
setVideoMode(VIDEO_3DTAB);
|
||||||
setVideoMode(VIDEO_2D);
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
setVideoMode(VIDEO_2D);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -142,6 +157,41 @@ void XBMCVideoChecker::receiveReply()
|
|||||||
// result of System.ScreenSaverActive
|
// result of System.ScreenSaverActive
|
||||||
bool active = reply.contains("\"System.ScreenSaverActive\":true");
|
bool active = reply.contains("\"System.ScreenSaverActive\":true");
|
||||||
setScreensaverMode(!_grabScreensaver && active);
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,11 +28,7 @@ add_executable(hyperion-remote
|
|||||||
${hyperion-remote_HEADERS}
|
${hyperion-remote_HEADERS}
|
||||||
${hyperion-remote_SOURCES})
|
${hyperion-remote_SOURCES})
|
||||||
|
|
||||||
qt4_use_modules(hyperion-remote
|
|
||||||
Core
|
|
||||||
Gui
|
|
||||||
Network)
|
|
||||||
|
|
||||||
target_link_libraries(hyperion-remote
|
target_link_libraries(hyperion-remote
|
||||||
jsoncpp
|
jsoncpp
|
||||||
getoptPlusPlus)
|
getoptPlusPlus
|
||||||
|
${QT_LIBRARIES})
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
// STL includes
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
// Qt includes
|
// Qt includes
|
||||||
#include <QColor>
|
#include <QColor>
|
||||||
#include <QImage>
|
#include <QImage>
|
||||||
|
@ -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]");
|
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)");
|
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");
|
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 & 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 & 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<> & 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<> & 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");
|
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();
|
bool colorTransform = argSaturation.isSet() || argValue.isSet() || argThreshold.isSet() || argGamma.isSet() || argBlacklevel.isSet() || argWhitelevel.isSet();
|
||||||
|
|
||||||
// check that exactly one command was given
|
// 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)
|
if (commandCount != 1)
|
||||||
{
|
{
|
||||||
std::cerr << (commandCount == 0 ? "No command found." : "Multiple commands found.") << " Provide exactly one of the following options:" << std::endl;
|
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 << " " << argColor.usageLine() << std::endl;
|
||||||
std::cerr << " " << argImage.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 << " " << argServerInfo.usageLine() << std::endl;
|
||||||
std::cerr << " " << argClear.usageLine() << std::endl;
|
std::cerr << " " << argClear.usageLine() << std::endl;
|
||||||
std::cerr << " " << argClearAll.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());
|
connection.setImage(argImage.getValue(), argPriority.getValue(), argDuration.getValue());
|
||||||
}
|
}
|
||||||
else if (argEffect.isSet())
|
else if (argEffect.isSet())
|
||||||
{
|
{
|
||||||
connection.setEffect(argEffect.getValue(), argEffectArgs.getValue(), argPriority.getValue(), argDuration.getValue());
|
connection.setEffect(argEffect.getValue(), argEffectArgs.getValue(), argPriority.getValue(), argDuration.getValue());
|
||||||
}
|
}
|
||||||
else if (argServerInfo.isSet())
|
else if (argServerInfo.isSet())
|
||||||
{
|
{
|
||||||
QString info = connection.getServerInfo();
|
QString info = connection.getServerInfo();
|
||||||
std::cout << "Server info:\n" << info.toStdString() << std::endl;
|
std::cout << "Server info:\n" << info.toStdString() << std::endl;
|
||||||
|
@ -14,18 +14,22 @@ include_directories(
|
|||||||
${QT_INCLUDES}
|
${QT_INCLUDES}
|
||||||
)
|
)
|
||||||
|
|
||||||
set(Hyperion_V4L2_HEADERS
|
set(Hyperion_V4L2_QT_HEADERS
|
||||||
V4L2Grabber.h
|
|
||||||
ProtoConnection.h
|
|
||||||
ImageHandler.h
|
ImageHandler.h
|
||||||
|
ScreenshotHandler.h
|
||||||
|
)
|
||||||
|
|
||||||
|
set(Hyperion_V4L2_HEADERS
|
||||||
VideoStandardParameter.h
|
VideoStandardParameter.h
|
||||||
|
PixelFormatParameter.h
|
||||||
|
ProtoConnection.h
|
||||||
)
|
)
|
||||||
|
|
||||||
set(Hyperion_V4L2_SOURCES
|
set(Hyperion_V4L2_SOURCES
|
||||||
hyperion-v4l2.cpp
|
hyperion-v4l2.cpp
|
||||||
V4L2Grabber.cpp
|
|
||||||
ProtoConnection.cpp
|
ProtoConnection.cpp
|
||||||
ImageHandler.cpp
|
ImageHandler.cpp
|
||||||
|
ScreenshotHandler.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
set(Hyperion_V4L2_PROTOS
|
set(Hyperion_V4L2_PROTOS
|
||||||
@ -36,22 +40,23 @@ protobuf_generate_cpp(Hyperion_V4L2_PROTO_SRCS Hyperion_V4L2_PROTO_HDRS
|
|||||||
${Hyperion_V4L2_PROTOS}
|
${Hyperion_V4L2_PROTOS}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
QT4_WRAP_CPP(Hyperion_V4L2_MOC_SOURCES ${Hyperion_V4L2_QT_HEADERS})
|
||||||
|
|
||||||
add_executable(hyperion-v4l2
|
add_executable(hyperion-v4l2
|
||||||
${Hyperion_V4L2_HEADERS}
|
${Hyperion_V4L2_HEADERS}
|
||||||
${Hyperion_V4L2_SOURCES}
|
${Hyperion_V4L2_SOURCES}
|
||||||
|
${Hyperion_V4L2_QT_HEADERS}
|
||||||
|
${Hyperion_V4L2_MOC_SOURCES}
|
||||||
${Hyperion_V4L2_PROTO_SRCS}
|
${Hyperion_V4L2_PROTO_SRCS}
|
||||||
${Hyperion_V4L2_PROTO_HDRS}
|
${Hyperion_V4L2_PROTO_HDRS}
|
||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(hyperion-v4l2
|
target_link_libraries(hyperion-v4l2
|
||||||
|
v4l2-grabber
|
||||||
getoptPlusPlus
|
getoptPlusPlus
|
||||||
blackborder
|
blackborder
|
||||||
hyperion-utils
|
hyperion-utils
|
||||||
${PROTOBUF_LIBRARIES}
|
${PROTOBUF_LIBRARIES}
|
||||||
pthread
|
pthread
|
||||||
|
${QT_LIBRARIES}
|
||||||
)
|
)
|
||||||
|
|
||||||
qt4_use_modules(hyperion-v4l2
|
|
||||||
Core
|
|
||||||
Gui
|
|
||||||
Network)
|
|
||||||
|
@ -1,41 +1,18 @@
|
|||||||
// hyperion-v4l2 includes
|
// hyperion-v4l2 includes
|
||||||
#include "ImageHandler.h"
|
#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),
|
_priority(priority),
|
||||||
_connection(address),
|
_connection(address)
|
||||||
_signalThreshold(signalThreshold),
|
|
||||||
_signalProcessor(100, 50, 0, uint8_t(std::min(255, std::max(0, int(255*signalThreshold)))))
|
|
||||||
{
|
{
|
||||||
_connection.setSkipReply(skipProtoReply);
|
_connection.setSkipReply(skipProtoReply);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ImageHandler::~ImageHandler()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void ImageHandler::receiveImage(const Image<ColorRgb> & image)
|
void ImageHandler::receiveImage(const Image<ColorRgb> & image)
|
||||||
{
|
{
|
||||||
// check if we should do signal detection
|
_connection.setImage(image, _priority, 1000);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageHandler::imageCallback(void *arg, const Image<ColorRgb> &image)
|
|
||||||
{
|
|
||||||
ImageHandler * handler = static_cast<ImageHandler *>(arg);
|
|
||||||
handler->receiveImage(image);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
@ -1,34 +1,31 @@
|
|||||||
// blackborder includes
|
// Qt includes
|
||||||
#include <blackborder/BlackBorderProcessor.h>
|
#include <QObject>
|
||||||
|
|
||||||
// hyperion-v4l includes
|
// hyperion includes
|
||||||
|
#include <utils/Image.h>
|
||||||
|
#include <utils/ColorRgb.h>
|
||||||
|
|
||||||
|
// hyperion v4l2 includes
|
||||||
#include "ProtoConnection.h"
|
#include "ProtoConnection.h"
|
||||||
|
|
||||||
/// This class handles callbacks from the V4L2 grabber
|
/// This class handles callbacks from the V4L2 grabber
|
||||||
class ImageHandler
|
class ImageHandler : public QObject
|
||||||
{
|
{
|
||||||
public:
|
Q_OBJECT
|
||||||
ImageHandler(const std::string & address, int priority, double signalThreshold, bool skipProtoReply);
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
ImageHandler(const std::string & address, int priority, bool skipProtoReply);
|
||||||
|
virtual ~ImageHandler();
|
||||||
|
|
||||||
|
public slots:
|
||||||
/// Handle a single image
|
/// Handle a single image
|
||||||
/// @param image The image to process
|
/// @param image The image to process
|
||||||
void receiveImage(const Image<ColorRgb> & image);
|
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:
|
private:
|
||||||
/// Priority for calls to Hyperion
|
/// Priority for calls to Hyperion
|
||||||
const int _priority;
|
const int _priority;
|
||||||
|
|
||||||
/// Hyperion proto connection object
|
/// Hyperion proto connection object
|
||||||
ProtoConnection _connection;
|
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;
|
|
||||||
};
|
};
|
||||||
|
43
src/hyperion-v4l2/PixelFormatParameter.h
Normal file
43
src/hyperion-v4l2/PixelFormatParameter.h
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
25
src/hyperion-v4l2/ScreenshotHandler.cpp
Normal file
25
src/hyperion-v4l2/ScreenshotHandler.cpp
Normal 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();
|
||||||
|
}
|
24
src/hyperion-v4l2/ScreenshotHandler.h
Normal file
24
src/hyperion-v4l2/ScreenshotHandler.h
Normal 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;
|
||||||
|
};
|
@ -1,10 +1,13 @@
|
|||||||
// getoptPlusPLus includes
|
// getoptPlusPLus includes
|
||||||
#include <getoptPlusPlus/getoptpp.h>
|
#include <getoptPlusPlus/getoptpp.h>
|
||||||
|
|
||||||
|
// grabber includes
|
||||||
|
#include <grabber/VideoStandard.h>
|
||||||
|
|
||||||
using namespace vlofgren;
|
using namespace vlofgren;
|
||||||
|
|
||||||
/// Data parameter for the video standard
|
/// Data parameter for the video standard
|
||||||
typedef vlofgren::PODParameter<V4L2Grabber::VideoStandard> VideoStandardParameter;
|
typedef vlofgren::PODParameter<VideoStandard> VideoStandardParameter;
|
||||||
|
|
||||||
namespace vlofgren {
|
namespace vlofgren {
|
||||||
/// Translates a string (as passed on the commandline) to a color standard
|
/// Translates a string (as passed on the commandline) to a color standard
|
||||||
@ -13,24 +16,24 @@ namespace vlofgren {
|
|||||||
/// @return The color standard
|
/// @return The color standard
|
||||||
/// @throws Parameter::ParameterRejected If the string did not result in a video standard
|
/// @throws Parameter::ParameterRejected If the string did not result in a video standard
|
||||||
template<>
|
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();
|
QString input = QString::fromStdString(s).toLower();
|
||||||
|
|
||||||
if (input == "pal")
|
if (input == "pal")
|
||||||
{
|
{
|
||||||
return V4L2Grabber::PAL;
|
return VIDEOSTANDARD_PAL;
|
||||||
}
|
}
|
||||||
else if (input == "ntsc")
|
else if (input == "ntsc")
|
||||||
{
|
{
|
||||||
return V4L2Grabber::NTSC;
|
return VIDEOSTANDARD_NTSC;
|
||||||
}
|
}
|
||||||
else if (input == "no-change")
|
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");
|
throw Parameter::ParameterRejected("Invalid value for video standard. Valid values are: PAL, NTSC, and NO-CHANGE");
|
||||||
return V4L2Grabber::NO_CHANGE;
|
return VIDEOSTANDARD_NO_CHANGE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
#include <clocale>
|
#include <clocale>
|
||||||
|
|
||||||
// QT includes
|
// QT includes
|
||||||
#include <QImage>
|
#include <QCoreApplication>
|
||||||
|
|
||||||
// getoptPlusPLus includes
|
// getoptPlusPLus includes
|
||||||
#include <getoptPlusPlus/getoptpp.h>
|
#include <getoptPlusPlus/getoptpp.h>
|
||||||
@ -12,11 +12,15 @@
|
|||||||
// blackborder includes
|
// blackborder includes
|
||||||
#include <blackborder/BlackBorderProcessor.h>
|
#include <blackborder/BlackBorderProcessor.h>
|
||||||
|
|
||||||
|
// grabber includes
|
||||||
|
#include "grabber/V4L2Grabber.h"
|
||||||
|
|
||||||
// hyperion-v4l2 includes
|
// hyperion-v4l2 includes
|
||||||
#include "V4L2Grabber.h"
|
|
||||||
#include "ProtoConnection.h"
|
#include "ProtoConnection.h"
|
||||||
#include "VideoStandardParameter.h"
|
#include "VideoStandardParameter.h"
|
||||||
|
#include "PixelFormatParameter.h"
|
||||||
#include "ImageHandler.h"
|
#include "ImageHandler.h"
|
||||||
|
#include "ScreenshotHandler.h"
|
||||||
|
|
||||||
using namespace vlofgren;
|
using namespace vlofgren;
|
||||||
|
|
||||||
@ -30,8 +34,14 @@ void saveScreenshot(void *, const Image<ColorRgb> & image)
|
|||||||
|
|
||||||
int main(int argc, char** argv)
|
int main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
|
QCoreApplication app(argc, argv);
|
||||||
|
|
||||||
// force the locale
|
// force the locale
|
||||||
setlocale(LC_ALL, "C");
|
setlocale(LC_ALL, "C");
|
||||||
|
QLocale::setDefault(QLocale::c());
|
||||||
|
|
||||||
|
// register the image type to use in signals
|
||||||
|
qRegisterMetaType<Image<ColorRgb>>("Image<ColorRgb>");
|
||||||
|
|
||||||
try
|
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]");
|
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)");
|
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 & 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 & 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)");
|
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]");
|
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");
|
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 & 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<> & 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");
|
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]");
|
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
|
// set defaults
|
||||||
argDevice.setDefault("/dev/video0");
|
argDevice.setDefault("/dev/video0");
|
||||||
argVideoStandard.setDefault(V4L2Grabber::NO_CHANGE);
|
argVideoStandard.setDefault(VIDEOSTANDARD_NO_CHANGE);
|
||||||
|
argPixelFormat.setDefault(PIXELFORMAT_NO_CHANGE);
|
||||||
argInput.setDefault(-1);
|
argInput.setDefault(-1);
|
||||||
argWidth.setDefault(-1);
|
argWidth.setDefault(-1);
|
||||||
argHeight.setDefault(-1);
|
argHeight.setDefault(-1);
|
||||||
@ -95,12 +110,20 @@ int main(int argc, char** argv)
|
|||||||
argDevice.getValue(),
|
argDevice.getValue(),
|
||||||
argInput.getValue(),
|
argInput.getValue(),
|
||||||
argVideoStandard.getValue(),
|
argVideoStandard.getValue(),
|
||||||
|
argPixelFormat.getValue(),
|
||||||
argWidth.getValue(),
|
argWidth.getValue(),
|
||||||
argHeight.getValue(),
|
argHeight.getValue(),
|
||||||
std::max(1, argFrameDecimation.getValue()),
|
std::max(1, argFrameDecimation.getValue()),
|
||||||
std::max(1, argSizeDecimation.getValue()),
|
std::max(1, argSizeDecimation.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
|
// set cropping values
|
||||||
grabber.setCropping(
|
grabber.setCropping(
|
||||||
std::max(0, argCropLeft.getValue()),
|
std::max(0, argCropLeft.getValue()),
|
||||||
@ -111,31 +134,30 @@ int main(int argc, char** argv)
|
|||||||
// set 3D mode if applicable
|
// set 3D mode if applicable
|
||||||
if (arg3DSBS.isSet())
|
if (arg3DSBS.isSet())
|
||||||
{
|
{
|
||||||
grabber.set3D(V4L2Grabber::MODE_3DSBS);
|
grabber.set3D(VIDEO_3DSBS);
|
||||||
}
|
}
|
||||||
else if (arg3DTAB.isSet())
|
else if (arg3DTAB.isSet())
|
||||||
{
|
{
|
||||||
grabber.set3D(V4L2Grabber::MODE_3DTAB);
|
grabber.set3D(VIDEO_3DTAB);
|
||||||
}
|
}
|
||||||
|
|
||||||
// start the grabber
|
|
||||||
grabber.start();
|
|
||||||
|
|
||||||
// run the grabber
|
// run the grabber
|
||||||
if (argScreenshot.isSet())
|
if (argScreenshot.isSet())
|
||||||
{
|
{
|
||||||
grabber.setCallback(&saveScreenshot, nullptr);
|
ScreenshotHandler handler("screenshot.png");
|
||||||
grabber.capture(1);
|
QObject::connect(&grabber, SIGNAL(newFrame(Image<ColorRgb>)), &handler, SLOT(receiveImage(Image<ColorRgb>)));
|
||||||
|
grabber.start();
|
||||||
|
QCoreApplication::exec();
|
||||||
|
grabber.stop();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ImageHandler handler(argAddress.getValue(), argPriority.getValue(), argSignalThreshold.getValue(), argSkipReply.isSet());
|
ImageHandler handler(argAddress.getValue(), argPriority.getValue(), argSkipReply.isSet());
|
||||||
grabber.setCallback(&ImageHandler::imageCallback, &handler);
|
QObject::connect(&grabber, SIGNAL(newFrame(Image<ColorRgb>)), &handler, SLOT(receiveImage(Image<ColorRgb>)));
|
||||||
grabber.capture();
|
grabber.start();
|
||||||
|
QCoreApplication::exec();
|
||||||
|
grabber.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
// stop the grabber
|
|
||||||
grabber.stop();
|
|
||||||
}
|
}
|
||||||
catch (const std::runtime_error & e)
|
catch (const std::runtime_error & e)
|
||||||
{
|
{
|
||||||
|
@ -8,8 +8,13 @@ target_link_libraries(hyperiond
|
|||||||
effectengine
|
effectengine
|
||||||
jsonserver
|
jsonserver
|
||||||
protoserver
|
protoserver
|
||||||
boblightserver)
|
boblightserver
|
||||||
|
)
|
||||||
|
|
||||||
if (ENABLE_DISPMANX)
|
if (ENABLE_DISPMANX)
|
||||||
target_link_libraries(hyperiond dispmanx-grabber)
|
target_link_libraries(hyperiond dispmanx-grabber)
|
||||||
endif (ENABLE_DISPMANX)
|
endif (ENABLE_DISPMANX)
|
||||||
|
|
||||||
|
if (ENABLE_V4L2)
|
||||||
|
target_link_libraries(hyperiond v4l2-grabber)
|
||||||
|
endif (ENABLE_V4L2)
|
||||||
|
@ -19,7 +19,12 @@
|
|||||||
|
|
||||||
#ifdef ENABLE_DISPMANX
|
#ifdef ENABLE_DISPMANX
|
||||||
// Dispmanx grabber includes
|
// Dispmanx grabber includes
|
||||||
#include <dispmanx-grabber/DispmanxWrapper.h>
|
#include <grabber/DispmanxWrapper.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef ENABLE_V4L2
|
||||||
|
// v4l2 grabber
|
||||||
|
#include <grabber/V4L2Wrapper.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// XBMC Video checker includes
|
// XBMC Video checker includes
|
||||||
@ -108,13 +113,30 @@ int main(int argc, char** argv)
|
|||||||
const unsigned duration_ms = effectConfig["duration_ms"].asUInt();
|
const unsigned duration_ms = effectConfig["duration_ms"].asUInt();
|
||||||
const int priority = 0;
|
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
|
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
|
#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
|
// Create Json server if configuration is present
|
||||||
JsonServer * jsonServer = nullptr;
|
JsonServer * jsonServer = nullptr;
|
||||||
if (config.isMember("jsonServer"))
|
if (config.isMember("jsonServer"))
|
||||||
@ -199,6 +258,9 @@ int main(int argc, char** argv)
|
|||||||
// Delete all component
|
// Delete all component
|
||||||
#ifdef ENABLE_DISPMANX
|
#ifdef ENABLE_DISPMANX
|
||||||
delete dispmanx;
|
delete dispmanx;
|
||||||
|
#endif
|
||||||
|
#ifdef ENABLE_V4L2
|
||||||
|
delete v4l2Grabber;
|
||||||
#endif
|
#endif
|
||||||
delete xbmcVideoChecker;
|
delete xbmcVideoChecker;
|
||||||
delete jsonServer;
|
delete jsonServer;
|
||||||
|
@ -52,20 +52,3 @@ target_link_libraries(test_qregexp
|
|||||||
add_executable(test_qtscreenshot TestQtScreenshot.cpp)
|
add_executable(test_qtscreenshot TestQtScreenshot.cpp)
|
||||||
target_link_libraries(test_qtscreenshot
|
target_link_libraries(test_qtscreenshot
|
||||||
${QT_LIBRARIES})
|
${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()
|
|
||||||
|
@ -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;
|
|
||||||
}
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -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;
|
|
||||||
}
|
|
@ -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;
|
|
||||||
}
|
|
@ -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, ¶ms);
|
|
||||||
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, ¶ms);
|
|
||||||
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;
|
|
||||||
}
|
|
@ -10,7 +10,7 @@
|
|||||||
#include <getoptPlusPlus/getoptpp.h>
|
#include <getoptPlusPlus/getoptpp.h>
|
||||||
|
|
||||||
// Dispmanx grabber includes
|
// Dispmanx grabber includes
|
||||||
#include <dispmanx-grabber/DispmanxFrameGrabber.h>
|
#include <grabber/dispmanx/DispmanxFrameGrabber.h>
|
||||||
|
|
||||||
using namespace vlofgren;
|
using namespace vlofgren;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user