mirror of
https://github.com/hyperion-project/hyperion.ng.git
synced 2023-10-10 13:36:59 +02:00
Merge branch 'master' into add_x11
Former-commit-id: 4ce19c22a28609978e1eb72375d3aa7bf63a91be
This commit is contained in:
commit
b20f932ded
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,3 +1,5 @@
|
||||
/*.user
|
||||
/build
|
||||
/build-x86
|
||||
.DS_Store
|
||||
|
||||
|
@ -19,10 +19,13 @@ message(STATUS "ENABLE_V4L2 = " ${ENABLE_V4L2})
|
||||
option(ENABLE_X11 "Enable the X11 grabber" ON)
|
||||
message(STATUS "ENABLE_X11 = " ${ENABLE_X11})
|
||||
|
||||
option(ENABLE_TINKERFORGE "Enable the TINKERFORGE device" ON)
|
||||
message(STATUS "ENABLE_TINKERFORGE = " ${ENABLE_TINKERFORGE})
|
||||
|
||||
# Createt the configuration file
|
||||
# configure a header file to pass some of the CMake settings
|
||||
# to the source code
|
||||
configure_file ("${PROJECT_SOURCE_DIR}/HyperionConfig.h.in" "${PROJECT_BINARY_DIR}/HyperionConfig.h")
|
||||
configure_file("${PROJECT_SOURCE_DIR}/HyperionConfig.h.in" "${PROJECT_BINARY_DIR}/HyperionConfig.h")
|
||||
include_directories("${PROJECT_BINARY_DIR}")
|
||||
|
||||
# Add project specific cmake modules (find, etc)
|
||||
@ -65,6 +68,10 @@ set(CMAKE_FIND_LIBRARY_SUFFIXES_OLD)
|
||||
find_package(libusb-1.0 REQUIRED)
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
if (ENABLE_TINKERFORGE)
|
||||
#find_package(libtinkerforge-1.0 REQUIRED)
|
||||
endif (ENABLE_TINKERFORGE)
|
||||
|
||||
include(${QT_USE_FILE})
|
||||
add_definitions(${QT_DEFINITIONS})
|
||||
# TODO[TvdZ]: This linking directory should only be added if we are cross compiling
|
||||
|
@ -19,8 +19,8 @@ cd "$HYPERION_DIR/build"
|
||||
|
||||
# run cmake to generate make files on the rsapberry pi
|
||||
cmake ..
|
||||
# or if you are not compiling on the raspberry pi and need to disable the Disppmanx grabber
|
||||
cmake -DENABLE_DISPMANX=OFF ..
|
||||
# or if you are not compiling on the raspberry pi and need to disable the Dispmanx grabber and support for spi devices
|
||||
cmake -DENABLE_DISPMANX=OFF -DENABLE_SPIDEV=OFF ..
|
||||
|
||||
# run make to build Hyperion
|
||||
make
|
||||
|
@ -8,3 +8,6 @@
|
||||
|
||||
// Define to enable the spi-device
|
||||
#cmakedefine ENABLE_SPIDEV
|
||||
|
||||
// Define to enable the spi-device
|
||||
#cmakedefine ENABLE_TINKERFORGE
|
||||
|
@ -7,22 +7,27 @@ DAEMON=hyperiond
|
||||
DAEMONOPTS="/etc/hyperion.config.json"
|
||||
DAEMON_PATH="/usr/bin"
|
||||
|
||||
NAME=$DEAMON
|
||||
NAME=$DAEMON
|
||||
DESC="Hyperion ambilight server"
|
||||
PIDFILE=/var/run/$NAME.pid
|
||||
SCRIPTNAME=/etc/init.d/$NAME
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
printf "%-50s" "Starting $NAME..."
|
||||
cd $DAEMON_PATH
|
||||
PID=`$DAEMON $DAEMONOPTS > /dev/null 2>&1 & echo $!`
|
||||
#echo "Saving PID" $PID " to " $PIDFILE
|
||||
if [ -z $PID ]; then
|
||||
printf "%s\n" "Fail"
|
||||
if [ $(pgrep -l $NAME |wc -l) = 1 ]
|
||||
then
|
||||
printf "%-50s\n" "Already running..."
|
||||
exit 1
|
||||
else
|
||||
echo $PID > $PIDFILE
|
||||
printf "%s\n" "Ok"
|
||||
printf "%-50s" "Starting $NAME..."
|
||||
cd $DAEMON_PATH
|
||||
PID=`$DAEMON $DAEMONOPTS > /dev/null 2>&1 & echo $!`
|
||||
#echo "Saving PID" $PID " to " $PIDFILE
|
||||
if [ -z $PID ]; then
|
||||
printf "%s\n" "Fail"
|
||||
else
|
||||
echo $PID > $PIDFILE
|
||||
printf "%s\n" "Ok"
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
status)
|
||||
@ -39,24 +44,31 @@ status)
|
||||
fi
|
||||
;;
|
||||
stop)
|
||||
printf "%-50s" "Stopping $NAME"
|
||||
PID=`cat $PIDFILE`
|
||||
cd $DAEMON_PATH
|
||||
if [ -f $PIDFILE ]; then
|
||||
kill -HUP $PID
|
||||
printf "%s\n" "Ok"
|
||||
rm -f $PIDFILE
|
||||
if [ -f $PIDFILE ]
|
||||
then
|
||||
printf "%-50s" "Stopping $NAME"
|
||||
PID=`cat $PIDFILE`
|
||||
cd $DAEMON_PATH
|
||||
if [ -f $PIDFILE ]; then
|
||||
kill -HUP $PID
|
||||
printf "%s\n" "Ok"
|
||||
rm -f $PIDFILE
|
||||
else
|
||||
printf "%s\n" "pidfile not found"
|
||||
fi
|
||||
else
|
||||
printf "%s\n" "pidfile not found"
|
||||
printf "%-50s\n" "No PID file $NAME not running?"
|
||||
fi
|
||||
;;
|
||||
|
||||
restart)
|
||||
$0 stop
|
||||
$0 start
|
||||
$0 stop
|
||||
$0 start
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "Usage: $0 {status|start|stop|restart}"
|
||||
exit 1
|
||||
esac
|
||||
|
||||
exit 0
|
||||
|
@ -14,7 +14,7 @@
|
||||
{
|
||||
"name" : "MyPi",
|
||||
"type" : "adalight",
|
||||
"output" : "/dev/ttyUSB0",
|
||||
"output" : "/dev/ttyUSB0",
|
||||
"rate" : 115200,
|
||||
"colorOrder" : "rgb"
|
||||
},
|
||||
@ -363,7 +363,7 @@
|
||||
{
|
||||
"paths" :
|
||||
[
|
||||
"/opt/hyperion/effects"
|
||||
"/home/dincs/projects/hyperion/effects"
|
||||
]
|
||||
},
|
||||
|
||||
|
5
dependencies/build/CMakeLists.txt
vendored
5
dependencies/build/CMakeLists.txt
vendored
@ -1,5 +1,6 @@
|
||||
|
||||
add_subdirectory(jsoncpp)
|
||||
add_subdirectory(getoptPlusPlus)
|
||||
add_subdirectory(serial)
|
||||
add_subdirectory(hidapi)
|
||||
add_subdirectory(jsoncpp)
|
||||
add_subdirectory(serial)
|
||||
add_subdirectory(tinkerforge)
|
||||
|
@ -280,7 +280,6 @@ void PresettableUniquelySwitchable::preset() {
|
||||
template<>
|
||||
PODParameter<string>::PODParameter(char shortOption, const char *longOption,
|
||||
const char* description) : CommonParameter<PresettableUniquelySwitchable>(shortOption, longOption, description) {
|
||||
preset();
|
||||
}
|
||||
|
||||
|
||||
|
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 @@
|
||||
08d42deff1de4c4296e4c6e22c783a0096ed3396
|
||||
d61b685eca164580cb39eb5bc3cf65b89afad410
|
10
effects/snake.json
Normal file
10
effects/snake.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"name" : "Snake",
|
||||
"script" : "snake.py",
|
||||
"args" :
|
||||
{
|
||||
"rotation-time" : 10.0,
|
||||
"color" : [255, 0, 0],
|
||||
"percentage" : 25
|
||||
}
|
||||
}
|
41
effects/snake.py
Normal file
41
effects/snake.py
Normal file
@ -0,0 +1,41 @@
|
||||
import hyperion
|
||||
import time
|
||||
import colorsys
|
||||
|
||||
# Get the parameters
|
||||
rotationTime = float(hyperion.args.get('rotation-time', 10.0))
|
||||
color = hyperion.args.get('color', (255,0,0))
|
||||
percentage = int(hyperion.args.get('percentage', 10))
|
||||
|
||||
# Check parameters
|
||||
rotationTime = max(0.1, rotationTime)
|
||||
percentage = max(1, min(percentage, 100))
|
||||
|
||||
# Process parameters
|
||||
factor = percentage/100.0
|
||||
hsv = colorsys.rgb_to_hsv(color[0]/255.0, color[1]/255.0, color[2]/255.0)
|
||||
|
||||
# Initialize the led data
|
||||
snakeLeds = max(1, int(hyperion.ledCount*factor))
|
||||
ledData = bytearray()
|
||||
|
||||
for i in range(hyperion.ledCount-snakeLeds):
|
||||
ledData += bytearray((0, 0, 0))
|
||||
|
||||
for i in range(1,snakeLeds+1):
|
||||
rgb = colorsys.hsv_to_rgb(hsv[0], hsv[1], hsv[2]/i)
|
||||
ledData += bytearray((int(rgb[0]*255), int(rgb[1]*255), int(rgb[2]*255)))
|
||||
|
||||
# Calculate the sleep time and rotation increment
|
||||
increment = 3
|
||||
sleepTime = rotationTime / hyperion.ledCount
|
||||
while sleepTime < 0.05:
|
||||
increment *= 2
|
||||
sleepTime *= 2
|
||||
increment %= hyperion.ledCount
|
||||
|
||||
# Start the write data loop
|
||||
while not hyperion.abort():
|
||||
hyperion.setColor(ledData)
|
||||
ledData = ledData[increment:] + ledData[:increment]
|
||||
time.sleep(sleepTime)
|
@ -1,4 +1,3 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
// Utils includes
|
||||
@ -126,7 +125,7 @@ namespace hyperion
|
||||
inline bool isBlack(const Pixel_T & color)
|
||||
{
|
||||
// Return the simple compare of the color against black
|
||||
return color.red < _blackborderThreshold && color.green < _blackborderThreshold && color.green < _blackborderThreshold;
|
||||
return color.red < _blackborderThreshold && color.green < _blackborderThreshold && color.blue < _blackborderThreshold;
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -55,5 +55,5 @@ private:
|
||||
|
||||
std::list<Effect *> _activeEffects;
|
||||
|
||||
PyThreadState * _mainThreadState;
|
||||
PyThreadState * _mainThreadState;
|
||||
};
|
||||
|
@ -73,7 +73,7 @@ private:
|
||||
const int _updateInterval_ms;
|
||||
/// The timeout of the led colors [ms]
|
||||
const int _timeout_ms;
|
||||
/// The priority of the led colors [ms]
|
||||
/// The priority of the led colors
|
||||
const int _priority;
|
||||
|
||||
/// The timer for generating events with the specified update rate
|
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,34 +4,60 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
// Qt includes
|
||||
#include <QObject>
|
||||
#include <QSocketNotifier>
|
||||
|
||||
// util includes
|
||||
#include <utils/Image.h>
|
||||
#include <utils/ColorRgb.h>
|
||||
#include <utils/VideoMode.h>
|
||||
|
||||
// grabber includes
|
||||
#include <grabber/VideoStandard.h>
|
||||
#include <grabber/PixelFormat.h>
|
||||
|
||||
/// Capture class for V4L2 devices
|
||||
///
|
||||
/// @see http://linuxtv.org/downloads/v4l-dvb-apis/capture-example.html
|
||||
class V4L2Grabber
|
||||
class V4L2Grabber : public QObject
|
||||
{
|
||||
public:
|
||||
typedef void (*ImageCallback)(void * arg, const Image<ColorRgb> & image);
|
||||
|
||||
enum VideoStandard {
|
||||
PAL, NTSC, NO_CHANGE
|
||||
};
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
V4L2Grabber(const std::string & device, int input, VideoStandard videoStandard, int width, int height, int cropHorizontal, int cropVertical, int frameDecimation, int pixelDecimation);
|
||||
V4L2Grabber(const std::string & device,
|
||||
int input,
|
||||
VideoStandard videoStandard, PixelFormat pixelFormat,
|
||||
int width,
|
||||
int height,
|
||||
int frameDecimation,
|
||||
int horizontalPixelDecimation,
|
||||
int verticalPixelDecimation);
|
||||
virtual ~V4L2Grabber();
|
||||
|
||||
void setCallback(ImageCallback callback, void * arg);
|
||||
public slots:
|
||||
void setCropping(int cropLeft,
|
||||
int cropRight,
|
||||
int cropTop,
|
||||
int cropBottom);
|
||||
|
||||
void set3D(VideoMode mode);
|
||||
|
||||
void setSignalThreshold(double redSignalThreshold,
|
||||
double greenSignalThreshold,
|
||||
double blueSignalThreshold,
|
||||
int noSignalCounterThreshold);
|
||||
|
||||
void start();
|
||||
|
||||
void capture(int frameCount = -1);
|
||||
|
||||
void stop();
|
||||
|
||||
signals:
|
||||
void newFrame(const Image<ColorRgb> & image);
|
||||
|
||||
private slots:
|
||||
int read_frame();
|
||||
|
||||
private:
|
||||
void open_device();
|
||||
|
||||
@ -51,8 +77,6 @@ private:
|
||||
|
||||
void stop_capturing();
|
||||
|
||||
int read_frame();
|
||||
|
||||
bool process_image(const void *p, int size);
|
||||
|
||||
void process_image(const uint8_t *p);
|
||||
@ -81,16 +105,25 @@ private:
|
||||
int _fileDescriptor;
|
||||
std::vector<buffer> _buffers;
|
||||
|
||||
uint32_t _pixelFormat;
|
||||
PixelFormat _pixelFormat;
|
||||
int _width;
|
||||
int _height;
|
||||
const int _cropWidth;
|
||||
const int _cropHeight;
|
||||
const int _frameDecimation;
|
||||
const int _pixelDecimation;
|
||||
int _frameByteSize;
|
||||
int _cropLeft;
|
||||
int _cropRight;
|
||||
int _cropTop;
|
||||
int _cropBottom;
|
||||
int _frameDecimation;
|
||||
int _horizontalPixelDecimation;
|
||||
int _verticalPixelDecimation;
|
||||
int _noSignalCounterThreshold;
|
||||
|
||||
ColorRgb _noSignalThresholdColor;
|
||||
|
||||
VideoMode _mode3D;
|
||||
|
||||
int _currentFrame;
|
||||
int _noSignalCounter;
|
||||
|
||||
ImageCallback _callback;
|
||||
void * _callbackArg;
|
||||
QSocketNotifier * _streamNotifier;
|
||||
};
|
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;
|
||||
}
|
@ -48,3 +48,16 @@ inline std::ostream& operator<<(std::ostream& os, const ColorRgb& color)
|
||||
os << "{" << unsigned(color.red) << "," << unsigned(color.green) << "," << unsigned(color.blue) << "}";
|
||||
return os;
|
||||
}
|
||||
|
||||
|
||||
/// Compare operator to check if a color is 'smaller' than another color
|
||||
inline bool operator<(const ColorRgb & lhs, const ColorRgb & rhs)
|
||||
{
|
||||
return (lhs.red < rhs.red) && (lhs.green < rhs.green) && (lhs.blue < rhs.blue);
|
||||
}
|
||||
|
||||
/// Compare operator to check if a color is 'smaller' than or 'equal' to another color
|
||||
inline bool operator<=(const ColorRgb & lhs, const ColorRgb & rhs)
|
||||
{
|
||||
return (lhs.red <= rhs.red) && (lhs.green <= rhs.green) && (lhs.blue <= rhs.blue);
|
||||
}
|
||||
|
@ -13,6 +13,18 @@ public:
|
||||
|
||||
typedef Pixel_T pixel_type;
|
||||
|
||||
///
|
||||
/// Default constructor for an image
|
||||
///
|
||||
Image() :
|
||||
_width(1),
|
||||
_height(1),
|
||||
_pixels(new Pixel_T[2]),
|
||||
_endOfPixels(_pixels + 1)
|
||||
{
|
||||
memset(_pixels, 0, 2*sizeof(Pixel_T));
|
||||
}
|
||||
|
||||
///
|
||||
/// Constructor for an image with specified width and height
|
||||
///
|
||||
@ -22,8 +34,8 @@ public:
|
||||
Image(const unsigned width, const unsigned height) :
|
||||
_width(width),
|
||||
_height(height),
|
||||
_pixels(new Pixel_T[width*height + 1]),
|
||||
_endOfPixels(_pixels + width*height)
|
||||
_pixels(new Pixel_T[width * height + 1]),
|
||||
_endOfPixels(_pixels + width * height)
|
||||
{
|
||||
memset(_pixels, 0, (_width*_height+1)*sizeof(Pixel_T));
|
||||
}
|
||||
@ -38,12 +50,24 @@ public:
|
||||
Image(const unsigned width, const unsigned height, const Pixel_T background) :
|
||||
_width(width),
|
||||
_height(height),
|
||||
_pixels(new Pixel_T[width*height + 1]),
|
||||
_endOfPixels(_pixels + width*height)
|
||||
_pixels(new Pixel_T[width * height + 1]),
|
||||
_endOfPixels(_pixels + width * height)
|
||||
{
|
||||
std::fill(_pixels, _endOfPixels, background);
|
||||
}
|
||||
|
||||
///
|
||||
/// Copy constructor for an image
|
||||
///
|
||||
Image(const Image & other) :
|
||||
_width(other._width),
|
||||
_height(other._height),
|
||||
_pixels(new Pixel_T[other._width * other._height + 1]),
|
||||
_endOfPixels(_pixels + other._width * other._height)
|
||||
{
|
||||
memcpy(_pixels, other._pixels, other._width * other._height * sizeof(Pixel_T));
|
||||
}
|
||||
|
||||
///
|
||||
/// Destructor
|
||||
///
|
||||
|
@ -1,5 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
|
||||
/**
|
||||
* Enumeration of the possible modes in which video can be playing (2D, 3D)
|
||||
*/
|
||||
@ -9,3 +12,21 @@ enum VideoMode
|
||||
VIDEO_3DSBS,
|
||||
VIDEO_3DTAB
|
||||
};
|
||||
|
||||
inline VideoMode parse3DMode(std::string videoMode)
|
||||
{
|
||||
// convert to lower case
|
||||
std::transform(videoMode.begin(), videoMode.end(), videoMode.begin(), ::tolower);
|
||||
|
||||
if (videoMode == "23DTAB")
|
||||
{
|
||||
return VIDEO_3DTAB;
|
||||
}
|
||||
else if (videoMode == "3DSBS")
|
||||
{
|
||||
return VIDEO_3DSBS;
|
||||
}
|
||||
|
||||
// return the default 2D
|
||||
return VIDEO_2D;
|
||||
}
|
||||
|
@ -12,7 +12,4 @@ add_subdirectory(leddevice)
|
||||
add_subdirectory(utils)
|
||||
add_subdirectory(xbmcvideochecker)
|
||||
add_subdirectory(effectengine)
|
||||
|
||||
if (ENABLE_DISPMANX)
|
||||
add_subdirectory(dispmanx-grabber)
|
||||
endif (ENABLE_DISPMANX)
|
||||
add_subdirectory(grabber)
|
||||
|
@ -28,9 +28,5 @@ add_library(boblightserver
|
||||
|
||||
target_link_libraries(boblightserver
|
||||
hyperion
|
||||
hyperion-utils)
|
||||
|
||||
qt4_use_modules(boblightserver
|
||||
Core
|
||||
Gui
|
||||
Network)
|
||||
hyperion-utils
|
||||
${QT_LIBRARIES})
|
||||
|
@ -19,9 +19,39 @@ PyMethodDef Effect::effectMethods[] = {
|
||||
{NULL, NULL, 0, NULL}
|
||||
};
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
// create the hyperion module
|
||||
struct PyModuleDef Effect::moduleDef = {
|
||||
PyModuleDef_HEAD_INIT,
|
||||
"hyperion", /* m_name */
|
||||
"Hyperion module", /* m_doc */
|
||||
-1, /* m_size */
|
||||
Effect::effectMethods, /* m_methods */
|
||||
NULL, /* m_reload */
|
||||
NULL, /* m_traverse */
|
||||
NULL, /* m_clear */
|
||||
NULL, /* m_free */
|
||||
};
|
||||
|
||||
Effect::Effect(int priority, int timeout, const std::string & script, const Json::Value & args) :
|
||||
PyObject* Effect::PyInit_hyperion()
|
||||
{
|
||||
return PyModule_Create(&moduleDef);
|
||||
}
|
||||
#else
|
||||
void Effect::PyInit_hyperion()
|
||||
{
|
||||
Py_InitModule("hyperion", effectMethods);
|
||||
}
|
||||
#endif
|
||||
|
||||
void Effect::registerHyperionExtensionModule()
|
||||
{
|
||||
PyImport_AppendInittab("hyperion", &PyInit_hyperion);
|
||||
}
|
||||
|
||||
Effect::Effect(PyThreadState * mainThreadState, int priority, int timeout, const std::string & script, const Json::Value & args) :
|
||||
QThread(),
|
||||
_mainThreadState(mainThreadState),
|
||||
_priority(priority),
|
||||
_timeout(timeout),
|
||||
_script(script),
|
||||
@ -44,20 +74,26 @@ Effect::~Effect()
|
||||
|
||||
void Effect::run()
|
||||
{
|
||||
// switch to the main thread state and acquire the GIL
|
||||
PyEval_RestoreThread(_mainThreadState);
|
||||
|
||||
// Initialize a new thread state
|
||||
PyEval_AcquireLock(); // Get the GIL
|
||||
_interpreterThreadState = Py_NewInterpreter();
|
||||
|
||||
// add methods extra builtin methods to the interpreter
|
||||
PyObject * thisCapsule = PyCapsule_New(this, nullptr, nullptr);
|
||||
PyObject * module = Py_InitModule4("hyperion", effectMethods, nullptr, thisCapsule, PYTHON_API_VERSION);
|
||||
// import the buildtin Hyperion module
|
||||
PyObject * module = PyImport_ImportModule("hyperion");
|
||||
|
||||
// add a capsule containing 'this' to the module to be able to retrieve the effect from the callback function
|
||||
PyObject_SetAttrString(module, "__effectObj", PyCapsule_New(this, nullptr, nullptr));
|
||||
|
||||
// add ledCount variable to the interpreter
|
||||
PyObject_SetAttrString(module, "ledCount", Py_BuildValue("i", _imageProcessor->getLedCount()));
|
||||
|
||||
// add a args variable to the interpreter
|
||||
PyObject_SetAttrString(module, "args", json2python(_args));
|
||||
//PyObject_SetAttrString(module, "args", Py_BuildValue("s", _args.c_str()));
|
||||
|
||||
// decref the module
|
||||
Py_XDECREF(module);
|
||||
|
||||
// Set the end time if applicable
|
||||
if (_timeout > 0)
|
||||
@ -119,19 +155,23 @@ PyObject *Effect::json2python(const Json::Value &json) const
|
||||
return Py_BuildValue("s", json.asCString());
|
||||
case Json::objectValue:
|
||||
{
|
||||
PyObject * obj = PyDict_New();
|
||||
PyObject * dict= PyDict_New();
|
||||
for (Json::Value::iterator i = json.begin(); i != json.end(); ++i)
|
||||
{
|
||||
PyDict_SetItemString(obj, i.memberName(), json2python(*i));
|
||||
PyObject * obj = json2python(*i);
|
||||
PyDict_SetItemString(dict, i.memberName(), obj);
|
||||
Py_XDECREF(obj);
|
||||
}
|
||||
return obj;
|
||||
return dict;
|
||||
}
|
||||
case Json::arrayValue:
|
||||
{
|
||||
PyObject * list = PyList_New(json.size());
|
||||
for (Json::Value::iterator i = json.begin(); i != json.end(); ++i)
|
||||
{
|
||||
PyList_SetItem(list, i.index(), json2python(*i));
|
||||
PyObject * obj = json2python(*i);
|
||||
PyList_SetItem(list, i.index(), obj);
|
||||
Py_XDECREF(obj);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
@ -144,7 +184,7 @@ PyObject *Effect::json2python(const Json::Value &json) const
|
||||
PyObject* Effect::wrapSetColor(PyObject *self, PyObject *args)
|
||||
{
|
||||
// get the effect
|
||||
Effect * effect = getEffect(self);
|
||||
Effect * effect = getEffect();
|
||||
|
||||
// check if we have aborted already
|
||||
if (effect->_abortRequested)
|
||||
@ -229,7 +269,7 @@ PyObject* Effect::wrapSetColor(PyObject *self, PyObject *args)
|
||||
PyObject* Effect::wrapSetImage(PyObject *self, PyObject *args)
|
||||
{
|
||||
// get the effect
|
||||
Effect * effect = getEffect(self);
|
||||
Effect * effect = getEffect();
|
||||
|
||||
// check if we have aborted already
|
||||
if (effect->_abortRequested)
|
||||
@ -292,7 +332,7 @@ PyObject* Effect::wrapSetImage(PyObject *self, PyObject *args)
|
||||
|
||||
PyObject* Effect::wrapAbort(PyObject *self, PyObject *)
|
||||
{
|
||||
Effect * effect = getEffect(self);
|
||||
Effect * effect = getEffect();
|
||||
|
||||
// Test if the effect has reached it end time
|
||||
if (effect->_timeout > 0 && QDateTime::currentMSecsSinceEpoch() > effect->_endTime)
|
||||
@ -303,8 +343,33 @@ PyObject* Effect::wrapAbort(PyObject *self, PyObject *)
|
||||
return Py_BuildValue("i", effect->_abortRequested ? 1 : 0);
|
||||
}
|
||||
|
||||
Effect * Effect::getEffect(PyObject *self)
|
||||
Effect * Effect::getEffect()
|
||||
{
|
||||
// Get the effect from the capsule in the self pointer
|
||||
return reinterpret_cast<Effect *>(PyCapsule_GetPointer(self, nullptr));
|
||||
// extract the module from the runtime
|
||||
PyObject * module = PyObject_GetAttrString(PyImport_AddModule("__main__"), "hyperion");
|
||||
|
||||
if (!PyModule_Check(module))
|
||||
{
|
||||
// something is wrong
|
||||
Py_XDECREF(module);
|
||||
std::cerr << "Unable to retrieve the effect object from the Python runtime" << std::endl;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// retrieve the capsule with the effect
|
||||
PyObject * effectCapsule = PyObject_GetAttrString(module, "__effectObj");
|
||||
Py_XDECREF(module);
|
||||
|
||||
if (!PyCapsule_CheckExact(effectCapsule))
|
||||
{
|
||||
// something is wrong
|
||||
Py_XDECREF(effectCapsule);
|
||||
std::cerr << "Unable to retrieve the effect object from the Python runtime" << std::endl;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Get the effect from the capsule
|
||||
Effect * effect = reinterpret_cast<Effect *>(PyCapsule_GetPointer(effectCapsule, nullptr));
|
||||
Py_XDECREF(effectCapsule);
|
||||
return effect;
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ class Effect : public QThread
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Effect(int priority, int timeout, const std::string & script, const Json::Value & args = Json::Value());
|
||||
Effect(PyThreadState * mainThreadState, int priority, int timeout, const std::string & script, const Json::Value & args = Json::Value());
|
||||
virtual ~Effect();
|
||||
|
||||
virtual void run();
|
||||
@ -23,6 +23,9 @@ public:
|
||||
|
||||
bool isAbortRequested() const;
|
||||
|
||||
/// This function registers the extension module in Python
|
||||
static void registerHyperionExtensionModule();
|
||||
|
||||
public slots:
|
||||
void abort();
|
||||
|
||||
@ -38,13 +41,22 @@ private:
|
||||
PyObject * json2python(const Json::Value & json) const;
|
||||
|
||||
// Wrapper methods for Python interpreter extra buildin methods
|
||||
static PyMethodDef effectMethods[];
|
||||
static PyObject* wrapSetColor(PyObject *self, PyObject *args);
|
||||
static PyMethodDef effectMethods[];
|
||||
static PyObject* wrapSetColor(PyObject *self, PyObject *args);
|
||||
static PyObject* wrapSetImage(PyObject *self, PyObject *args);
|
||||
static PyObject* wrapAbort(PyObject *self, PyObject *args);
|
||||
static Effect * getEffect(PyObject *self);
|
||||
static Effect * getEffect();
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
static struct PyModuleDef moduleDef;
|
||||
static PyObject* PyInit_hyperion();
|
||||
#else
|
||||
static void PyInit_hyperion();
|
||||
#endif
|
||||
|
||||
private:
|
||||
PyThreadState * _mainThreadState;
|
||||
|
||||
const int _priority;
|
||||
|
||||
const int _timeout;
|
||||
|
@ -54,6 +54,7 @@ EffectEngine::EffectEngine(Hyperion * hyperion, const Json::Value & jsonEffectCo
|
||||
|
||||
// initialize the python interpreter
|
||||
std::cout << "Initializing Python interpreter" << std::endl;
|
||||
Effect::registerHyperionExtensionModule();
|
||||
Py_InitializeEx(0);
|
||||
PyEval_InitThreads(); // Create the GIL
|
||||
_mainThreadState = PyEval_SaveThread();
|
||||
@ -151,7 +152,7 @@ int EffectEngine::runEffectScript(const std::string &script, const Json::Value &
|
||||
channelCleared(priority);
|
||||
|
||||
// create the effect
|
||||
Effect * effect = new Effect(priority, timeout, script, args);
|
||||
Effect * effect = new Effect(_mainThreadState, priority, timeout, script, args);
|
||||
connect(effect, SIGNAL(setColors(int,std::vector<ColorRgb>,int,bool)), _hyperion, SLOT(setColors(int,std::vector<ColorRgb>,int,bool)), Qt::QueuedConnection);
|
||||
connect(effect, SIGNAL(effectFinished(Effect*)), this, SLOT(effectFinished(Effect*)));
|
||||
_activeEffects.push_back(effect);
|
||||
|
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})
|
||||
|
||||
# Define the current source locations
|
||||
SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/dispmanx-grabber)
|
||||
SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/dispmanx-grabber)
|
||||
SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/grabber)
|
||||
SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/grabber/dispmanx)
|
||||
|
||||
# Group the headers that go through the MOC compiler
|
||||
SET(DispmanxGrabberQT_HEADERS
|
@ -7,8 +7,8 @@
|
||||
#include <hyperion/ImageProcessorFactory.h>
|
||||
#include <hyperion/ImageProcessor.h>
|
||||
|
||||
// Local-dispmanx includes
|
||||
#include <dispmanx-grabber/DispmanxWrapper.h>
|
||||
// Dispmanx grabber includes
|
||||
#include <grabber/DispmanxWrapper.h>
|
||||
#include "DispmanxFrameGrabber.h"
|
||||
|
||||
|
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 <linux/videodev2.h>
|
||||
|
||||
#include "V4L2Grabber.h"
|
||||
#include "grabber/V4L2Grabber.h"
|
||||
|
||||
#define CLEAR(x) memset(&(x), 0, sizeof(x))
|
||||
|
||||
@ -36,21 +36,36 @@ static void yuv2rgb(uint8_t y, uint8_t u, uint8_t v, uint8_t & r, uint8_t & g, u
|
||||
}
|
||||
|
||||
|
||||
V4L2Grabber::V4L2Grabber(const std::string &device, int input, VideoStandard videoStandard, int width, int height, int cropHorizontal, int cropVertical, int frameDecimation, int pixelDecimation) :
|
||||
V4L2Grabber::V4L2Grabber(const std::string & device,
|
||||
int input,
|
||||
VideoStandard videoStandard,
|
||||
PixelFormat pixelFormat,
|
||||
int width,
|
||||
int height,
|
||||
int frameDecimation,
|
||||
int horizontalPixelDecimation,
|
||||
int verticalPixelDecimation) :
|
||||
_deviceName(device),
|
||||
_ioMethod(IO_METHOD_MMAP),
|
||||
_fileDescriptor(-1),
|
||||
_buffers(),
|
||||
_pixelFormat(0),
|
||||
_pixelFormat(pixelFormat),
|
||||
_width(width),
|
||||
_height(height),
|
||||
_cropWidth(cropHorizontal),
|
||||
_cropHeight(cropVertical),
|
||||
_frameByteSize(-1),
|
||||
_cropLeft(0),
|
||||
_cropRight(0),
|
||||
_cropTop(0),
|
||||
_cropBottom(0),
|
||||
_frameDecimation(std::max(1, frameDecimation)),
|
||||
_pixelDecimation(std::max(1, pixelDecimation)),
|
||||
_horizontalPixelDecimation(std::max(1, horizontalPixelDecimation)),
|
||||
_verticalPixelDecimation(std::max(1, verticalPixelDecimation)),
|
||||
_noSignalCounterThreshold(50),
|
||||
_noSignalThresholdColor(ColorRgb{0,0,0}),
|
||||
_mode3D(VIDEO_2D),
|
||||
_currentFrame(0),
|
||||
_callback(nullptr),
|
||||
_callbackArg(nullptr)
|
||||
_noSignalCounter(0),
|
||||
_streamNotifier(nullptr)
|
||||
{
|
||||
open_device();
|
||||
init_device(videoStandard, input);
|
||||
@ -58,65 +73,53 @@ V4L2Grabber::V4L2Grabber(const std::string &device, int input, VideoStandard vid
|
||||
|
||||
V4L2Grabber::~V4L2Grabber()
|
||||
{
|
||||
// stop if the grabber was not stopped
|
||||
stop();
|
||||
uninit_device();
|
||||
close_device();
|
||||
}
|
||||
|
||||
void V4L2Grabber::setCallback(V4L2Grabber::ImageCallback callback, void *arg)
|
||||
void V4L2Grabber::setCropping(int cropLeft, int cropRight, int cropTop, int cropBottom)
|
||||
{
|
||||
_callback = callback;
|
||||
_callbackArg = arg;
|
||||
_cropLeft = cropLeft;
|
||||
_cropRight = cropRight;
|
||||
_cropTop = cropTop;
|
||||
_cropBottom = cropBottom;
|
||||
}
|
||||
|
||||
void V4L2Grabber::set3D(VideoMode mode)
|
||||
{
|
||||
_mode3D = mode;
|
||||
}
|
||||
|
||||
void V4L2Grabber::setSignalThreshold(double redSignalThreshold, double greenSignalThreshold, double blueSignalThreshold, int noSignalCounterThreshold)
|
||||
{
|
||||
_noSignalThresholdColor.red = uint8_t(255*redSignalThreshold);
|
||||
_noSignalThresholdColor.green = uint8_t(255*greenSignalThreshold);
|
||||
_noSignalThresholdColor.blue = uint8_t(255*blueSignalThreshold);
|
||||
_noSignalCounterThreshold = std::max(1, noSignalCounterThreshold);
|
||||
|
||||
std::cout << "V4L2 grabber signal threshold set to: " << _noSignalThresholdColor << std::endl;
|
||||
}
|
||||
|
||||
void V4L2Grabber::start()
|
||||
{
|
||||
start_capturing();
|
||||
}
|
||||
|
||||
void V4L2Grabber::capture(int frameCount)
|
||||
{
|
||||
for (int count = 0; count < frameCount || frameCount < 0; ++count)
|
||||
if (_streamNotifier != nullptr && !_streamNotifier->isEnabled())
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
// the set of file descriptors for select
|
||||
fd_set fds;
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(_fileDescriptor, &fds);
|
||||
|
||||
// timeout
|
||||
struct timeval tv;
|
||||
tv.tv_sec = 2;
|
||||
tv.tv_usec = 0;
|
||||
|
||||
// block until data is available
|
||||
int r = select(_fileDescriptor + 1, &fds, NULL, NULL, &tv);
|
||||
|
||||
if (-1 == r)
|
||||
{
|
||||
if (EINTR == errno)
|
||||
continue;
|
||||
throw_errno_exception("select");
|
||||
}
|
||||
|
||||
if (0 == r)
|
||||
{
|
||||
throw_exception("select timeout");
|
||||
}
|
||||
|
||||
if (read_frame())
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
/* EAGAIN - continue select loop. */
|
||||
}
|
||||
_streamNotifier->setEnabled(true);
|
||||
start_capturing();
|
||||
std::cout << "V4L2 grabber started" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void V4L2Grabber::stop()
|
||||
{
|
||||
stop_capturing();
|
||||
if (_streamNotifier != nullptr && _streamNotifier->isEnabled())
|
||||
{
|
||||
stop_capturing();
|
||||
_streamNotifier->setEnabled(false);
|
||||
std::cout << "V4L2 grabber stopped" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void V4L2Grabber::open_device()
|
||||
@ -145,6 +148,11 @@ void V4L2Grabber::open_device()
|
||||
oss << "Cannot open '" << _deviceName << "'";
|
||||
throw_errno_exception(oss.str());
|
||||
}
|
||||
|
||||
// create the notifier for when a new frame is available
|
||||
_streamNotifier = new QSocketNotifier(_fileDescriptor, QSocketNotifier::Read);
|
||||
_streamNotifier->setEnabled(false);
|
||||
connect(_streamNotifier, SIGNAL(activated(int)), this, SLOT(read_frame()));
|
||||
}
|
||||
|
||||
void V4L2Grabber::close_device()
|
||||
@ -153,6 +161,12 @@ void V4L2Grabber::close_device()
|
||||
throw_errno_exception("close");
|
||||
|
||||
_fileDescriptor = -1;
|
||||
|
||||
if (_streamNotifier != nullptr)
|
||||
{
|
||||
delete _streamNotifier;
|
||||
_streamNotifier = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void V4L2Grabber::init_read(unsigned int buffer_size)
|
||||
@ -334,7 +348,7 @@ void V4L2Grabber::init_device(VideoStandard videoStandard, int input)
|
||||
// set the video standard if needed
|
||||
switch (videoStandard)
|
||||
{
|
||||
case PAL:
|
||||
case VIDEOSTANDARD_PAL:
|
||||
{
|
||||
v4l2_std_id std_id = V4L2_STD_PAL;
|
||||
if (-1 == xioctl(VIDIOC_S_STD, &std_id))
|
||||
@ -343,7 +357,7 @@ void V4L2Grabber::init_device(VideoStandard videoStandard, int input)
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NTSC:
|
||||
case VIDEOSTANDARD_NTSC:
|
||||
{
|
||||
v4l2_std_id std_id = V4L2_STD_NTSC;
|
||||
if (-1 == xioctl(VIDIOC_S_STD, &std_id))
|
||||
@ -352,7 +366,7 @@ void V4L2Grabber::init_device(VideoStandard videoStandard, int input)
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NO_CHANGE:
|
||||
case VIDEOSTANDARD_NO_CHANGE:
|
||||
default:
|
||||
// No change to device settings
|
||||
break;
|
||||
@ -368,17 +382,25 @@ void V4L2Grabber::init_device(VideoStandard videoStandard, int input)
|
||||
throw_errno_exception("VIDIOC_G_FMT");
|
||||
}
|
||||
|
||||
// check pixel format
|
||||
switch (fmt.fmt.pix.pixelformat)
|
||||
// set the requested pixel format
|
||||
switch (_pixelFormat)
|
||||
{
|
||||
case V4L2_PIX_FMT_UYVY:
|
||||
case V4L2_PIX_FMT_YUYV:
|
||||
_pixelFormat = fmt.fmt.pix.pixelformat;
|
||||
case PIXELFORMAT_UYVY:
|
||||
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY;
|
||||
break;
|
||||
case PIXELFORMAT_YUYV:
|
||||
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
|
||||
break;
|
||||
case PIXELFORMAT_RGB32:
|
||||
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB32;
|
||||
break;
|
||||
case PIXELFORMAT_NO_CHANGE:
|
||||
default:
|
||||
throw_exception("Only pixel formats UYVY and YUYV are supported");
|
||||
// No change to device settings
|
||||
break;
|
||||
}
|
||||
|
||||
// set the requested withd and height
|
||||
if (_width > 0 || _height > 0)
|
||||
{
|
||||
if (_width > 0)
|
||||
@ -390,19 +412,19 @@ void V4L2Grabber::init_device(VideoStandard videoStandard, int input)
|
||||
{
|
||||
fmt.fmt.pix.height = _height;
|
||||
}
|
||||
}
|
||||
|
||||
// set the settings
|
||||
if (-1 == xioctl(VIDIOC_S_FMT, &fmt))
|
||||
{
|
||||
throw_errno_exception("VIDIOC_S_FMT");
|
||||
}
|
||||
// set the settings
|
||||
if (-1 == xioctl(VIDIOC_S_FMT, &fmt))
|
||||
{
|
||||
throw_errno_exception("VIDIOC_S_FMT");
|
||||
}
|
||||
|
||||
// get the format settings again
|
||||
// (the size may not have been accepted without an error)
|
||||
if (-1 == xioctl(VIDIOC_G_FMT, &fmt))
|
||||
{
|
||||
throw_errno_exception("VIDIOC_G_FMT");
|
||||
}
|
||||
// get the format settings again
|
||||
// (the size may not have been accepted without an error)
|
||||
if (-1 == xioctl(VIDIOC_G_FMT, &fmt))
|
||||
{
|
||||
throw_errno_exception("VIDIOC_G_FMT");
|
||||
}
|
||||
|
||||
// store width & height
|
||||
@ -412,6 +434,28 @@ void V4L2Grabber::init_device(VideoStandard videoStandard, int input)
|
||||
// print the eventually used width and height
|
||||
std::cout << "V4L2 width=" << _width << " height=" << _height << std::endl;
|
||||
|
||||
// check pixel format and frame size
|
||||
switch (fmt.fmt.pix.pixelformat)
|
||||
{
|
||||
case V4L2_PIX_FMT_UYVY:
|
||||
_pixelFormat = PIXELFORMAT_UYVY;
|
||||
_frameByteSize = _width * _height * 2;
|
||||
std::cout << "V4L2 pixel format=UYVY" << std::endl;
|
||||
break;
|
||||
case V4L2_PIX_FMT_YUYV:
|
||||
_pixelFormat = PIXELFORMAT_YUYV;
|
||||
_frameByteSize = _width * _height * 2;
|
||||
std::cout << "V4L2 pixel format=YUYV" << std::endl;
|
||||
break;
|
||||
case V4L2_PIX_FMT_RGB32:
|
||||
_pixelFormat = PIXELFORMAT_RGB32;
|
||||
_frameByteSize = _width * _height * 4;
|
||||
std::cout << "V4L2 pixel format=RGB32" << std::endl;
|
||||
break;
|
||||
default:
|
||||
throw_exception("Only pixel formats UYVY, YUYV, and RGB32 are supported");
|
||||
}
|
||||
|
||||
switch (_ioMethod) {
|
||||
case IO_METHOD_READ:
|
||||
init_read(fmt.fmt.pix.sizeimage);
|
||||
@ -627,9 +671,9 @@ bool V4L2Grabber::process_image(const void *p, int size)
|
||||
{
|
||||
// We do want a new frame...
|
||||
|
||||
if (size != 2*_width*_height)
|
||||
if (size != _frameByteSize)
|
||||
{
|
||||
std::cout << "Frame too small: " << size << " != " << (2*_width*_height) << std::endl;
|
||||
std::cout << "Frame too small: " << size << " != " << _frameByteSize << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -644,42 +688,103 @@ bool V4L2Grabber::process_image(const void *p, int size)
|
||||
|
||||
void V4L2Grabber::process_image(const uint8_t * data)
|
||||
{
|
||||
int width = (_width - 2 * _cropWidth + _pixelDecimation/2) / _pixelDecimation;
|
||||
int height = (_height - 2 * _cropHeight + _pixelDecimation/2) / _pixelDecimation;
|
||||
int width = _width;
|
||||
int height = _height;
|
||||
|
||||
Image<ColorRgb> image(width, height);
|
||||
|
||||
for (int ySource = _cropHeight + _pixelDecimation/2, yDest = 0; ySource < _height - _cropHeight; ySource += _pixelDecimation, ++yDest)
|
||||
switch (_mode3D)
|
||||
{
|
||||
for (int xSource = _cropWidth + _pixelDecimation/2, xDest = 0; xSource < _width - _cropWidth; xSource += _pixelDecimation, ++xDest)
|
||||
case VIDEO_3DSBS:
|
||||
width = _width/2;
|
||||
break;
|
||||
case VIDEO_3DTAB:
|
||||
height = _height/2;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// create output structure
|
||||
int outputWidth = (width - _cropLeft - _cropRight + _horizontalPixelDecimation/2) / _horizontalPixelDecimation;
|
||||
int outputHeight = (height - _cropTop - _cropBottom + _verticalPixelDecimation/2) / _verticalPixelDecimation;
|
||||
Image<ColorRgb> image(outputWidth, outputHeight);
|
||||
|
||||
for (int ySource = _cropTop + _verticalPixelDecimation/2, yDest = 0; ySource < height - _cropBottom; ySource += _verticalPixelDecimation, ++yDest)
|
||||
{
|
||||
for (int xSource = _cropLeft + _horizontalPixelDecimation/2, xDest = 0; xSource < width - _cropRight; xSource += _horizontalPixelDecimation, ++xDest)
|
||||
{
|
||||
int index = (_width * ySource + xSource) * 2;
|
||||
uint8_t y = 0;
|
||||
uint8_t u = 0;
|
||||
uint8_t v = 0;
|
||||
ColorRgb & rgb = image(xDest, yDest);
|
||||
|
||||
switch (_pixelFormat)
|
||||
{
|
||||
case V4L2_PIX_FMT_UYVY:
|
||||
y = data[index+1];
|
||||
u = (xSource%2 == 0) ? data[index ] : data[index-2];
|
||||
v = (xSource%2 == 0) ? data[index+2] : data[index ];
|
||||
case PIXELFORMAT_UYVY:
|
||||
{
|
||||
int index = (_width * ySource + xSource) * 2;
|
||||
uint8_t y = data[index+1];
|
||||
uint8_t u = (xSource%2 == 0) ? data[index ] : data[index-2];
|
||||
uint8_t v = (xSource%2 == 0) ? data[index+2] : data[index ];
|
||||
yuv2rgb(y, u, v, rgb.red, rgb.green, rgb.blue);
|
||||
}
|
||||
break;
|
||||
case V4L2_PIX_FMT_YUYV:
|
||||
y = data[index];
|
||||
u = (xSource%2 == 0) ? data[index+1] : data[index-1];
|
||||
v = (xSource%2 == 0) ? data[index+3] : data[index+1];
|
||||
case PIXELFORMAT_YUYV:
|
||||
{
|
||||
int index = (_width * ySource + xSource) * 2;
|
||||
uint8_t y = data[index];
|
||||
uint8_t u = (xSource%2 == 0) ? data[index+1] : data[index-1];
|
||||
uint8_t v = (xSource%2 == 0) ? data[index+3] : data[index+1];
|
||||
yuv2rgb(y, u, v, rgb.red, rgb.green, rgb.blue);
|
||||
}
|
||||
break;
|
||||
case PIXELFORMAT_RGB32:
|
||||
{
|
||||
int index = (_width * ySource + xSource) * 4;
|
||||
rgb.red = data[index ];
|
||||
rgb.green = data[index+1];
|
||||
rgb.blue = data[index+2];
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// this should not be possible
|
||||
break;
|
||||
}
|
||||
|
||||
ColorRgb & rgb = image(xDest, yDest);
|
||||
yuv2rgb(y, u, v, rgb.red, rgb.green, rgb.blue);
|
||||
}
|
||||
}
|
||||
|
||||
if (_callback != nullptr)
|
||||
// check signal (only in center of the resulting image, because some grabbers have noise values along the borders)
|
||||
bool noSignal = true;
|
||||
for (unsigned x = 0; noSignal && x < (image.width()>>1); ++x)
|
||||
{
|
||||
(*_callback)(_callbackArg, image);
|
||||
int xImage = (image.width()>>2) + x;
|
||||
|
||||
for (unsigned y = 0; noSignal && y < (image.height()>>1); ++y)
|
||||
{
|
||||
int yImage = (image.height()>>2) + y;
|
||||
|
||||
ColorRgb & rgb = image(xImage, yImage);
|
||||
noSignal &= rgb <= _noSignalThresholdColor;
|
||||
}
|
||||
}
|
||||
|
||||
if (noSignal)
|
||||
{
|
||||
++_noSignalCounter;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_noSignalCounter >= _noSignalCounterThreshold)
|
||||
{
|
||||
std::cout << "V4L2 Grabber: " << "Signal detected" << std::endl;
|
||||
}
|
||||
|
||||
_noSignalCounter = 0;
|
||||
}
|
||||
|
||||
if (_noSignalCounter < _noSignalCounterThreshold)
|
||||
{
|
||||
emit newFrame(image);
|
||||
}
|
||||
else if (_noSignalCounter == _noSignalCounterThreshold)
|
||||
{
|
||||
std::cout << "V4L2 Grabber: " << "Signal lost" << std::endl;
|
||||
}
|
||||
}
|
||||
|
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();
|
||||
}
|
@ -1,4 +1,3 @@
|
||||
|
||||
// STL includes
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
@ -33,17 +32,35 @@ ImageToLedsMap::ImageToLedsMap(
|
||||
|
||||
for (const Led& led : leds)
|
||||
{
|
||||
// skip leds without area
|
||||
if ((led.maxX_frac-led.minX_frac) < 1e-6 || (led.maxY_frac-led.minY_frac) < 1e-6)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Compute the index boundaries for this led
|
||||
const unsigned minX_idx = xOffset + unsigned(std::round((actualWidth-1) * led.minX_frac));
|
||||
const unsigned maxX_idx = xOffset + unsigned(std::round((actualWidth-1) * led.maxX_frac));
|
||||
const unsigned minY_idx = yOffset + unsigned(std::round((actualHeight-1) * led.minY_frac));
|
||||
const unsigned maxY_idx = yOffset + unsigned(std::round((actualHeight-1) * led.maxY_frac));
|
||||
unsigned minX_idx = xOffset + unsigned(std::round(actualWidth * led.minX_frac));
|
||||
unsigned maxX_idx = xOffset + unsigned(std::round(actualWidth * led.maxX_frac));
|
||||
unsigned minY_idx = yOffset + unsigned(std::round(actualHeight * led.minY_frac));
|
||||
unsigned maxY_idx = yOffset + unsigned(std::round(actualHeight * led.maxY_frac));
|
||||
|
||||
// make sure that the area is at least a single led large
|
||||
minX_idx = std::min(minX_idx, xOffset + actualWidth - 1);
|
||||
if (minX_idx == maxX_idx)
|
||||
{
|
||||
maxX_idx = minX_idx + 1;
|
||||
}
|
||||
minY_idx = std::min(minY_idx, yOffset + actualHeight - 1);
|
||||
if (minY_idx == maxY_idx)
|
||||
{
|
||||
maxY_idx = minY_idx + 1;
|
||||
}
|
||||
|
||||
// Add all the indices in the above defined rectangle to the indices for this led
|
||||
std::vector<unsigned> ledColors;
|
||||
for (unsigned y = minY_idx; y<=maxY_idx && y<height; ++y)
|
||||
for (unsigned y = minY_idx; y<maxY_idx && y<(yOffset+actualHeight); ++y)
|
||||
{
|
||||
for (unsigned x = minX_idx; x<=maxX_idx && x<width; ++x)
|
||||
for (unsigned x = minX_idx; x<maxX_idx && x<(xOffset+actualWidth); ++x)
|
||||
{
|
||||
ledColors.push_back(y*width + x);
|
||||
}
|
||||
|
@ -37,9 +37,5 @@ add_library(jsonserver
|
||||
target_link_libraries(jsonserver
|
||||
hyperion
|
||||
hyperion-utils
|
||||
jsoncpp)
|
||||
|
||||
qt4_use_modules(jsonserver
|
||||
Core
|
||||
Gui
|
||||
Network)
|
||||
jsoncpp
|
||||
${QT_LIBRARIES})
|
||||
|
@ -8,8 +8,8 @@ find_package(libusb-1.0 REQUIRED)
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
include_directories(
|
||||
../../include/hidapi
|
||||
${LIBUSB_1_INCLUDE_DIRS}) # for Lightpack device
|
||||
../../include/hidapi
|
||||
${LIBUSB_1_INCLUDE_DIRS}) # for Lightpack device
|
||||
|
||||
# Group the headers that go through the MOC compiler
|
||||
SET(Leddevice_QT_HEADERS
|
||||
@ -28,8 +28,7 @@ SET(Leddevice_HEADERS
|
||||
${CURRENT_SOURCE_DIR}/LedDevicePiBlaster.h
|
||||
${CURRENT_SOURCE_DIR}/LedDeviceSedu.h
|
||||
${CURRENT_SOURCE_DIR}/LedDeviceTest.h
|
||||
${CURRENT_SOURCE_DIR}/LedDeviceWs2812b.h
|
||||
${CURRENT_SOURCE_DIR}/LedDeviceWs2811.h
|
||||
${CURRENT_SOURCE_DIR}/LedDeviceHyperionUsbasp.h
|
||||
)
|
||||
|
||||
SET(Leddevice_SOURCES
|
||||
@ -44,8 +43,7 @@ SET(Leddevice_SOURCES
|
||||
${CURRENT_SOURCE_DIR}/LedDevicePiBlaster.cpp
|
||||
${CURRENT_SOURCE_DIR}/LedDeviceSedu.cpp
|
||||
${CURRENT_SOURCE_DIR}/LedDeviceTest.cpp
|
||||
${CURRENT_SOURCE_DIR}/LedDeviceWs2811.cpp
|
||||
${CURRENT_SOURCE_DIR}/LedDeviceWs2812b.cpp
|
||||
${CURRENT_SOURCE_DIR}/LedDeviceHyperionUsbasp.cpp
|
||||
)
|
||||
|
||||
if(ENABLE_SPIDEV)
|
||||
@ -67,6 +65,17 @@ if(ENABLE_SPIDEV)
|
||||
)
|
||||
endif(ENABLE_SPIDEV)
|
||||
|
||||
if(ENABLE_TINKERFORGE)
|
||||
SET(Leddevice_HEADERS
|
||||
${Leddevice_HEADERS}
|
||||
${CURRENT_SOURCE_DIR}/LedDeviceTinkerforge.h
|
||||
)
|
||||
SET(Leddevice_SOURCES
|
||||
${Leddevice_SOURCES}
|
||||
${CURRENT_SOURCE_DIR}/LedDeviceTinkerforge.cpp
|
||||
)
|
||||
endif(ENABLE_TINKERFORGE)
|
||||
|
||||
|
||||
QT4_WRAP_CPP(Leddevice_HEADERS_MOC ${Leddevice_QT_HEADERS})
|
||||
|
||||
@ -78,12 +87,17 @@ add_library(leddevice
|
||||
)
|
||||
|
||||
target_link_libraries(leddevice
|
||||
hyperion-utils
|
||||
serialport
|
||||
${LIBUSB_1_LIBRARIES} #apt-get install libusb-1.0-0-dev
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
${QT_LIBRARIES}
|
||||
hyperion-utils
|
||||
serialport
|
||||
${LIBUSB_1_LIBRARIES} #apt-get install libusb-1.0-0-dev
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
${QT_LIBRARIES}
|
||||
)
|
||||
|
||||
if(ENABLE_TINKERFORGE)
|
||||
target_link_libraries(leddevice tinkerforge)
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
target_link_libraries(leddevice hidapi-mac)
|
||||
else()
|
||||
|
@ -1,3 +1,6 @@
|
||||
// Stl includes
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
|
||||
// Build configuration
|
||||
#include <HyperionConfig.h>
|
||||
@ -13,6 +16,10 @@
|
||||
#include "LedDeviceWs2801.h"
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_TINKERFORGE
|
||||
#include "LedDeviceTinkerforge.h"
|
||||
#endif
|
||||
|
||||
#include "LedDeviceAdalight.h"
|
||||
#include "LedDeviceLightpack.h"
|
||||
#include "LedDeviceMultiLightpack.h"
|
||||
@ -20,8 +27,7 @@
|
||||
#include "LedDevicePiBlaster.h"
|
||||
#include "LedDeviceSedu.h"
|
||||
#include "LedDeviceTest.h"
|
||||
#include "LedDeviceWs2811.h"
|
||||
#include "LedDeviceWs2812b.h"
|
||||
#include "LedDeviceHyperionUsbasp.h"
|
||||
|
||||
LedDevice * LedDeviceFactory::construct(const Json::Value & deviceConfig)
|
||||
{
|
||||
@ -84,23 +90,20 @@ LedDevice * LedDeviceFactory::construct(const Json::Value & deviceConfig)
|
||||
device = deviceWs2801;
|
||||
}
|
||||
#endif
|
||||
// else if (type == "ws2811")
|
||||
// {
|
||||
// const std::string output = deviceConfig["output"].asString();
|
||||
// const std::string outputSpeed = deviceConfig["output"].asString();
|
||||
// const std::string timingOption = deviceConfig["timingOption"].asString();
|
||||
#ifdef ENABLE_TINKERFORGE
|
||||
else if (type=="tinkerforge")
|
||||
{
|
||||
const std::string host = deviceConfig.get("output", "127.0.0.1").asString();
|
||||
const uint16_t port = deviceConfig.get("port", 4223).asInt();
|
||||
const std::string uid = deviceConfig["uid"].asString();
|
||||
const unsigned rate = deviceConfig["rate"].asInt();
|
||||
|
||||
// ws2811::SpeedMode speedMode = (outputSpeed == "high")? ws2811::highspeed : ws2811::lowspeed;
|
||||
// if (outputSpeed != "high" && outputSpeed != "low")
|
||||
// {
|
||||
// std::cerr << "Incorrect speed-mode selected for WS2811: " << outputSpeed << " != {'high', 'low'}" << std::endl;
|
||||
// }
|
||||
LedDeviceTinkerforge* deviceTinkerforge = new LedDeviceTinkerforge(host, port, uid, rate);
|
||||
deviceTinkerforge->open();
|
||||
|
||||
// LedDeviceWs2811 * deviceWs2811 = new LedDeviceWs2811(output, ws2811::fromString(timingOption, ws2811::option_2855), speedMode);
|
||||
// deviceWs2811->open();
|
||||
|
||||
// device = deviceWs2811;
|
||||
// }
|
||||
device = deviceTinkerforge;
|
||||
}
|
||||
#endif
|
||||
else if (type == "lightpack")
|
||||
{
|
||||
const std::string output = deviceConfig.get("output", "").asString();
|
||||
@ -144,18 +147,23 @@ LedDevice * LedDeviceFactory::construct(const Json::Value & deviceConfig)
|
||||
|
||||
device = deviceSedu;
|
||||
}
|
||||
else if (type == "hyperion-usbasp-ws2801")
|
||||
{
|
||||
LedDeviceHyperionUsbasp * deviceHyperionUsbasp = new LedDeviceHyperionUsbasp(LedDeviceHyperionUsbasp::CMD_WRITE_WS2801);
|
||||
deviceHyperionUsbasp->open();
|
||||
device = deviceHyperionUsbasp;
|
||||
}
|
||||
else if (type == "hyperion-usbasp-ws2812")
|
||||
{
|
||||
LedDeviceHyperionUsbasp * deviceHyperionUsbasp = new LedDeviceHyperionUsbasp(LedDeviceHyperionUsbasp::CMD_WRITE_WS2812);
|
||||
deviceHyperionUsbasp->open();
|
||||
device = deviceHyperionUsbasp;
|
||||
}
|
||||
else if (type == "test")
|
||||
{
|
||||
const std::string output = deviceConfig["output"].asString();
|
||||
device = new LedDeviceTest(output);
|
||||
}
|
||||
else if (type == "ws2812b")
|
||||
{
|
||||
LedDeviceWs2812b * deviceWs2812b = new LedDeviceWs2812b();
|
||||
deviceWs2812b->open();
|
||||
|
||||
device = deviceWs2812b;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Unable to create device " << type << std::endl;
|
||||
|
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)
|
||||
{
|
||||
unsigned messageLength = 4 + 2*ledValues.size() + ledValues.size()/8 + 1;
|
||||
// Reconfigure if the current connfiguration does not match the required configuration
|
||||
if (4 + 2*ledValues.size() != _ledBuffer.size())
|
||||
if (messageLength != _ledBuffer.size())
|
||||
{
|
||||
// Initialise the buffer
|
||||
_ledBuffer.resize(4 + 2*ledValues.size(), 0x00);
|
||||
_ledBuffer.resize(messageLength, 0x00);
|
||||
}
|
||||
|
||||
// Copy the colors from the ColorRgb vector to the Ldp6803 data vector
|
||||
|
@ -28,7 +28,7 @@ int LedDeviceSedu::write(const std::vector<ColorRgb> &ledValues)
|
||||
{
|
||||
if (_ledBuffer.size() == 0)
|
||||
{
|
||||
std::vector<FrameSpec> frameSpecs{{0xA0, 96}, {0xA1, 256}, {0xA2, 512}, {0xB0, 768}, {0xB1, 1536}, {0xB2, 3072} };
|
||||
std::vector<FrameSpec> frameSpecs{{0xA1, 256}, {0xA2, 512}, {0xB0, 768}, {0xB1, 1536}, {0xB2, 3072} };
|
||||
|
||||
const unsigned reqColorChannels = ledValues.size() * sizeof(ColorRgb);
|
||||
|
||||
|
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;
|
||||
|
||||
};
|
@ -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;
|
||||
};
|
@ -44,9 +44,5 @@ add_library(protoserver
|
||||
target_link_libraries(protoserver
|
||||
hyperion
|
||||
hyperion-utils
|
||||
${PROTOBUF_LIBRARIES})
|
||||
|
||||
qt4_use_modules(protoserver
|
||||
Core
|
||||
Gui
|
||||
Network)
|
||||
${PROTOBUF_LIBRARIES}
|
||||
${QT_LIBRARIES})
|
||||
|
@ -28,11 +28,7 @@ add_executable(hyperion-remote
|
||||
${hyperion-remote_HEADERS}
|
||||
${hyperion-remote_SOURCES})
|
||||
|
||||
qt4_use_modules(hyperion-remote
|
||||
Core
|
||||
Gui
|
||||
Network)
|
||||
|
||||
target_link_libraries(hyperion-remote
|
||||
jsoncpp
|
||||
getoptPlusPlus)
|
||||
getoptPlusPlus
|
||||
${QT_LIBRARIES})
|
||||
|
@ -1,5 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
// STL includes
|
||||
#include <algorithm>
|
||||
|
||||
// Qt includes
|
||||
#include <QColor>
|
||||
#include <QImage>
|
||||
|
@ -1,8 +1,10 @@
|
||||
// stl includes
|
||||
#include <clocale>
|
||||
#include <initializer_list>
|
||||
|
||||
// Qt includes
|
||||
#include <QCoreApplication>
|
||||
#include <QLocale>
|
||||
|
||||
// getoptPlusPLus includes
|
||||
#include <getoptPlusPlus/getoptpp.h>
|
||||
@ -28,6 +30,10 @@ int main(int argc, char * argv[])
|
||||
{
|
||||
QCoreApplication app(argc, argv);
|
||||
|
||||
// force the locale
|
||||
setlocale(LC_ALL, "C");
|
||||
QLocale::setDefault(QLocale::c());
|
||||
|
||||
try
|
||||
{
|
||||
// some default settings
|
||||
@ -42,8 +48,8 @@ int main(int argc, char * argv[])
|
||||
IntParameter & argDuration = parameters.add<IntParameter> ('d', "duration" , "Specify how long the leds should be switched on in millseconds [default: infinity]");
|
||||
ColorParameter & argColor = parameters.add<ColorParameter> ('c', "color" , "Set all leds to a constant color (either RRGGBB hex value or a color name. The color may be repeated multiple time like: RRGGBBRRGGBB)");
|
||||
ImageParameter & argImage = parameters.add<ImageParameter> ('i', "image" , "Set the leds to the colors according to the given image file");
|
||||
StringParameter & argEffect = parameters.add<StringParameter> ('e', "effect" , "Enable the effect with the given name");
|
||||
StringParameter & argEffectArgs = parameters.add<StringParameter> (0x0, "effectArgs", "Arguments to use in combination with the specified effect. Should be a Json object string.");
|
||||
StringParameter & argEffect = parameters.add<StringParameter> ('e', "effect" , "Enable the effect with the given name");
|
||||
StringParameter & argEffectArgs = parameters.add<StringParameter> (0x0, "effectArgs", "Arguments to use in combination with the specified effect. Should be a Json object string.");
|
||||
SwitchParameter<> & argServerInfo = parameters.add<SwitchParameter<> >('l', "list" , "List server info");
|
||||
SwitchParameter<> & argClear = parameters.add<SwitchParameter<> >('x', "clear" , "Clear data for the priority channel provided by the -p option");
|
||||
SwitchParameter<> & argClearAll = parameters.add<SwitchParameter<> >(0x0, "clearall" , "Clear data for all active priority channels");
|
||||
@ -77,13 +83,13 @@ int main(int argc, char * argv[])
|
||||
bool colorTransform = argSaturation.isSet() || argValue.isSet() || argThreshold.isSet() || argGamma.isSet() || argBlacklevel.isSet() || argWhitelevel.isSet();
|
||||
|
||||
// check that exactly one command was given
|
||||
int commandCount = count({argColor.isSet(), argImage.isSet(), argEffect.isSet(), argServerInfo.isSet(), argClear.isSet(), argClearAll.isSet(), colorTransform});
|
||||
int commandCount = count({argColor.isSet(), argImage.isSet(), argEffect.isSet(), argServerInfo.isSet(), argClear.isSet(), argClearAll.isSet(), colorTransform});
|
||||
if (commandCount != 1)
|
||||
{
|
||||
std::cerr << (commandCount == 0 ? "No command found." : "Multiple commands found.") << " Provide exactly one of the following options:" << std::endl;
|
||||
std::cerr << " " << argColor.usageLine() << std::endl;
|
||||
std::cerr << " " << argImage.usageLine() << std::endl;
|
||||
std::cerr << " " << argEffect.usageLine() << std::endl;
|
||||
std::cerr << " " << argEffect.usageLine() << std::endl;
|
||||
std::cerr << " " << argServerInfo.usageLine() << std::endl;
|
||||
std::cerr << " " << argClear.usageLine() << std::endl;
|
||||
std::cerr << " " << argClearAll.usageLine() << std::endl;
|
||||
@ -110,11 +116,11 @@ int main(int argc, char * argv[])
|
||||
{
|
||||
connection.setImage(argImage.getValue(), argPriority.getValue(), argDuration.getValue());
|
||||
}
|
||||
else if (argEffect.isSet())
|
||||
{
|
||||
connection.setEffect(argEffect.getValue(), argEffectArgs.getValue(), argPriority.getValue(), argDuration.getValue());
|
||||
}
|
||||
else if (argServerInfo.isSet())
|
||||
else if (argEffect.isSet())
|
||||
{
|
||||
connection.setEffect(argEffect.getValue(), argEffectArgs.getValue(), argPriority.getValue(), argDuration.getValue());
|
||||
}
|
||||
else if (argServerInfo.isSet())
|
||||
{
|
||||
QString info = connection.getServerInfo();
|
||||
std::cout << "Server info:\n" << info.toStdString() << std::endl;
|
||||
|
@ -14,18 +14,22 @@ include_directories(
|
||||
${QT_INCLUDES}
|
||||
)
|
||||
|
||||
set(Hyperion_V4L2_HEADERS
|
||||
V4L2Grabber.h
|
||||
ProtoConnection.h
|
||||
set(Hyperion_V4L2_QT_HEADERS
|
||||
ImageHandler.h
|
||||
ScreenshotHandler.h
|
||||
)
|
||||
|
||||
set(Hyperion_V4L2_HEADERS
|
||||
VideoStandardParameter.h
|
||||
PixelFormatParameter.h
|
||||
ProtoConnection.h
|
||||
)
|
||||
|
||||
set(Hyperion_V4L2_SOURCES
|
||||
hyperion-v4l2.cpp
|
||||
V4L2Grabber.cpp
|
||||
ProtoConnection.cpp
|
||||
ImageHandler.cpp
|
||||
ScreenshotHandler.cpp
|
||||
)
|
||||
|
||||
set(Hyperion_V4L2_PROTOS
|
||||
@ -36,22 +40,23 @@ protobuf_generate_cpp(Hyperion_V4L2_PROTO_SRCS Hyperion_V4L2_PROTO_HDRS
|
||||
${Hyperion_V4L2_PROTOS}
|
||||
)
|
||||
|
||||
QT4_WRAP_CPP(Hyperion_V4L2_MOC_SOURCES ${Hyperion_V4L2_QT_HEADERS})
|
||||
|
||||
add_executable(hyperion-v4l2
|
||||
${Hyperion_V4L2_HEADERS}
|
||||
${Hyperion_V4L2_SOURCES}
|
||||
${Hyperion_V4L2_QT_HEADERS}
|
||||
${Hyperion_V4L2_MOC_SOURCES}
|
||||
${Hyperion_V4L2_PROTO_SRCS}
|
||||
${Hyperion_V4L2_PROTO_HDRS}
|
||||
)
|
||||
|
||||
target_link_libraries(hyperion-v4l2
|
||||
v4l2-grabber
|
||||
getoptPlusPlus
|
||||
blackborder
|
||||
hyperion-utils
|
||||
${PROTOBUF_LIBRARIES}
|
||||
pthread
|
||||
${QT_LIBRARIES}
|
||||
)
|
||||
|
||||
qt4_use_modules(hyperion-v4l2
|
||||
Core
|
||||
Gui
|
||||
Network)
|
||||
|
@ -1,41 +1,18 @@
|
||||
// hyperion-v4l2 includes
|
||||
#include "ImageHandler.h"
|
||||
|
||||
ImageHandler::ImageHandler(const std::string &address, int priority, double signalThreshold, bool skipProtoReply) :
|
||||
_priority(priority),
|
||||
_connection(address),
|
||||
_signalThreshold(signalThreshold),
|
||||
_signalProcessor(100, 50, 0, uint8_t(std::min(255, std::max(0, int(255*signalThreshold)))))
|
||||
ImageHandler::ImageHandler(const std::string & address, int priority, bool skipProtoReply) :
|
||||
_priority(priority),
|
||||
_connection(address)
|
||||
{
|
||||
_connection.setSkipReply(skipProtoReply);
|
||||
}
|
||||
|
||||
void ImageHandler::receiveImage(const Image<ColorRgb> &image)
|
||||
ImageHandler::~ImageHandler()
|
||||
{
|
||||
// check if we should do signal detection
|
||||
if (_signalThreshold < 0)
|
||||
{
|
||||
_connection.setImage(image, _priority, 200);
|
||||
}
|
||||
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, 200);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ImageHandler::imageCallback(void *arg, const Image<ColorRgb> &image)
|
||||
void ImageHandler::receiveImage(const Image<ColorRgb> & image)
|
||||
{
|
||||
ImageHandler * handler = static_cast<ImageHandler *>(arg);
|
||||
handler->receiveImage(image);
|
||||
_connection.setImage(image, _priority, 1000);
|
||||
}
|
||||
|
||||
|
@ -1,34 +1,31 @@
|
||||
// blackborder includes
|
||||
#include <blackborder/BlackBorderProcessor.h>
|
||||
// Qt includes
|
||||
#include <QObject>
|
||||
|
||||
// hyperion-v4l includes
|
||||
// hyperion includes
|
||||
#include <utils/Image.h>
|
||||
#include <utils/ColorRgb.h>
|
||||
|
||||
// hyperion v4l2 includes
|
||||
#include "ProtoConnection.h"
|
||||
|
||||
/// This class handles callbacks from the V4L2 grabber
|
||||
class ImageHandler
|
||||
class ImageHandler : public QObject
|
||||
{
|
||||
public:
|
||||
ImageHandler(const std::string & address, int priority, double signalThreshold, bool skipProtoReply);
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ImageHandler(const std::string & address, int priority, bool skipProtoReply);
|
||||
virtual ~ImageHandler();
|
||||
|
||||
public slots:
|
||||
/// Handle a single image
|
||||
/// @param image The image to process
|
||||
void receiveImage(const Image<ColorRgb> & image);
|
||||
|
||||
/// static function used to direct callbacks to a ImageHandler object
|
||||
/// @param arg This should be an ImageHandler instance
|
||||
/// @param image The image to process
|
||||
static void imageCallback(void * arg, const Image<ColorRgb> & image);
|
||||
|
||||
private:
|
||||
/// Priority for calls to Hyperion
|
||||
const int _priority;
|
||||
|
||||
/// Hyperion proto connection object
|
||||
ProtoConnection _connection;
|
||||
|
||||
/// Threshold used for signal detection
|
||||
double _signalThreshold;
|
||||
|
||||
/// Blackborder detector which is used as a signal detector (unknown border = no signal)
|
||||
hyperion::BlackBorderProcessor _signalProcessor;
|
||||
};
|
||||
|
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
|
||||
#include <getoptPlusPlus/getoptpp.h>
|
||||
|
||||
// grabber includes
|
||||
#include <grabber/VideoStandard.h>
|
||||
|
||||
using namespace vlofgren;
|
||||
|
||||
/// Data parameter for the video standard
|
||||
typedef vlofgren::PODParameter<V4L2Grabber::VideoStandard> VideoStandardParameter;
|
||||
typedef vlofgren::PODParameter<VideoStandard> VideoStandardParameter;
|
||||
|
||||
namespace vlofgren {
|
||||
/// Translates a string (as passed on the commandline) to a color standard
|
||||
@ -13,24 +16,24 @@ namespace vlofgren {
|
||||
/// @return The color standard
|
||||
/// @throws Parameter::ParameterRejected If the string did not result in a video standard
|
||||
template<>
|
||||
V4L2Grabber::VideoStandard VideoStandardParameter::validate(const std::string& s) throw (Parameter::ParameterRejected)
|
||||
VideoStandard VideoStandardParameter::validate(const std::string& s) throw (Parameter::ParameterRejected)
|
||||
{
|
||||
QString input = QString::fromStdString(s).toLower();
|
||||
|
||||
if (input == "pal")
|
||||
{
|
||||
return V4L2Grabber::PAL;
|
||||
return VIDEOSTANDARD_PAL;
|
||||
}
|
||||
else if (input == "ntsc")
|
||||
{
|
||||
return V4L2Grabber::NTSC;
|
||||
return VIDEOSTANDARD_NTSC;
|
||||
}
|
||||
else if (input == "no-change")
|
||||
{
|
||||
return V4L2Grabber::NO_CHANGE;
|
||||
return VIDEOSTANDARD_NO_CHANGE;
|
||||
}
|
||||
|
||||
throw Parameter::ParameterRejected("Invalid value for video standard. Valid values are: PAL, NTSC, and NO-CHANGE");
|
||||
return V4L2Grabber::NO_CHANGE;
|
||||
return VIDEOSTANDARD_NO_CHANGE;
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
|
||||
// STL includes
|
||||
#include <csignal>
|
||||
#include <iomanip>
|
||||
#include <clocale>
|
||||
|
||||
// QT includes
|
||||
#include <QImage>
|
||||
#include <QCoreApplication>
|
||||
|
||||
// getoptPlusPLus includes
|
||||
#include <getoptPlusPlus/getoptpp.h>
|
||||
@ -12,11 +12,15 @@
|
||||
// blackborder includes
|
||||
#include <blackborder/BlackBorderProcessor.h>
|
||||
|
||||
// grabber includes
|
||||
#include "grabber/V4L2Grabber.h"
|
||||
|
||||
// hyperion-v4l2 includes
|
||||
#include "V4L2Grabber.h"
|
||||
#include "ProtoConnection.h"
|
||||
#include "VideoStandardParameter.h"
|
||||
#include "PixelFormatParameter.h"
|
||||
#include "ImageHandler.h"
|
||||
#include "ScreenshotHandler.h"
|
||||
|
||||
using namespace vlofgren;
|
||||
|
||||
@ -30,6 +34,15 @@ void saveScreenshot(void *, const Image<ColorRgb> & image)
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
QCoreApplication app(argc, argv);
|
||||
|
||||
// force the locale
|
||||
setlocale(LC_ALL, "C");
|
||||
QLocale::setDefault(QLocale::c());
|
||||
|
||||
// register the image type to use in signals
|
||||
qRegisterMetaType<Image<ColorRgb>>("Image<ColorRgb>");
|
||||
|
||||
try
|
||||
{
|
||||
// create the option parser and initialize all parameters
|
||||
@ -38,15 +51,25 @@ int main(int argc, char** argv)
|
||||
|
||||
StringParameter & argDevice = parameters.add<StringParameter> ('d', "device", "The device to use [default=/dev/video0]");
|
||||
VideoStandardParameter & argVideoStandard = parameters.add<VideoStandardParameter>('v', "video-standard", "The used video standard. Valid values are PAL or NTSC (optional)");
|
||||
PixelFormatParameter & argPixelFormat = parameters.add<PixelFormatParameter> (0x0, "pixel-format", "The use pixel format. Valid values are YUYV, UYVY, and RGB32 (optional)");
|
||||
IntParameter & argInput = parameters.add<IntParameter> (0x0, "input", "Input channel (optional)");
|
||||
IntParameter & argWidth = parameters.add<IntParameter> (0x0, "width", "Try to set the width of the video input (optional)");
|
||||
IntParameter & argHeight = parameters.add<IntParameter> (0x0, "height", "Try to set the height of the video input (optional)");
|
||||
IntParameter & argCropWidth = parameters.add<IntParameter> (0x0, "crop-width", "Number of pixels to crop from the left and right sides in the picture before decimation [default=0]");
|
||||
IntParameter & argCropHeight = parameters.add<IntParameter> (0x0, "crop-height", "Number of pixels to crop from the top and the bottom in the picture before decimation [default=0]");
|
||||
IntParameter & argCropWidth = parameters.add<IntParameter> (0x0, "crop-width", "Number of pixels to crop from the left and right sides of the picture before decimation [default=0]");
|
||||
IntParameter & argCropHeight = parameters.add<IntParameter> (0x0, "crop-height", "Number of pixels to crop from the top and the bottom of the picture before decimation [default=0]");
|
||||
IntParameter & argCropLeft = parameters.add<IntParameter> (0x0, "crop-left", "Number of pixels to crop from the left of the picture before decimation (overrides --crop-width)");
|
||||
IntParameter & argCropRight = parameters.add<IntParameter> (0x0, "crop-right", "Number of pixels to crop from the right of the picture before decimation (overrides --crop-width)");
|
||||
IntParameter & argCropTop = parameters.add<IntParameter> (0x0, "crop-top", "Number of pixels to crop from the top of the picture before decimation (overrides --crop-height)");
|
||||
IntParameter & argCropBottom = parameters.add<IntParameter> (0x0, "crop-bottom", "Number of pixels to crop from the bottom of the picture before decimation (overrides --crop-height)");
|
||||
IntParameter & argSizeDecimation = parameters.add<IntParameter> ('s', "size-decimator", "Decimation factor for the output size [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");
|
||||
DoubleParameter & argSignalThreshold = parameters.add<DoubleParameter> ('t', "signal-threshold", "The signal threshold for detecting the presence of a signal. Value should be between 0.0 and 1.0.");
|
||||
DoubleParameter & argRedSignalThreshold = parameters.add<DoubleParameter> (0x0, "red-threshold", "The red signal threshold. Value should be between 0.0 and 1.0. (overrides --signal-threshold)");
|
||||
DoubleParameter & argGreenSignalThreshold = parameters.add<DoubleParameter> (0x0, "green-threshold", "The green signal threshold. Value should be between 0.0 and 1.0. (overrides --signal-threshold)");
|
||||
DoubleParameter & argBlueSignalThreshold = parameters.add<DoubleParameter> (0x0, "blue-threshold", "The blue signal threshold. Value should be between 0.0 and 1.0. (overrides --signal-threshold)");
|
||||
SwitchParameter<> & arg3DSBS = parameters.add<SwitchParameter<>> (0x0, "3DSBS", "Interpret the incoming video stream as 3D side-by-side");
|
||||
SwitchParameter<> & arg3DTAB = parameters.add<SwitchParameter<>> (0x0, "3DTAB", "Interpret the incoming video stream as 3D top-and-bottom");
|
||||
StringParameter & argAddress = parameters.add<StringParameter> ('a', "address", "Set the address of the hyperion server [default: 127.0.0.1:19445]");
|
||||
IntParameter & argPriority = parameters.add<IntParameter> ('p', "priority", "Use the provided priority channel (the lower the number, the higher the priority) [default: 800]");
|
||||
SwitchParameter<> & argSkipReply = parameters.add<SwitchParameter<>> (0x0, "skip-reply", "Do not receive and check reply messages from Hyperion");
|
||||
@ -54,7 +77,8 @@ int main(int argc, char** argv)
|
||||
|
||||
// set defaults
|
||||
argDevice.setDefault("/dev/video0");
|
||||
argVideoStandard.setDefault(V4L2Grabber::NO_CHANGE);
|
||||
argVideoStandard.setDefault(VIDEOSTANDARD_NO_CHANGE);
|
||||
argPixelFormat.setDefault(PIXELFORMAT_NO_CHANGE);
|
||||
argInput.setDefault(-1);
|
||||
argWidth.setDefault(-1);
|
||||
argHeight.setDefault(-1);
|
||||
@ -76,30 +100,64 @@ int main(int argc, char** argv)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!argCropLeft.isSet()) argCropLeft.setDefault(argCropWidth.getValue());
|
||||
if (!argCropRight.isSet()) argCropRight.setDefault(argCropWidth.getValue());
|
||||
if (!argCropTop.isSet()) argCropTop.setDefault(argCropHeight.getValue());
|
||||
if (!argCropBottom.isSet()) argCropBottom.setDefault(argCropHeight.getValue());
|
||||
|
||||
// initialize the grabber
|
||||
V4L2Grabber grabber(
|
||||
argDevice.getValue(),
|
||||
argInput.getValue(),
|
||||
argVideoStandard.getValue(),
|
||||
argPixelFormat.getValue(),
|
||||
argWidth.getValue(),
|
||||
argHeight.getValue(),
|
||||
std::max(0, argCropWidth.getValue()),
|
||||
std::max(0, argCropHeight.getValue()),
|
||||
std::max(1, argFrameDecimation.getValue()),
|
||||
std::max(1, argSizeDecimation.getValue()),
|
||||
std::max(1, argSizeDecimation.getValue()));
|
||||
|
||||
grabber.start();
|
||||
// set signal detection
|
||||
grabber.setSignalThreshold(
|
||||
std::min(1.0, std::max(0.0, argRedSignalThreshold.isSet() ? argRedSignalThreshold.getValue() : argSignalThreshold.getValue())),
|
||||
std::min(1.0, std::max(0.0, argGreenSignalThreshold.isSet() ? argGreenSignalThreshold.getValue() : argSignalThreshold.getValue())),
|
||||
std::min(1.0, std::max(0.0, argBlueSignalThreshold.isSet() ? argBlueSignalThreshold.getValue() : argSignalThreshold.getValue())),
|
||||
50);
|
||||
|
||||
// set cropping values
|
||||
grabber.setCropping(
|
||||
std::max(0, argCropLeft.getValue()),
|
||||
std::max(0, argCropRight.getValue()),
|
||||
std::max(0, argCropTop.getValue()),
|
||||
std::max(0, argCropBottom.getValue()));
|
||||
|
||||
// set 3D mode if applicable
|
||||
if (arg3DSBS.isSet())
|
||||
{
|
||||
grabber.set3D(VIDEO_3DSBS);
|
||||
}
|
||||
else if (arg3DTAB.isSet())
|
||||
{
|
||||
grabber.set3D(VIDEO_3DTAB);
|
||||
}
|
||||
|
||||
// run the grabber
|
||||
if (argScreenshot.isSet())
|
||||
{
|
||||
grabber.setCallback(&saveScreenshot, nullptr);
|
||||
grabber.capture(1);
|
||||
ScreenshotHandler handler("screenshot.png");
|
||||
QObject::connect(&grabber, SIGNAL(newFrame(Image<ColorRgb>)), &handler, SLOT(receiveImage(Image<ColorRgb>)));
|
||||
grabber.start();
|
||||
QCoreApplication::exec();
|
||||
grabber.stop();
|
||||
}
|
||||
else
|
||||
{
|
||||
ImageHandler handler(argAddress.getValue(), argPriority.getValue(), argSignalThreshold.getValue(), argSkipReply.isSet());
|
||||
grabber.setCallback(&ImageHandler::imageCallback, &handler);
|
||||
grabber.capture();
|
||||
ImageHandler handler(argAddress.getValue(), argPriority.getValue(), argSkipReply.isSet());
|
||||
QObject::connect(&grabber, SIGNAL(newFrame(Image<ColorRgb>)), &handler, SLOT(receiveImage(Image<ColorRgb>)));
|
||||
grabber.start();
|
||||
QCoreApplication::exec();
|
||||
grabber.stop();
|
||||
}
|
||||
grabber.stop();
|
||||
}
|
||||
catch (const std::runtime_error & e)
|
||||
{
|
||||
|
@ -3,7 +3,7 @@
|
||||
#include "ProtoWrapper.h"
|
||||
|
||||
ProtoWrapper::ProtoWrapper(const std::string & protoAddress, const bool skipReply) :
|
||||
_priority(200),
|
||||
_priority(10),
|
||||
_duration_ms(2000),
|
||||
_connection(protoAddress)
|
||||
{
|
||||
|
@ -31,7 +31,7 @@ int main(int argc, char ** argv)
|
||||
|
||||
IntParameter & argCropWidth = parameters.add<IntParameter> (0x0, "crop-width", "Number of pixels to crop from the left and right sides in the picture before decimation [default=0]");
|
||||
IntParameter & argCropHeight = parameters.add<IntParameter> (0x0, "crop-height", "Number of pixels to crop from the top and the bottom in the picture before decimation [default=0]");
|
||||
IntParameter & argSizeDecimation = parameters.add<IntParameter> ('s', "size-decimator", "Decimation factor for the output size [default=1]");
|
||||
IntParameter & argSizeDecimation = parameters.add<IntParameter> ('s', "size-decimator", "Decimation factor for the output size [default=16]");
|
||||
SwitchParameter<> & argScreenshot = parameters.add<SwitchParameter<>> (0x0, "screenshot", "Take a single screenshot, save it to file and quit");
|
||||
StringParameter & argAddress = parameters.add<StringParameter> ('a', "address", "Set the address of the hyperion server [default: 127.0.0.1:19445]");
|
||||
IntParameter & argPriority = parameters.add<IntParameter> ('p', "priority", "Use the provided priority channel (the lower the number, the higher the priority) [default: 800]");
|
||||
@ -41,7 +41,7 @@ int main(int argc, char ** argv)
|
||||
// set defaults
|
||||
argCropWidth.setDefault(0);
|
||||
argCropHeight.setDefault(0);
|
||||
argSizeDecimation.setDefault(1);
|
||||
argSizeDecimation.setDefault(16);
|
||||
argAddress.setDefault("127.0.0.1:19445");
|
||||
argPriority.setDefault(800);
|
||||
|
||||
|
@ -8,8 +8,13 @@ target_link_libraries(hyperiond
|
||||
effectengine
|
||||
jsonserver
|
||||
protoserver
|
||||
boblightserver)
|
||||
boblightserver
|
||||
)
|
||||
|
||||
if (ENABLE_DISPMANX)
|
||||
target_link_libraries(hyperiond dispmanx-grabber)
|
||||
endif (ENABLE_DISPMANX)
|
||||
|
||||
if (ENABLE_V4L2)
|
||||
target_link_libraries(hyperiond v4l2-grabber)
|
||||
endif (ENABLE_V4L2)
|
||||
|
@ -1,10 +1,12 @@
|
||||
// C++ includes
|
||||
#include <cassert>
|
||||
#include <csignal>
|
||||
#include <clocale>
|
||||
|
||||
// QT includes
|
||||
#include <QCoreApplication>
|
||||
#include <QResource>
|
||||
#include <QLocale>
|
||||
|
||||
// config includes
|
||||
#include "HyperionConfig.h"
|
||||
@ -17,7 +19,12 @@
|
||||
|
||||
#ifdef ENABLE_DISPMANX
|
||||
// Dispmanx grabber includes
|
||||
#include <dispmanx-grabber/DispmanxWrapper.h>
|
||||
#include <grabber/DispmanxWrapper.h>
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_V4L2
|
||||
// v4l2 grabber
|
||||
#include <grabber/V4L2Wrapper.h>
|
||||
#endif
|
||||
|
||||
// XBMC Video checker includes
|
||||
@ -78,6 +85,10 @@ int main(int argc, char** argv)
|
||||
signal(SIGINT, signal_handler);
|
||||
signal(SIGTERM, signal_handler);
|
||||
|
||||
// force the locale
|
||||
setlocale(LC_ALL, "C");
|
||||
QLocale::setDefault(QLocale::c());
|
||||
|
||||
if (argc < 2)
|
||||
{
|
||||
std::cout << "Missing required configuration file. Usage:" << std::endl;
|
||||
@ -159,6 +170,43 @@ int main(int argc, char** argv)
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_V4L2
|
||||
// construct and start the v4l2 grabber if the configuration is present
|
||||
V4L2Wrapper * v4l2Grabber = nullptr;
|
||||
if (config.isMember("grabber-v4l2"))
|
||||
{
|
||||
const Json::Value & grabberConfig = config["grabber-v4l2"];
|
||||
v4l2Grabber = new V4L2Wrapper(
|
||||
grabberConfig.get("device", "/dev/video0").asString(),
|
||||
grabberConfig.get("input", 0).asInt(),
|
||||
parseVideoStandard(grabberConfig.get("standard", "no-change").asString()),
|
||||
parsePixelFormat(grabberConfig.get("pixelFormat", "no-change").asString()),
|
||||
grabberConfig.get("width", -1).asInt(),
|
||||
grabberConfig.get("height", -1).asInt(),
|
||||
grabberConfig.get("frameDecimation", 2).asInt(),
|
||||
grabberConfig.get("sizeDecimation", 8).asInt(),
|
||||
grabberConfig.get("redSignalThreshold", 0.0).asDouble(),
|
||||
grabberConfig.get("greenSignalThreshold", 0.0).asDouble(),
|
||||
grabberConfig.get("blueSignalThreshold", 0.0).asDouble(),
|
||||
&hyperion,
|
||||
grabberConfig.get("priority", 800).asInt());
|
||||
v4l2Grabber->set3D(parse3DMode(grabberConfig.get("mode", "2D").asString()));
|
||||
v4l2Grabber->setCropping(
|
||||
grabberConfig.get("cropLeft", 0).asInt(),
|
||||
grabberConfig.get("cropRight", 0).asInt(),
|
||||
grabberConfig.get("cropTop", 0).asInt(),
|
||||
grabberConfig.get("cropBottom", 0).asInt());
|
||||
|
||||
v4l2Grabber->start();
|
||||
std::cout << "V4l2 grabber created and started" << std::endl;
|
||||
}
|
||||
#else
|
||||
if (config.isMember("grabber-v4l2"))
|
||||
{
|
||||
std::cerr << "The v4l2 grabber can not be instantiated, becuse it has been left out from the build" << std::endl;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Create Json server if configuration is present
|
||||
JsonServer * jsonServer = nullptr;
|
||||
if (config.isMember("jsonServer"))
|
||||
@ -193,6 +241,9 @@ int main(int argc, char** argv)
|
||||
// Delete all component
|
||||
#ifdef ENABLE_DISPMANX
|
||||
delete dispmanx;
|
||||
#endif
|
||||
#ifdef ENABLE_V4L2
|
||||
delete v4l2Grabber;
|
||||
#endif
|
||||
delete xbmcVideoChecker;
|
||||
delete jsonServer;
|
||||
|
@ -60,20 +60,3 @@ if(ENABLE_X11)
|
||||
add_executable(test_x11performance TestX11Performance.cpp)
|
||||
target_link_libraries(test_x11performance ${X11_LIBRARIES} ${QT_LIBRARIES})
|
||||
endif(ENABLE_X11)
|
||||
|
||||
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;
|
||||
}
|
@ -6,7 +6,7 @@
|
||||
#include <QApplication>
|
||||
#include <QDesktopWidget>
|
||||
#include <QPixmap>
|
||||
#include <Qfile>
|
||||
#include <QFile>
|
||||
#include <QRgb>
|
||||
|
||||
#include <QElapsedTimer>
|
||||
|
@ -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>
|
||||
|
||||
// Dispmanx grabber includes
|
||||
#include <dispmanx-grabber/DispmanxFrameGrabber.h>
|
||||
#include <grabber/dispmanx/DispmanxFrameGrabber.h>
|
||||
|
||||
using namespace vlofgren;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user