diff --git a/CMakeLists.txt b/CMakeLists.txt index eb288f56..f8929a55 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,6 +32,9 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x -Wall") # Configure the use of QT4 find_package(Qt4 COMPONENTS QtCore QtGui QtNetwork REQUIRED QUIET) +# add protocol buffers +find_package(Protobuf REQUIRED) + #SET(QT_DONT_USE_QTGUI TRUE) #SET(QT_USE_QTCONSOLE TRUE) include(${QT_USE_FILE}) diff --git a/README.md b/README.md index 90cd0086..1dcbd8dd 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,14 @@ HYPERION ======== -An opensource 'AmbiLight' implementation controlled using the RaspBerry Pi running Raspbmc. -The intention is to replace BobLight. The replacement includes several 'improvements': -* Frame capture of screen included in deamon. This reduces the processor usages for image based led control to less than 2%. -* Priority channel can specificy a timeout on their 'command'. This allows a client or remote control to specify a fixed color and then close the connection. -* Json IP-control interface. Easy to use interface based on json format for control over TCP/IP. +Hyperion is an opensource 'AmbiLight' implementation controlled using the RaspBerry Pi running [Raspbmc](http://www.raspbmc.com). The main features of Hyperion are: +* Low CPU load. For a led string of 50 leds the CPU usage will typically be below 1.5% on a non-overclocked Pi. +* Json interface which allows easy integration into scripts. +* A command line utility allows easy testing and configuration of the color transforms (Transformation settings are not preserved over a restart at the moment...). +* Priority channels are not coupled to a specific led data provider which means that a provider can post led data and leave without the need to maintain a connection to Hyperion. This is ideal for a remote application (like our Android app). +* HyperCon. A tool which helps generate a Hyperion configuration file. +* Generic software architecture to support new devices and new algorithms easily. + +More information can be found on the [wiki](https://github.com/tvdzwan/hyperion/wiki). The source is released under MIT-License (see http://opensource.org/licenses/MIT). diff --git a/bin/copy_binaries_to_deploy.sh b/bin/copy_binaries_to_deploy.sh new file mode 100755 index 00000000..26595acd --- /dev/null +++ b/bin/copy_binaries_to_deploy.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +if [ "$#" -ne 2 ] || ! [ -d "$1" ] || ! [ -d "$2" ]; then + echo "Usage: $0 " >&2 + exit 1 +fi + +builddir="$1" +repodir="$2" +echo build directory = $builddir +echo repository root dirrectory = $repodir + +echo Copying binaries +cp -v "$builddir"/bin/hyperiond "$repodir"/deploy +cp -v "$builddir"/bin/hyperion-remote "$repodir"/deploy +cp -v "$builddir"/bin/gpio2spi "$repodir"/deploy diff --git a/bin/install_hyperion.sh b/bin/install_hyperion.sh index 7dbf228f..ad6b5080 100755 --- a/bin/install_hyperion.sh +++ b/bin/install_hyperion.sh @@ -13,15 +13,22 @@ fi # Stop hyperion daemon if it is running initctl stop hyperion -# Copy the hyperion-binaries to the /usr/bin -wget raw.github.com/tvdzwan/hyperion/master/deploy/hyperiond -P /usr/bin/ -wget raw.github.com/tvdzwan/hyperion/master/deploy/hyperion-remote -P /usr/bin/ +wget -N github.com/tvdzwan/hyperion/raw/master/deploy/hyperiond -P /usr/bin/ +wget -N github.com/tvdzwan/hyperion/raw/master/deploy/hyperion-remote -P /usr/bin/ + +# Copy the gpio changer (gpio->spi) to the /usr/bin +wget -N github.com/tvdzwan/hyperion/raw/master/deploy/gpio2spi -P /usr/bin/ # Copy the hyperion configuration file to /etc -wget raw.github.com/tvdzwan/hyperion/master/config/hyperion.config.json -P /etc/ +wget -N github.com/tvdzwan/hyperion/raw/master/config/hyperion.config.json -P /etc/ # Copy the service control configuration to /etc/int -wget raw.github.com/tvdzwan/hyperion/master/bin/hyperion.conf -P /etc/init/ +wget -N github.com/tvdzwan/hyperion/raw/master/deploy/hyperion.conf -P /etc/init/ + +# Set permissions +chmod +x /usr/bin/hyperiond +chmod +x /usr/bin/hyperion-remote +chmod +x /usr/bin/gpio2spi # Start the hyperion daemon initctl start hyperion diff --git a/config/hyperion.config.json b/config/hyperion.config.json index 2f22a63d..1241d3bd 100644 --- a/config/hyperion.config.json +++ b/config/hyperion.config.json @@ -1,333 +1,372 @@ -// Hyperion configuration +// Automatically generated configuration file for 'Hyperion daemon' +// Generated by: HyperCon (The Hyperion deamon configuration file builder + { - "device" : - { - "name" : "MyPi", - "type" : "ws2801", - "output" : "/dev/spidev0.0", - "interval" : 20000, - "rate" : 48000 - }, - "color" : - { - "hsv" : { - "saturationGain" : 1.0, - "valueGain" : 1.0 - }, - "red" : - { - "threshold" : 0.0, - "gamma" : 1.0, - "blacklevel" : 0.0, - "whitelevel" : 1.0 - }, - "green" : - { - "threshold" : 0.0, - "gamma" : 1.0, - "blacklevel" : 0.0, - "whitelevel" : 1.0 - }, - "blue" : - { - "threshold" : 0.0, - "gamma" : 1.0, - "blacklevel" : 0.0, - "whitelevel" : 1.0 - } - }, - "leds" : - [ - { - "index" : 0, - "hscan" : { "minimum" : 47.0588, "maximum" : 52.9412 }, - "vscan" : { "minimum" : 0, "maximum" : 10 } - }, - { - "index" : 1, - "hscan" : { "minimum" : 41.1765, "maximum" : 47.0588 }, - "vscan" : { "minimum" : 0, "maximum" : 10 } - }, - { - "index" : 2, - "hscan" : { "minimum" : 35.2941, "maximum" : 41.1765 }, - "vscan" : { "minimum" : 0, "maximum" : 10 } - }, - { - "index" : 3, - "hscan" : { "minimum" : 29.4118, "maximum" : 35.2941 }, - "vscan" : { "minimum" : 0, "maximum" : 10 } - }, - { - "index" : 4, - "hscan" : { "minimum" : 23.5294, "maximum" : 29.4118 }, - "vscan" : { "minimum" : 0, "maximum" : 10 } - }, - { - "index" : 5, - "hscan" : { "minimum" : 17.6471, "maximum" : 23.5294 }, - "vscan" : { "minimum" : 0, "maximum" : 10 } - }, - { - "index" : 6, - "hscan" : { "minimum" : 11.7647, "maximum" : 17.6471 }, - "vscan" : { "minimum" : 0, "maximum" : 10 } - }, - { - "index" : 7, - "hscan" : { "minimum" : 5.88235, "maximum" : 11.7647 }, - "vscan" : { "minimum" : 0, "maximum" : 10 } - }, - // TOP-LEFT Corner - { - "index" : 8, - "hscan" : { "minimum" : 0, "maximum" : 5.88235 }, - "vscan" : { "minimum" : 0, "maximum" : 10 } - }, - { - "index" : 9, - "hscan" : { "minimum" : 0, "maximum" : 10 }, - "vscan" : { "minimum" : 10, "maximum" : 20 } - }, - { - "index" : 10, - "hscan" : { "minimum" : 0, "maximum" : 10 }, - "vscan" : { "minimum" : 20, "maximum" : 30 } - }, - { - "index" : 11, - "hscan" : { "minimum" : 0, "maximum" : 10 }, - "vscan" : { "minimum" : 30, "maximum" : 40 } - }, - { - "index" : 12, - "hscan" : { "minimum" : 0, "maximum" : 10 }, - "vscan" : { "minimum" : 40, "maximum" : 50 } - }, - { - "index" : 13, - "hscan" : { "minimum" : 0, "maximum" : 10 }, - "vscan" : { "minimum" : 50, "maximum" : 60 } - }, - { - "index" : 14, - "hscan" : { "minimum" : 0, "maximum" : 10 }, - "vscan" : { "minimum" : 60, "maximum" : 70 } - }, - { - "index" : 15, - "hscan" : { "minimum" : 0, "maximum" : 10 }, - "vscan" : { "minimum" : 70, "maximum" : 80 } - }, - { - "index" : 16, - "hscan" : { "minimum" : 0, "maximum" : 10 }, - "vscan" : { "minimum" : 80, "maximum" : 90 } - }, - // BOTTOM-LEFT Corner - { - "index" : 17, - "hscan" : { "minimum" : 0, "maximum" : 5.88235 }, - "vscan" : { "minimum" : 90, "maximum" : 100 } - }, - { - "index" : 18, - "hscan" : { "minimum" : 5.88235, "maximum" : 11.7647 }, - "vscan" : { "minimum" : 90, "maximum" : 100 } - }, - { - "index" : 19, - "hscan" : { "minimum" : 11.7647, "maximum" : 17.6471 }, - "vscan" : { "minimum" : 90, "maximum" : 100 } - }, - { - "index" : 20, - "hscan" : { "minimum" : 17.6471, "maximum" : 23.5294 }, - "vscan" : { "minimum" : 90, "maximum" : 100 } - }, - { - "index" : 21, - "hscan" : { "minimum" : 23.5294, "maximum" : 29.4118 }, - "vscan" : { "minimum" : 90, "maximum" : 100 } - }, - { - "index" : 22, - "hscan" : { "minimum" : 29.4118, "maximum" : 35.2941 }, - "vscan" : { "minimum" : 90, "maximum" : 100 } - }, - { - "index" : 23, - "hscan" : { "minimum" : 35.2941, "maximum" : 41.1765 }, - "vscan" : { "minimum" : 90, "maximum" : 100 } - }, - { - "index" : 24, - "hscan" : { "minimum" : 41.1765, "maximum" : 47.0588 }, - "vscan" : { "minimum" : 90, "maximum" : 100 } - }, - { - "index" : 25, - "hscan" : { "minimum" : 47.0588, "maximum" : 52.9412 }, - "vscan" : { "minimum" : 90, "maximum" : 100 } - }, - { - "index" : 26, - "hscan" : { "minimum" : 52.9412, "maximum" : 58.8235 }, - "vscan" : { "minimum" : 90, "maximum" : 100 } - }, - { - "index" : 27, - "hscan" : { "minimum" : 58.8235, "maximum" : 64.7059 }, - "vscan" : { "minimum" : 90, "maximum" : 100 } - }, - { - "index" : 28, - "hscan" : { "minimum" : 64.7059, "maximum" : 70.5882 }, - "vscan" : { "minimum" : 90, "maximum" : 100 } - }, - { - "index" : 29, - "hscan" : { "minimum" : 70.5882, "maximum" : 76.4706 }, - "vscan" : { "minimum" : 90, "maximum" : 100 } - }, - { - "index" : 30, - "hscan" : { "minimum" : 76.4706, "maximum" : 82.3529 }, - "vscan" : { "minimum" : 90, "maximum" : 100 } - }, - { - "index" : 31, - "hscan" : { "minimum" : 82.3529, "maximum" : 88.2353 }, - "vscan" : { "minimum" : 90, "maximum" : 100 } - }, - { - "index" : 32, - "hscan" : { "minimum" : 88.2353, "maximum" : 94.1176 }, - "vscan" : { "minimum" : 90, "maximum" : 100 } - }, - // BOTTOM-RIGHT Corner - { - "index" : 33, - "hscan" : { "minimum" : 94.1176, "maximum" : 100 }, - "vscan" : { "minimum" : 90, "maximum" : 100 } - }, - { - "index" : 34, - "hscan" : { "minimum" : 90, "maximum" : 100 }, - "vscan" : { "minimum" : 80, "maximum" : 90 } - }, - { - "index" : 35, - "hscan" : { "minimum" : 90, "maximum" : 100 }, - "vscan" : { "minimum" : 70, "maximum" : 80 } - }, - { - "index" : 36, - "hscan" : { "minimum" : 90, "maximum" : 100 }, - "vscan" : { "minimum" : 60, "maximum" : 70 } - }, - { - "index" : 37, - "hscan" : { "minimum" : 90, "maximum" : 100 }, - "vscan" : { "minimum" : 50, "maximum" : 60 } - }, - { - "index" : 38, - "hscan" : { "minimum" : 90, "maximum" : 100 }, - "vscan" : { "minimum" : 40, "maximum" : 50 } - }, - { - "index" : 39, - "hscan" : { "minimum" : 90, "maximum" : 100 }, - "vscan" : { "minimum" : 30, "maximum" : 40 } - }, - { - "index" : 40, - "hscan" : { "minimum" : 90, "maximum" : 100 }, - "vscan" : { "minimum" : 20, "maximum" : 30 } - }, - { - "index" : 41, - "hscan" : { "minimum" : 90, "maximum" : 100 }, - "vscan" : { "minimum" : 10, "maximum" : 20 } - }, - // TOP-RIGHT Corner - { - "index" : 42, - "hscan" : { "minimum" : 94.1176, "maximum" : 100 }, - "vscan" : { "minimum" : 0, "maximum" : 10 } - }, - { - "index" : 43, - "hscan" : { "minimum" : 88.2353, "maximum" : 94.1176 }, - "vscan" : { "minimum" : 0, "maximum" : 10 } - }, - { - "index" : 44, - "hscan" : { "minimum" : 82.3529, "maximum" : 88.2353 }, - "vscan" : { "minimum" : 0, "maximum" : 10 } - }, - { - "index" : 45, - "hscan" : { "minimum" : 76.4706, "maximum" : 82.3529 }, - "vscan" : { "minimum" : 0, "maximum" : 10 } - }, - { - "index" : 46, - "hscan" : { "minimum" : 70.5882, "maximum" : 76.4706 }, - "vscan" : { "minimum" : 0, "maximum" : 10 } - }, - { - "index" : 47, - "hscan" : { "minimum" : 64.7059, "maximum" : 70.5882 }, - "vscan" : { "minimum" : 0, "maximum" : 10 } - }, - { - "index" : 48, - "hscan" : { "minimum" : 58.8235, "maximum" : 64.7059 }, - "vscan" : { "minimum" : 0, "maximum" : 10 } - }, - { - "index" : 49, - "hscan" : { "minimum" : 52.9412, "maximum" : 58.8235 }, - "vscan" : { "minimum" : 0, "maximum" : 10 } - } - ], + /// Device configuration contains the following fields: + /// * 'name' : The user friendly name of the device (only used for display purposes) + /// * 'type' : The type of the device or leds (known types for now are 'ws2801', 'test' and 'none') + /// * 'output' : The output specification depends on selected device + /// - 'ws2801' this is the device (eg '/dev/spidev0.0') + /// - 'test' this is the file used to write test output (eg '/home/pi/hyperion.out') + /// * 'rate' : The baudrate of the output to the device (only applicable for 'ws2801') + "device" : + { + "name" : "MyPi", + "type" : "ws2801", + "output" : "/dev/spidev0.0", + "rate" : 1000000 + }, - // The XBMC video checker will connect to XBMC to check its player state and adjust the grabbing on it - "xbmcVideoChecker" : { - // Enable the use of the XBMC checker - "enable" : true, + /// Color manipulation configuration used to tune the output colors to specific surroundings. Contains the following fields: + /// * 'hsv' : The manipulation in the Hue-Saturation-Value color domain with the following tuning parameters: + /// - 'saturationGain' The gain adjustement of the saturation + /// - 'valueGain' The gain adjustement of the value + /// * 'red'/'green'/'blue' : The manipulation in the Red-Green-Blue color domain with the following tuning parameters for each channel: + /// - 'threshold' The minimum required input value for the channel to be on (else zero) + /// - 'gamma' The gamma-curve correction factor + /// - 'blacklevel' The lowest possible value (when the channel is black) + /// - 'whitelevel' The highest possible value (when the channel is white) + "color" : + { + "hsv" : + { + "saturationGain" : 1.0000, + "valuGain" : 1.5000 + }, + "red" : + { + "threshold" : 0.1000, + "gamma" : 2.0000, + "blacklevel" : 0.0000, + "whitelevel" : 0.8000 + }, + "green" : + { + "threshold" : 0.1000, + "gamma" : 2.0000, + "blacklevel" : 0.0000, + "whitelevel" : 1.0000 + }, + "blue" : + { + "threshold" : 0.1000, + "gamma" : 2.0000, + "blacklevel" : 0.0000, + "whitelevel" : 1.0000 + } + }, - // Address of the hoxt running XBMC - "xbmcAddress" : "127.0.0.1", + /// The configuration for each individual led. This contains the specification of the area + /// averaged of an input image for each led to determine its color. Each item in the list + /// contains the following fields: + /// * index: The index of the led. This determines its location in the string of leds; zero + /// being the first led. + /// * hscan: The fractional part of the image along the horizontal used for the averaging + /// (minimum and maximum inclusive) + /// * vscan: The fractional part of the image along the vertical used for the averaging + /// (minimum and maximum inclusive) + "leds" : + [ + { + "index" : 0, + "hscan" : { "minimum" : 0.4375, "maximum" : 0.5000 }, + "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } + }, + { + "index" : 1, + "hscan" : { "minimum" : 0.3750, "maximum" : 0.4375 }, + "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } + }, + { + "index" : 2, + "hscan" : { "minimum" : 0.3125, "maximum" : 0.3750 }, + "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } + }, + { + "index" : 3, + "hscan" : { "minimum" : 0.2500, "maximum" : 0.3125 }, + "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } + }, + { + "index" : 4, + "hscan" : { "minimum" : 0.1875, "maximum" : 0.2500 }, + "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } + }, + { + "index" : 5, + "hscan" : { "minimum" : 0.1250, "maximum" : 0.1875 }, + "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } + }, + { + "index" : 6, + "hscan" : { "minimum" : 0.0625, "maximum" : 0.1250 }, + "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } + }, + { + "index" : 7, + "hscan" : { "minimum" : 0.0000, "maximum" : 0.0625 }, + "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } + }, + { + "index" : 8, + "hscan" : { "minimum" : 0.0000, "maximum" : 0.0500 }, + "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } + }, + { + "index" : 9, + "hscan" : { "minimum" : 0.0000, "maximum" : 0.0500 }, + "vscan" : { "minimum" : 0.8571, "maximum" : 1.0000 } + }, + { + "index" : 10, + "hscan" : { "minimum" : 0.0000, "maximum" : 0.0500 }, + "vscan" : { "minimum" : 0.7143, "maximum" : 0.8571 } + }, + { + "index" : 11, + "hscan" : { "minimum" : 0.0000, "maximum" : 0.0500 }, + "vscan" : { "minimum" : 0.5714, "maximum" : 0.7143 } + }, + { + "index" : 12, + "hscan" : { "minimum" : 0.0000, "maximum" : 0.0500 }, + "vscan" : { "minimum" : 0.4286, "maximum" : 0.5714 } + }, + { + "index" : 13, + "hscan" : { "minimum" : 0.0000, "maximum" : 0.0500 }, + "vscan" : { "minimum" : 0.2857, "maximum" : 0.4286 } + }, + { + "index" : 14, + "hscan" : { "minimum" : 0.0000, "maximum" : 0.0500 }, + "vscan" : { "minimum" : 0.1429, "maximum" : 0.2857 } + }, + { + "index" : 15, + "hscan" : { "minimum" : 0.0000, "maximum" : 0.0500 }, + "vscan" : { "minimum" : 0.0000, "maximum" : 0.1429 } + }, + { + "index" : 16, + "hscan" : { "minimum" : 0.0000, "maximum" : 0.0500 }, + "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } + }, + { + "index" : 17, + "hscan" : { "minimum" : 0.0000, "maximum" : 0.0625 }, + "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } + }, + { + "index" : 18, + "hscan" : { "minimum" : 0.0625, "maximum" : 0.1250 }, + "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } + }, + { + "index" : 19, + "hscan" : { "minimum" : 0.1250, "maximum" : 0.1875 }, + "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } + }, + { + "index" : 20, + "hscan" : { "minimum" : 0.1875, "maximum" : 0.2500 }, + "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } + }, + { + "index" : 21, + "hscan" : { "minimum" : 0.2500, "maximum" : 0.3125 }, + "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } + }, + { + "index" : 22, + "hscan" : { "minimum" : 0.3125, "maximum" : 0.3750 }, + "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } + }, + { + "index" : 23, + "hscan" : { "minimum" : 0.3750, "maximum" : 0.4375 }, + "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } + }, + { + "index" : 24, + "hscan" : { "minimum" : 0.4375, "maximum" : 0.5000 }, + "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } + }, + { + "index" : 25, + "hscan" : { "minimum" : 0.5000, "maximum" : 0.5625 }, + "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } + }, + { + "index" : 26, + "hscan" : { "minimum" : 0.5625, "maximum" : 0.6250 }, + "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } + }, + { + "index" : 27, + "hscan" : { "minimum" : 0.6250, "maximum" : 0.6875 }, + "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } + }, + { + "index" : 28, + "hscan" : { "minimum" : 0.6875, "maximum" : 0.7500 }, + "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } + }, + { + "index" : 29, + "hscan" : { "minimum" : 0.7500, "maximum" : 0.8125 }, + "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } + }, + { + "index" : 30, + "hscan" : { "minimum" : 0.8125, "maximum" : 0.8750 }, + "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } + }, + { + "index" : 31, + "hscan" : { "minimum" : 0.8750, "maximum" : 0.9375 }, + "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } + }, + { + "index" : 32, + "hscan" : { "minimum" : 0.9375, "maximum" : 1.0000 }, + "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } + }, + { + "index" : 33, + "hscan" : { "minimum" : 0.9500, "maximum" : 1.0000 }, + "vscan" : { "minimum" : 0.0000, "maximum" : 0.0800 } + }, + { + "index" : 34, + "hscan" : { "minimum" : 0.9500, "maximum" : 1.0000 }, + "vscan" : { "minimum" : 0.0000, "maximum" : 0.1429 } + }, + { + "index" : 35, + "hscan" : { "minimum" : 0.9500, "maximum" : 1.0000 }, + "vscan" : { "minimum" : 0.1429, "maximum" : 0.2857 } + }, + { + "index" : 36, + "hscan" : { "minimum" : 0.9500, "maximum" : 1.0000 }, + "vscan" : { "minimum" : 0.2857, "maximum" : 0.4286 } + }, + { + "index" : 37, + "hscan" : { "minimum" : 0.9500, "maximum" : 1.0000 }, + "vscan" : { "minimum" : 0.4286, "maximum" : 0.5714 } + }, + { + "index" : 38, + "hscan" : { "minimum" : 0.9500, "maximum" : 1.0000 }, + "vscan" : { "minimum" : 0.5714, "maximum" : 0.7143 } + }, + { + "index" : 39, + "hscan" : { "minimum" : 0.9500, "maximum" : 1.0000 }, + "vscan" : { "minimum" : 0.7143, "maximum" : 0.8571 } + }, + { + "index" : 40, + "hscan" : { "minimum" : 0.9500, "maximum" : 1.0000 }, + "vscan" : { "minimum" : 0.8571, "maximum" : 1.0000 } + }, + { + "index" : 41, + "hscan" : { "minimum" : 0.9500, "maximum" : 1.0000 }, + "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } + }, + { + "index" : 42, + "hscan" : { "minimum" : 0.9375, "maximum" : 1.0000 }, + "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } + }, + { + "index" : 43, + "hscan" : { "minimum" : 0.8750, "maximum" : 0.9375 }, + "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } + }, + { + "index" : 44, + "hscan" : { "minimum" : 0.8125, "maximum" : 0.8750 }, + "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } + }, + { + "index" : 45, + "hscan" : { "minimum" : 0.7500, "maximum" : 0.8125 }, + "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } + }, + { + "index" : 46, + "hscan" : { "minimum" : 0.6875, "maximum" : 0.7500 }, + "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } + }, + { + "index" : 47, + "hscan" : { "minimum" : 0.6250, "maximum" : 0.6875 }, + "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } + }, + { + "index" : 48, + "hscan" : { "minimum" : 0.5625, "maximum" : 0.6250 }, + "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } + }, + { + "index" : 49, + "hscan" : { "minimum" : 0.5000, "maximum" : 0.5625 }, + "vscan" : { "minimum" : 0.9200, "maximum" : 1.0000 } + } + ], - // Port used by XBMC for the TCP json service (Default disabled by XBMC for non-local clients) - "xbmcTcpPort" : 9090, + /// The boot-sequence configuration, contains the following items: + /// * type : The type of the boot-sequence ('rainbow', 'knight_rider', 'none') + /// * duration_ms : The length of the boot-sequence [ms] + "bootsequence" : + { + "type" : "Rainbow", + "duration_ms" : 3000 + }, - // Grab screen when XBMC is playing video - "grabVideo" : true, + /// The configuration for the frame-grabber, contains the following items: + /// * width : The width of the grabbed frames [pixels] + /// * height : The height of the grabbed frames [pixels] + /// * frequency_Hz : The frequency of the frame grab [Hz] + "framegrabber" : + { + "width" : 64, + "height" : 64, + "frequency_Hz" : 10.0 + }, - // Grab screen when XBMC is playing pictures - "grabPictures" : true, + /// The configuration of the XBMC connection used to enable and disable the frame-grabber. Contains the following fields: + /// * xbmcAddress : The IP address of the XBMC-host + /// * xbmcTcpPort : The TCP-port of the XBMC-server + /// * grabVideo : Flag indicating that the frame-grabber is on(true) during video playback + /// * grabPictures : Flag indicating that the frame-grabber is on(true) during picture show + /// * grabAudio : Flag indicating that the frame-grabber is on(true) during audio playback + /// * grabMenu : Flag indicating that the frame-grabber is on(true) in the XBMC menu + "xbmcVideoChecker" : + { + "xbmcAddress" : "127.0.0.1", + "xbmcTcpPort" : 9090, + "grabVideo" : true, + "grabPictures" : true, + "grabAudio" : true, + "grabMenu" : false + }, - // Grab screen when XBMC is playing audio - "grabAudio" : true, + /// The configuration of the Json server which enables the json remote interface + /// * port : Port at which the json server is started + "jsonServer" : + { + "port" : 19444 + }, - // Grab screen when XBMC is not playing anything (in menu) - "grabMenu" : true - }, - - "bootsequence" : - { - "type" : "rainbow", - "duration_ms" : 3000 - }, - - "framegrabber" : - { - "width" : 64, - "height" : 64, - "frequency_Hz" : 10 - } + /// The configuration of the Proto server which enables the protobuffer remote interface + /// * port : Port at which the protobuffer server is started + "protoServer" : + { + "port" : 19445 + } } diff --git a/deploy/HyperCon.jar.REMOVED.git-id b/deploy/HyperCon.jar.REMOVED.git-id index b045c0bd..2168765a 100644 --- a/deploy/HyperCon.jar.REMOVED.git-id +++ b/deploy/HyperCon.jar.REMOVED.git-id @@ -1 +1 @@ -76f29d2a8a10e60629d08889f183d318dfee9f3f \ No newline at end of file +58c9b56f08a3ea76161730b0f8b8f102e8d70143 \ No newline at end of file diff --git a/deploy/gpio2spi b/deploy/gpio2spi new file mode 100755 index 00000000..f999003f Binary files /dev/null and b/deploy/gpio2spi differ diff --git a/deploy/hyperion-remote b/deploy/hyperion-remote index 51d6553d..616f4ffd 100755 Binary files a/deploy/hyperion-remote and b/deploy/hyperion-remote differ diff --git a/deploy/hyperion.conf b/deploy/hyperion.conf new file mode 100644 index 00000000..c25f91f2 --- /dev/null +++ b/deploy/hyperion.conf @@ -0,0 +1,17 @@ +## Hyperion daemon + +description "hyperion" +author "poljvd & tvdzwan" + +start on (runlevel [2345]) +stop on (runlevel [!2345]) + +respawn + +pre-start script +modprobe spidev +/usr/bin/gpio2spi +end script + +exec /usr/bin/hyperiond /etc/hyperion.config.json + diff --git a/deploy/hyperiond b/deploy/hyperiond deleted file mode 100755 index ad8fe284..00000000 Binary files a/deploy/hyperiond and /dev/null differ diff --git a/deploy/hyperiond.REMOVED.git-id b/deploy/hyperiond.REMOVED.git-id new file mode 100644 index 00000000..9a0be94a --- /dev/null +++ b/deploy/hyperiond.REMOVED.git-id @@ -0,0 +1 @@ +cf974e6aa1ff2a7194ce2b82fd5da5fa2373676a \ No newline at end of file diff --git a/include/protoserver/ProtoServer.h b/include/protoserver/ProtoServer.h new file mode 100644 index 00000000..dae57481 --- /dev/null +++ b/include/protoserver/ProtoServer.h @@ -0,0 +1,59 @@ +#pragma once + +// system includes +#include + +// Qt includes +#include +#include + +// Hyperion includes +#include + +class ProtoClientConnection; + +/// +/// This class creates a TCP server which accepts connections wich can then send +/// in Protocol Buffer encoded commands. This interface to Hyperion is used by +/// hyperion-remote to control the leds +/// +class ProtoServer : public QObject +{ + Q_OBJECT + +public: + /// + /// ProtoServer constructor + /// @param hyperion Hyperion instance + /// @param port port number on which to start listening for connections + /// + ProtoServer(Hyperion * hyperion, uint16_t port = 19445); + ~ProtoServer(); + + /// + /// @return the port number on which this TCP listens for incoming connections + /// + uint16_t getPort() const; + +private slots: + /// + /// Slot which is called when a client tries to create a new connection + /// + void newConnection(); + + /// + /// Slot which is called when a client closes a connection + /// @param connection The Connection object which is being closed + /// + void closedConnection(ProtoClientConnection * connection); + +private: + /// Hyperion instance + Hyperion * _hyperion; + + /// The TCP server object + QTcpServer _server; + + /// List with open connections + QSet _openConnections; +}; diff --git a/libsrc/CMakeLists.txt b/libsrc/CMakeLists.txt index af63b296..a95f328f 100644 --- a/libsrc/CMakeLists.txt +++ b/libsrc/CMakeLists.txt @@ -7,5 +7,6 @@ add_subdirectory(bootsequence) add_subdirectory(dispmanx-grabber) add_subdirectory(hyperion) add_subdirectory(jsonserver) +add_subdirectory(protoserver) add_subdirectory(utils) add_subdirectory(xbmcvideochecker) diff --git a/libsrc/bootsequence/BootSequenceFactory.cpp b/libsrc/bootsequence/BootSequenceFactory.cpp index 50c2cdf2..dccbfdae 100644 --- a/libsrc/bootsequence/BootSequenceFactory.cpp +++ b/libsrc/bootsequence/BootSequenceFactory.cpp @@ -1,3 +1,6 @@ +// stl includes +#include +#include // Bootsequence includes #include @@ -8,7 +11,8 @@ BootSequence * BootSequenceFactory::createBootSequence(Hyperion * hyperion, const Json::Value & jsonConfig) { - const std::string type = jsonConfig["type"].asString(); + std::string type = jsonConfig["type"].asString(); + std::transform(type.begin(), type.end(), type.begin(), ::tolower); if (type == "none") { @@ -19,7 +23,7 @@ BootSequence * BootSequenceFactory::createBootSequence(Hyperion * hyperion, cons const unsigned duration_ms = jsonConfig["duration_ms"].asUInt(); return new RainbowBootSequence(hyperion, duration_ms); } - else if (type == "knightrider") + else if (type == "knightrider" || type == "knight rider") { const unsigned duration_ms = jsonConfig["duration_ms"].asUInt(); return new KittBootSequence(hyperion, duration_ms); diff --git a/libsrc/dispmanx-grabber/DispmanxFrameGrabber.cpp b/libsrc/dispmanx-grabber/DispmanxFrameGrabber.cpp index 204346fe..3bdc4bdf 100644 --- a/libsrc/dispmanx-grabber/DispmanxFrameGrabber.cpp +++ b/libsrc/dispmanx-grabber/DispmanxFrameGrabber.cpp @@ -1,18 +1,6 @@ #include "DispmanxFrameGrabber.h" -// Because the shapshot function is incompatible between versions (use of different enum as -// third argument) and no proper version number is available as preprocessor define we cast the -// function to the same function with the third argument as 'int'. -// This way we can call the function in both versions of the VideoCore library without -// switching. -static int my_vc_dispmanx_snapshot(DISPMANX_DISPLAY_HANDLE_T display, DISPMANX_RESOURCE_HANDLE_T snapshot_resource, int transform) -{ - typedef int (*SnapshotFunctionPtr)(DISPMANX_DISPLAY_HANDLE_T, DISPMANX_RESOURCE_HANDLE_T, int); - SnapshotFunctionPtr snapshot = (SnapshotFunctionPtr) &vc_dispmanx_snapshot; - return (*snapshot)(display, snapshot_resource, transform); -} - DispmanxFrameGrabber::DispmanxFrameGrabber(const unsigned width, const unsigned height) : _vc_display(0), _vc_resource(0), @@ -77,7 +65,7 @@ void DispmanxFrameGrabber::grabFrame(RgbImage& image) _vc_display = vc_dispmanx_display_open(0); // Create the snapshot (incl down-scaling) - my_vc_dispmanx_snapshot(_vc_display, _vc_resource, _vc_flags); + vc_dispmanx_snapshot(_vc_display, _vc_resource, (DISPMANX_TRANSFORM_T) _vc_flags); // Read the snapshot into the memory void* image_ptr = image.memptr(); diff --git a/libsrc/hyperion/Hyperion.cpp b/libsrc/hyperion/Hyperion.cpp index 631ff2a5..c1f29985 100644 --- a/libsrc/hyperion/Hyperion.cpp +++ b/libsrc/hyperion/Hyperion.cpp @@ -66,10 +66,10 @@ LedString Hyperion::createLedString(const Json::Value& ledsConfig) led.index = ledConfig["index"].asInt(); const Json::Value& hscanConfig = ledConfig["hscan"]; const Json::Value& vscanConfig = ledConfig["vscan"]; - led.minX_frac = std::max(0.0, std::min(100.0, hscanConfig["minimum"].asDouble()))/100.0; - led.maxX_frac = std::max(0.0, std::min(100.0, hscanConfig["maximum"].asDouble()))/100.0; - led.minY_frac = 1.0 - std::max(0.0, std::min(100.0, vscanConfig["maximum"].asDouble()))/100.0; - led.maxY_frac = 1.0 - std::max(0.0, std::min(100.0, vscanConfig["minimum"].asDouble()))/100.0; + led.minX_frac = std::max(0.0, std::min(1.0, hscanConfig["minimum"].asDouble())); + led.maxX_frac = std::max(0.0, std::min(1.0, hscanConfig["maximum"].asDouble())); + led.minY_frac = std::max(0.0, std::min(1.0, vscanConfig["maximum"].asDouble())); + led.maxY_frac = std::max(0.0, std::min(1.0, vscanConfig["minimum"].asDouble())); ledString.leds().push_back(led); } diff --git a/libsrc/hyperion/hyperion.schema.json b/libsrc/hyperion/hyperion.schema.json index f3923a90..7924961e 100644 --- a/libsrc/hyperion/hyperion.schema.json +++ b/libsrc/hyperion/hyperion.schema.json @@ -1,50 +1,47 @@ { - "type":"object", - "required":true, - "properties":{ - "device": { - "type":"object", - "required":true, - "properties":{ - "name": { - "type":"string", - "required":true + "type" : "object", + "required" : true, + "properties" : { + "device" : { + "type" : "object", + "required" : true, + "properties" : { + "name" : { + "type" : "string", + "required" : true }, - "type": { - "type":"string", - "required":true + "type" : { + "type" : "string", + "required" : true }, - "output": { - "type":"string", - "required":true + "output" : { + "type" : "string", + "required" : true }, - "interval": { - "type":"integer", - "required":true - }, - "rate": { - "type":"integer", - "required":true + "rate" : { + "type" : "integer", + "required" : true, + "minimum" : 0 } }, - "additionalProperties": false + "additionalProperties" : false }, "color": { "type":"object", - "required":true, + "required":false, "properties": { "hsv" : { "type" : "object", - "required" : true, + "required" : false, "properties" : { "saturationGain" : { "type" : "number", - "required" : true, + "required" : false, "minimum" : 0.0 }, "valueGain" : { "type" : "number", - "required" : true, + "required" : false, "minimum" : 0.0 } }, @@ -52,77 +49,81 @@ }, "red": { "type":"object", - "required":true, + "required":false, "properties":{ "gamma": { "type":"number", - "required":true + "required":false }, "blacklevel": { "type":"number", - "required":true + "required":false }, "whitelevel": { "type":"number", - "required":true + "required":false }, "threshold": { "type":"number", - "required":true, + "required":false, "minimum" : 0.0, "maximum" : 1.0 } - } + }, + "additionalProperties" : false }, "green": { "type":"object", - "required":true, + "required":false, "properties":{ "gamma": { "type":"number", - "required":true + "required":false }, "blacklevel": { "type":"number", - "required":true + "required":false }, "whitelevel": { "type":"number", - "required":true + "required":false }, "threshold": { "type":"number", - "required":true, + "required":false, "minimum" : 0.0, "maximum" : 1.0 } - } + }, + "additionalProperties" : false }, "blue": { "type":"object", - "required":true, + "required":false, "properties":{ "gamma": { "type":"number", - "required":true + "required":false }, "whitelevel": { "type":"number", - "required":true + "required":false }, "blacklevel": { "type":"number", - "required":true + "required":false }, "threshold": { "type":"number", - "required":true, + "required":false, "minimum" : 0.0, "maximum" : 1.0 } - } + }, + "additionalProperties" : false } - } + }, + "additionalProperties" : false }, "leds": { "type":"array", @@ -146,7 +147,8 @@ "type":"number", "required":true } - } + }, + "additionalProperties" : false }, "vscan": { "type":"object", @@ -160,20 +162,18 @@ "type":"number", "required":true } - } + }, + "additionalProperties" : false } - } + }, + "additionalProperties" : false } }, "xbmcVideoChecker" : { "type" : "object", - "required" : true, + "required" : false, "properties" : { - "enable" : { - "type" : "boolean", - "required" : true - }, "xbmcAddress" : { "type" : "string", "required" : true @@ -204,7 +204,7 @@ "bootsequence" : { "type" : "object", - "required" : true, + "required" : false, "properties" : { "type" : { "type" : "string", @@ -220,7 +220,7 @@ "framegrabber" : { "type" : "object", - "required" : true, + "required" : false, "properties" : { "width" : { "type" : "integer", @@ -236,6 +236,34 @@ } }, "additionalProperties" : false + }, + "jsonServer" : + { + "type" : "object", + "required" : false, + "properties" : { + "port" : { + "type" : "integer", + "required" : true, + "minimum" : 0, + "maximum" : 65535 + } + }, + "additionalProperties" : false + }, + "protoServer" : + { + "type" : "object", + "required" : false, + "properties" : { + "port" : { + "type" : "integer", + "required" : true, + "minimum" : 0, + "maximum" : 65535 + } + }, + "additionalProperties" : false } }, "additionalProperties" : false diff --git a/libsrc/protoserver/CMakeLists.txt b/libsrc/protoserver/CMakeLists.txt new file mode 100644 index 00000000..a66ef336 --- /dev/null +++ b/libsrc/protoserver/CMakeLists.txt @@ -0,0 +1,52 @@ + +# Define the current source locations +set(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/protoserver) +set(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/protoserver) + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${PROTOBUF_INCLUDE_DIRS}) + +# Group the headers that go through the MOC compiler +set(ProtoServer_QT_HEADERS + ${CURRENT_HEADER_DIR}/ProtoServer.h + ${CURRENT_SOURCE_DIR}/ProtoClientConnection.h +) + +set(ProtoServer_HEADERS +) + +set(ProtoServer_SOURCES + ${CURRENT_SOURCE_DIR}/ProtoServer.cpp + ${CURRENT_SOURCE_DIR}/ProtoClientConnection.cpp +) + +set(ProtoServer_PROTOS + ${CURRENT_SOURCE_DIR}/message.proto +) + +protobuf_generate_cpp(ProtoServer_PROTO_SRCS ProtoServer_PROTO_HDRS + ${ProtoServer_PROTOS} +) + +qt4_wrap_cpp(ProtoServer_HEADERS_MOC ${ProtoServer_QT_HEADERS}) + +add_library(protoserver + ${ProtoServer_HEADERS} + ${ProtoServer_QT_HEADERS} + ${ProtoServer_SOURCES} + ${ProtoServer_HEADERS_MOC} + ${ProtoServer_PROTOS} + ${ProtoServer_PROTO_SRCS} + ${ProtoServer_PROTO_HDRS} +) + +target_link_libraries(protoserver + hyperion + hyperion-utils + ${PROTOBUF_LIBRARIES}) + +qt4_use_modules(protoserver + Core + Gui + Network) diff --git a/libsrc/protoserver/ProtoClientConnection.cpp b/libsrc/protoserver/ProtoClientConnection.cpp new file mode 100644 index 00000000..f7deb85d --- /dev/null +++ b/libsrc/protoserver/ProtoClientConnection.cpp @@ -0,0 +1,223 @@ +// system includes +#include +#include + +// stl includes +#include +#include +#include + +// Qt includes +#include +#include +#include + +// hyperion util includes +#include "hyperion/ImageProcessorFactory.h" +#include "hyperion/ImageProcessor.h" +#include "utils/RgbColor.h" + +// project includes +#include "ProtoClientConnection.h" + +ProtoClientConnection::ProtoClientConnection(QTcpSocket *socket, Hyperion * hyperion) : + QObject(), + _socket(socket), + _imageProcessor(ImageProcessorFactory::getInstance().newImageProcessor()), + _hyperion(hyperion), + _receiveBuffer() +{ + // connect internal signals and slots + connect(_socket, SIGNAL(disconnected()), this, SLOT(socketClosed())); + connect(_socket, SIGNAL(readyRead()), this, SLOT(readData())); +} + +ProtoClientConnection::~ProtoClientConnection() +{ + delete _socket; +} + +void ProtoClientConnection::readData() +{ + _receiveBuffer += _socket->readAll(); + + // check if we can read a message size + if (_receiveBuffer.size() <= 4) + { + return; + } + + // read the message size + uint32_t messageSize = + ((_receiveBuffer[0]<<24) & 0xFF000000) | + ((_receiveBuffer[1]<<16) & 0x00FF0000) | + ((_receiveBuffer[2]<< 8) & 0x0000FF00) | + ((_receiveBuffer[3] ) & 0x000000FF); + + // check if we can read a complete message + if ((uint32_t) _receiveBuffer.size() < messageSize + 4) + { + return; + } + + // read a message + proto::HyperionRequest message; + if (!message.ParseFromArray(_receiveBuffer.data() + 4, messageSize)) + { + sendErrorReply("Unable to parse message"); + } + + // handle the message + handleMessage(message); + + // remove message data from buffer + _receiveBuffer = _receiveBuffer.mid(messageSize + 4); +} + +void ProtoClientConnection::socketClosed() +{ + emit connectionClosed(this); +} + +void ProtoClientConnection::handleMessage(const proto::HyperionRequest & message) +{ + switch (message.command()) + { + case proto::HyperionRequest::COLOR: + if (!message.HasExtension(proto::ColorRequest::colorRequest)) + { + sendErrorReply("Received COLOR command without ColorRequest"); + break; + } + handleColorCommand(message.GetExtension(proto::ColorRequest::colorRequest)); + break; + case proto::HyperionRequest::IMAGE: + if (!message.HasExtension(proto::ImageRequest::imageRequest)) + { + sendErrorReply("Received IMAGE command without ImageRequest"); + break; + } + handleImageCommand(message.GetExtension(proto::ImageRequest::imageRequest)); + break; + case proto::HyperionRequest::CLEAR: + if (!message.HasExtension(proto::ClearRequest::clearRequest)) + { + sendErrorReply("Received CLEAR command without ClearRequest"); + break; + } + handleClearCommand(message.GetExtension(proto::ClearRequest::clearRequest)); + break; + case proto::HyperionRequest::CLEARALL: + handleClearallCommand(); + break; + default: + handleNotImplemented(); + } +} + +void ProtoClientConnection::handleColorCommand(const proto::ColorRequest &message) +{ + // extract parameters + int priority = message.priority(); + int duration = message.has_duration() ? message.duration() : -1; + RgbColor color; + color.red = qRed(message.rgbcolor()); + color.green = qGreen(message.rgbcolor()); + color.blue = qBlue(message.rgbcolor()); + + // set output + _hyperion->setColor(priority, color, duration); + + // send reply + sendSuccessReply(); +} + +void ProtoClientConnection::handleImageCommand(const proto::ImageRequest &message) +{ + // extract parameters + int priority = message.priority(); + int duration = message.has_duration() ? message.duration() : -1; + int width = message.imagewidth(); + int height = message.imageheight(); + const std::string & imageData = message.imagedata(); + + // check consistency of the size of the received data + if ((int) imageData.size() != width*height*3) + { + sendErrorReply("Size of image data does not match with the width and height"); + return; + } + + // set width and height of the image processor + _imageProcessor->setSize(width, height); + + // create RgbImage + RgbImage image(width, height); + memcpy(image.memptr(), imageData.c_str(), imageData.size()); + + // process the image + std::vector ledColors = _imageProcessor->process(image); + _hyperion->setColors(priority, ledColors, duration); + + // send reply + sendSuccessReply(); +} + + +void ProtoClientConnection::handleClearCommand(const proto::ClearRequest &message) +{ + // extract parameters + int priority = message.priority(); + + // clear priority + _hyperion->clear(priority); + + // send reply + sendSuccessReply(); +} + +void ProtoClientConnection::handleClearallCommand() +{ + // clear priority + _hyperion->clearall(); + + // send reply + sendSuccessReply(); +} + + +void ProtoClientConnection::handleNotImplemented() +{ + sendErrorReply("Command not implemented"); +} + +void ProtoClientConnection::sendMessage(const google::protobuf::Message &message) +{ + std::string serializedReply = message.SerializeAsString(); + uint32_t size = serializedReply.size(); + uint8_t sizeData[] = {uint8_t(size >> 24), uint8_t(size >> 16), uint8_t(size >> 8), uint8_t(size)}; + _socket->write((const char *) sizeData, sizeof(sizeData)); + _socket->write(serializedReply.data(), serializedReply.length()); + _socket->flush(); +} + +void ProtoClientConnection::sendSuccessReply() +{ + // create reply + proto::HyperionReply reply; + reply.set_success(true); + + // send reply + sendMessage(reply); +} + +void ProtoClientConnection::sendErrorReply(const std::string &error) +{ + // create reply + proto::HyperionReply reply; + reply.set_success(false); + reply.set_error(error); + + // send reply + sendMessage(reply); +} diff --git a/libsrc/protoserver/ProtoClientConnection.h b/libsrc/protoserver/ProtoClientConnection.h new file mode 100644 index 00000000..ec7d2e1c --- /dev/null +++ b/libsrc/protoserver/ProtoClientConnection.h @@ -0,0 +1,126 @@ +#pragma once + +// stl includes +#include + +// Qt includes +#include +#include + +// Hyperion includes +#include + +// proto includes +#include "message.pb.h" + +class ImageProcessor; + +/// +/// The Connection object created by \a ProtoServer when a new connection is establshed +/// +class ProtoClientConnection : public QObject +{ + Q_OBJECT + +public: + /// + /// Constructor + /// @param socket The Socket object for this connection + /// @param hyperion The Hyperion server + /// + ProtoClientConnection(QTcpSocket * socket, Hyperion * hyperion); + + /// + /// Destructor + /// + ~ProtoClientConnection(); + +signals: + /// + /// Signal which is emitted when the connection is being closed + /// @param connection This connection object + /// + void connectionClosed(ProtoClientConnection * connection); + +private slots: + /// + /// Slot called when new data has arrived + /// + void readData(); + + /// + /// Slot called when this connection is being closed + /// + void socketClosed(); + +private: + /// + /// Handle an incoming Proto message + /// + /// @param message the incoming message as string + /// + void handleMessage(const proto::HyperionRequest &message); + + /// + /// Handle an incoming Proto Color message + /// + /// @param message the incoming message + /// + void handleColorCommand(const proto::ColorRequest & message); + + /// + /// Handle an incoming Proto Image message + /// + /// @param message the incoming message + /// + void handleImageCommand(const proto::ImageRequest & message); + + /// + /// Handle an incoming Proto Clear message + /// + /// @param message the incoming message + /// + void handleClearCommand(const proto::ClearRequest & message); + + /// + /// Handle an incoming Proto Clearall message + /// + void handleClearallCommand(); + + /// + /// Handle an incoming Proto message of unknown type + /// + void handleNotImplemented(); + + /// + /// Send a message to the connected client + /// + /// @param message The Proto message to send + /// + void sendMessage(const google::protobuf::Message &message); + + /// + /// Send a standard reply indicating success + /// + void sendSuccessReply(); + + /// + /// Send an error message back to the client + /// + /// @param error String describing the error + /// + void sendErrorReply(const std::string & error); + +private: + /// The TCP-Socket that is connected tot the Proto-client + QTcpSocket * _socket; + + /// The processor for translating images to led-values + ImageProcessor * _imageProcessor; + + /// Link to Hyperion for writing led-values to a priority channel + Hyperion * _hyperion; + + /// The buffer used for reading data from the socket + QByteArray _receiveBuffer; +}; diff --git a/libsrc/protoserver/ProtoServer.cpp b/libsrc/protoserver/ProtoServer.cpp new file mode 100644 index 00000000..a1afa30c --- /dev/null +++ b/libsrc/protoserver/ProtoServer.cpp @@ -0,0 +1,57 @@ +// system includes +#include + +// project includes +#include +#include "ProtoClientConnection.h" + +ProtoServer::ProtoServer(Hyperion *hyperion, uint16_t port) : + QObject(), + _hyperion(hyperion), + _server(), + _openConnections() +{ + if (!_server.listen(QHostAddress::Any, port)) + { + throw std::runtime_error("Proto server could not bind to port"); + } + + // Set trigger for incoming connections + connect(&_server, SIGNAL(newConnection()), this, SLOT(newConnection())); +} + +ProtoServer::~ProtoServer() +{ + foreach (ProtoClientConnection * connection, _openConnections) { + delete connection; + } +} + +uint16_t ProtoServer::getPort() const +{ + return _server.serverPort(); +} + +void ProtoServer::newConnection() +{ + QTcpSocket * socket = _server.nextPendingConnection(); + + if (socket != nullptr) + { + std::cout << "New proto connection" << std::endl; + ProtoClientConnection * connection = new ProtoClientConnection(socket, _hyperion); + _openConnections.insert(connection); + + // register slot for cleaning up after the connection closed + connect(connection, SIGNAL(connectionClosed(ProtoClientConnection*)), this, SLOT(closedConnection(ProtoClientConnection*))); + } +} + +void ProtoServer::closedConnection(ProtoClientConnection *connection) +{ + std::cout << "Proto connection closed" << std::endl; + _openConnections.remove(connection); + + // schedule to delete the connection object + connection->deleteLater(); +} diff --git a/libsrc/protoserver/message.proto b/libsrc/protoserver/message.proto new file mode 100644 index 00000000..cd1f087c --- /dev/null +++ b/libsrc/protoserver/message.proto @@ -0,0 +1,69 @@ +package proto; + +message HyperionRequest { + enum Command { + COLOR = 1; + IMAGE = 2; + CLEAR = 3; + CLEARALL = 4; + } + + // command specification + required Command command = 1; + + // extensions to define all specific requests + extensions 10 to 100; +} + +message ColorRequest { + extend HyperionRequest { + required ColorRequest colorRequest = 10; + } + + // priority to use when setting the color + required int32 priority = 1; + + // integer value containing the rgb color (0x00RRGGBB) + required int32 rgbColor = 2; + + // duration of the request (negative results in infinite) + optional int32 duration = 3; +} + +message ImageRequest { + extend HyperionRequest { + required ImageRequest imageRequest = 11; + } + + // priority to use when setting the image + required int32 priority = 1; + + // width of the image + required int32 imagewidth = 2; + + // height of the image + required int32 imageheight = 3; + + // image data + required bytes imagedata = 4; + + // duration of the request (negative results in infinite) + optional int32 duration = 5; +} + +message ClearRequest { + extend HyperionRequest { + required ClearRequest clearRequest = 12; + } + + // priority which need to be cleared + required int32 priority = 1; +} + +message HyperionReply { + // flag indication success or failure + required bool success = 1; + + // string indicating the reason for failure (if applicable) + optional string error = 2; +} diff --git a/libsrc/utils/HsvTransform.cpp b/libsrc/utils/HsvTransform.cpp index 3b147662..14554fa9 100644 --- a/libsrc/utils/HsvTransform.cpp +++ b/libsrc/utils/HsvTransform.cpp @@ -45,8 +45,6 @@ void HsvTransform::transform(uint8_t & red, uint8_t & green, uint8_t & blue) con uint8_t saturation, value; rgb2hsv(red, green, blue, hue, saturation, value); - std::cout << int(hue) << " " << int(saturation) << " " << int(value) << std::endl; - int s = saturation * _saturationGain; if (s > 255) saturation = 255; diff --git a/libsrc/xbmcvideochecker/XBMCVideoChecker.cpp b/libsrc/xbmcvideochecker/XBMCVideoChecker.cpp index ef72bded..7a1738ba 100644 --- a/libsrc/xbmcvideochecker/XBMCVideoChecker.cpp +++ b/libsrc/xbmcvideochecker/XBMCVideoChecker.cpp @@ -7,7 +7,7 @@ XBMCVideoChecker::XBMCVideoChecker(const std::string & address, uint16_t port, u QObject(), _address(QString::fromStdString(address)), _port(port), - _request("{\"jsonrpc\":\"2.0\",\"method\":\"Player.GetActivePlayers\",\"id\":666}"), + _request(R"({"jsonrpc":"2.0","method":"Player.GetActivePlayers","id":666})"), _timer(), _socket(), _grabVideo(grabVideo), diff --git a/src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/MiscConfigPanel.java b/src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/MiscConfigPanel.java index 686842b5..57c07af6 100644 --- a/src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/MiscConfigPanel.java +++ b/src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/MiscConfigPanel.java @@ -44,16 +44,16 @@ public class MiscConfigPanel extends JPanel { add(mMenuLabel); mMenuCombo = new JComboBox<>(new String[] {"On", "Off"}); - mMenuCombo.setSelectedItem("Off"); + mMenuCombo.setSelectedItem(mMiscConfig.mMenuOn? "On": "Off"); mMenuCombo.setToolTipText("Enables('On') or disbales('Off') the ambi-light in the XBMC Menu"); mMenuCombo.addActionListener(mActionListener); add(mMenuCombo); mVideoLabel = new JLabel("Video"); add(mVideoLabel); - + mVideoCombo = new JComboBox<>(new String[] {"On", "Off"}); - mVideoCombo.setSelectedItem("On"); + mVideoCombo.setSelectedItem(mMiscConfig.mVideoOn? "On": "Off"); mVideoCombo.setToolTipText("Enables('On') or disbales('Off') the ambi-light during video playback"); mVideoCombo.addActionListener(mActionListener); add(mVideoCombo); @@ -62,7 +62,7 @@ public class MiscConfigPanel extends JPanel { add(mPictureLabel); mPictureCombo = new JComboBox<>(new String[] {"On", "Off"}); - mPictureCombo.setSelectedItem("Off"); + mPictureCombo.setSelectedItem(mMiscConfig.mPictureOn? "On": "Off"); mPictureCombo.setToolTipText("Enables('On') or disbales('Off') the ambi-light when viewing pictures"); mPictureCombo.addActionListener(mActionListener); add(mPictureCombo); @@ -71,7 +71,7 @@ public class MiscConfigPanel extends JPanel { add(mAudioLabel); mAudioCombo = new JComboBox<>(new String[] {"On", "Off"}); - mAudioCombo.setSelectedItem("Off"); + mAudioCombo.setSelectedItem(mMiscConfig.mAudioOn? "On": "Off"); mAudioCombo.setToolTipText("Enables('On') or disbales('Off') the ambi-light when listing to audio"); mAudioCombo.addActionListener(mActionListener); add(mAudioCombo); diff --git a/src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/TestImageBBB_01.png.REMOVED.git-id b/src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/TestImageBBB_01.png.REMOVED.git-id index ca813483..b94c7fe7 100644 --- a/src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/TestImageBBB_01.png.REMOVED.git-id +++ b/src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/TestImageBBB_01.png.REMOVED.git-id @@ -1 +1 @@ -176f5c8e4a406929620764b19e77c5120570325c \ No newline at end of file +d37be0ef34a74fb15c9ec81e9ebadb57de62d294 \ No newline at end of file diff --git a/src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/TestImageBBB_02.png.REMOVED.git-id b/src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/TestImageBBB_02.png.REMOVED.git-id index b3f62ec5..0f2c589b 100644 --- a/src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/TestImageBBB_02.png.REMOVED.git-id +++ b/src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/TestImageBBB_02.png.REMOVED.git-id @@ -1 +1 @@ -048b062d931b7754a533734a1dec21692bce2dc6 \ No newline at end of file +1df18121f930623884da3de93f146f93d11f621c \ No newline at end of file diff --git a/src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/TestImageBBB_03.png.REMOVED.git-id b/src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/TestImageBBB_03.png.REMOVED.git-id index 5f5afb77..59984f1e 100644 --- a/src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/TestImageBBB_03.png.REMOVED.git-id +++ b/src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/TestImageBBB_03.png.REMOVED.git-id @@ -1 +1 @@ -bd23dc9ed11283d527effc78e437e8ef18903123 \ No newline at end of file +97019f8cd170a7792bb81ae09efd690669eef567 \ No newline at end of file diff --git a/src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/TestImage_01.png.REMOVED.git-id b/src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/TestImage_01.png.REMOVED.git-id index 202fe705..d8f772ef 100644 --- a/src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/TestImage_01.png.REMOVED.git-id +++ b/src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/TestImage_01.png.REMOVED.git-id @@ -1 +1 @@ -427d6ffe9935a3011f05b13bcf44a47796e99a80 \ No newline at end of file +b607e73b98996bfa40d19d508be01919552552d0 \ No newline at end of file diff --git a/src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/TestImage_02.png.REMOVED.git-id b/src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/TestImage_02.png.REMOVED.git-id index 4d26fccf..fc6e931f 100644 --- a/src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/TestImage_02.png.REMOVED.git-id +++ b/src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/TestImage_02.png.REMOVED.git-id @@ -1 +1 @@ -0dd04b5ec932aa251136740c6ea87f71847eb537 \ No newline at end of file +0d3aafb4a85649e53888a660b87de98f59f0ec32 \ No newline at end of file diff --git a/src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/TestImage_03.png.REMOVED.git-id b/src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/TestImage_03.png.REMOVED.git-id index bad5b106..09560055 100644 --- a/src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/TestImage_03.png.REMOVED.git-id +++ b/src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/TestImage_03.png.REMOVED.git-id @@ -1 +1 @@ -10fa576b5a494b9e0377e8b944904bfa7ac97cb6 \ No newline at end of file +7962c1194cb2a2b7af6c3b34151701e113f00087 \ No newline at end of file diff --git a/src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/TestImage_05.png.REMOVED.git-id b/src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/TestImage_05.png.REMOVED.git-id index e388ef7e..facc6405 100644 --- a/src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/TestImage_05.png.REMOVED.git-id +++ b/src/config-tool/ConfigTool/src/org/hyperion/hypercon/gui/TestImage_05.png.REMOVED.git-id @@ -1 +1 @@ -620cf4f8a4c8dc62cabe9045c1ad04c38cecb942 \ No newline at end of file +ad1aba652ea186845d0c340cbeca7efb9d662e10 \ No newline at end of file diff --git a/src/config-tool/ConfigTool/src/org/hyperion/hypercon/spec/ColorConfig.java b/src/config-tool/ConfigTool/src/org/hyperion/hypercon/spec/ColorConfig.java index afc91d98..ec445a77 100644 --- a/src/config-tool/ConfigTool/src/org/hyperion/hypercon/spec/ColorConfig.java +++ b/src/config-tool/ConfigTool/src/org/hyperion/hypercon/spec/ColorConfig.java @@ -10,30 +10,30 @@ public class ColorConfig { /** The saturation gain (in HSV space) */ double mSaturationGain = 1.0; /** The value gain (in HSV space) */ - double mValueGain = 1.0; + double mValueGain = 1.5; /** The minimum required RED-value (in RGB space) */ - double mRedThreshold = 0.0; + double mRedThreshold = 0.1; /** The gamma-curve correct for the RED-value (in RGB space) */ - double mRedGamma = 1.0; + double mRedGamma = 2.0; /** The black-level of the RED-value (in RGB space) */ double mRedBlacklevel = 0.0; /** The white-level of the RED-value (in RGB space) */ - double mRedWhitelevel = 1.0; + double mRedWhitelevel = 0.8; /** The minimum required GREEN-value (in RGB space) */ - double mGreenThreshold = 0.0; + double mGreenThreshold = 0.1; /** The gamma-curve correct for the GREEN-value (in RGB space) */ - double mGreenGamma = 1.0; + double mGreenGamma = 2.0; /** The black-level of the GREEN-value (in RGB space) */ double mGreenBlacklevel = 0.0; /** The white-level of the GREEN-value (in RGB space) */ double mGreenWhitelevel = 1.0; /** The minimum required BLUE-value (in RGB space) */ - double mBlueThreshold = 0.0; + double mBlueThreshold = 0.1; /** The gamma-curve correct for the BLUE-value (in RGB space) */ - double mBlueGamma = 1.0; + double mBlueGamma = 2.0; /** The black-level of the BLUE-value (in RGB space) */ double mBlueBlacklevel = 0.0; /** The white-level of the BLUE-value (in RGB space) */ @@ -75,8 +75,8 @@ public class ColorConfig { StringBuffer strBuf = new StringBuffer(); strBuf.append("\t\t\"hsv\" :\n"); strBuf.append("\t\t{\n"); - strBuf.append(String.format(Locale.ROOT, "\t\t\tsaturationGain : %.4f,\n", mSaturationGain)); - strBuf.append(String.format(Locale.ROOT, "\t\t\tvaluGain : %.4f\n", mValueGain)); + strBuf.append(String.format(Locale.ROOT, "\t\t\t\"saturationGain\" : %.4f,\n", mSaturationGain)); + strBuf.append(String.format(Locale.ROOT, "\t\t\t\"valuGain\" : %.4f\n", mValueGain)); strBuf.append("\t\t}"); return strBuf.toString(); @@ -92,26 +92,26 @@ public class ColorConfig { strBuf.append("\t\t\"red\" :\n"); strBuf.append("\t\t{\n"); - strBuf.append(String.format(Locale.ROOT, "\t\t\tthreshold : %.4f,\n", mRedThreshold)); - strBuf.append(String.format(Locale.ROOT, "\t\t\tgamma : %.4f,\n", mRedGamma)); - strBuf.append(String.format(Locale.ROOT, "\t\t\tblacklevel : %.4f,\n", mRedBlacklevel)); - strBuf.append(String.format(Locale.ROOT, "\t\t\twhitelevel : %.4f\n", mRedWhitelevel)); + strBuf.append(String.format(Locale.ROOT, "\t\t\t\"threshold\" : %.4f,\n", mRedThreshold)); + strBuf.append(String.format(Locale.ROOT, "\t\t\t\"gamma\" : %.4f,\n", mRedGamma)); + strBuf.append(String.format(Locale.ROOT, "\t\t\t\"blacklevel\" : %.4f,\n", mRedBlacklevel)); + strBuf.append(String.format(Locale.ROOT, "\t\t\t\"whitelevel\" : %.4f\n", mRedWhitelevel)); strBuf.append("\t\t},\n"); strBuf.append("\t\t\"green\" :\n"); strBuf.append("\t\t{\n"); - strBuf.append(String.format(Locale.ROOT, "\t\t\tthreshold : %.4f,\n", mGreenThreshold)); - strBuf.append(String.format(Locale.ROOT, "\t\t\tgamma : %.4f,\n", mGreenGamma)); - strBuf.append(String.format(Locale.ROOT, "\t\t\tblacklevel : %.4f,\n", mGreenBlacklevel)); - strBuf.append(String.format(Locale.ROOT, "\t\t\twhitelevel : %.4f\n", mGreenWhitelevel)); + strBuf.append(String.format(Locale.ROOT, "\t\t\t\"threshold\" : %.4f,\n", mGreenThreshold)); + strBuf.append(String.format(Locale.ROOT, "\t\t\t\"gamma\" : %.4f,\n", mGreenGamma)); + strBuf.append(String.format(Locale.ROOT, "\t\t\t\"blacklevel\" : %.4f,\n", mGreenBlacklevel)); + strBuf.append(String.format(Locale.ROOT, "\t\t\t\"whitelevel\" : %.4f\n", mGreenWhitelevel)); strBuf.append("\t\t},\n"); strBuf.append("\t\t\"blue\" :\n"); strBuf.append("\t\t{\n"); - strBuf.append(String.format(Locale.ROOT, "\t\t\tthreshold : %.4f,\n", mBlueThreshold)); - strBuf.append(String.format(Locale.ROOT, "\t\t\tgamma : %.4f,\n", mBlueGamma)); - strBuf.append(String.format(Locale.ROOT, "\t\t\tblacklevel : %.4f,\n", mBlueBlacklevel)); - strBuf.append(String.format(Locale.ROOT, "\t\t\twhitelevel : %.4f\n", mBlueWhitelevel)); + strBuf.append(String.format(Locale.ROOT, "\t\t\t\"threshold\" : %.4f,\n", mBlueThreshold)); + strBuf.append(String.format(Locale.ROOT, "\t\t\t\"gamma\" : %.4f,\n", mBlueGamma)); + strBuf.append(String.format(Locale.ROOT, "\t\t\t\"blacklevel\" : %.4f,\n", mBlueBlacklevel)); + strBuf.append(String.format(Locale.ROOT, "\t\t\t\"whitelevel\" : %.4f\n", mBlueWhitelevel)); strBuf.append("\t\t}"); return strBuf.toString(); diff --git a/src/config-tool/ConfigTool/src/org/hyperion/hypercon/spec/DeviceConfig.java b/src/config-tool/ConfigTool/src/org/hyperion/hypercon/spec/DeviceConfig.java index 468ae126..8147e7f3 100644 --- a/src/config-tool/ConfigTool/src/org/hyperion/hypercon/spec/DeviceConfig.java +++ b/src/config-tool/ConfigTool/src/org/hyperion/hypercon/spec/DeviceConfig.java @@ -12,7 +12,7 @@ public class DeviceConfig { /** The device 'file' name */ String mOutput = "/dev/spidev0.0"; /** The baudrate of the device */ - int mBaudrate = 48000; + int mBaudrate = 1000000; /** * Creates the JSON string of the configuration as used in the Hyperion daemon configfile diff --git a/src/config-tool/ConfigTool/src/org/hyperion/hypercon/spec/MiscConfig.java b/src/config-tool/ConfigTool/src/org/hyperion/hypercon/spec/MiscConfig.java index 5c2bbea6..5047df7a 100644 --- a/src/config-tool/ConfigTool/src/org/hyperion/hypercon/spec/MiscConfig.java +++ b/src/config-tool/ConfigTool/src/org/hyperion/hypercon/spec/MiscConfig.java @@ -18,8 +18,6 @@ public class MiscConfig { /** The interval of frame grabs (screen shots) [ms] */ public int mFrameGrabberInterval_ms = 100; - /** Flag enabling/disabling XBMC communication */ - public boolean mXbmcChecker = true; /** The IP-address of XBMC */ public String mXbmcAddress = "127.0.0.1"; /** The TCP JSON-Port of XBMC */ @@ -29,9 +27,15 @@ public class MiscConfig { /** Flag indicating that the frame-grabber is on during XBMC menu */ public boolean mMenuOn = false; /** Flag indicating that the frame-grabber is on during picture slideshow */ - public boolean mPictureOn = false; + public boolean mPictureOn = true; /** Flag indicating that the frame-grabber is on during audio playback */ - public boolean mAudioOn = false; + public boolean mAudioOn = true; + + /** The TCP port at which the JSON server is listening for incoming connections */ + public int mJsonPort = 19444; + + /** The TCP port at which the Protobuf server is listening for incoming connections */ + public int mProtoPort = 19445; /** * Creates the JSON string of the configuration as used in the Hyperion daemon configfile @@ -41,26 +45,6 @@ public class MiscConfig { public String toJsonString() { StringBuffer strBuf = new StringBuffer(); - strBuf.append("\t/// The configuration of the XBMC connection used to enable and disable the frame-grabber. Contains the following fields: \n"); - strBuf.append("\t/// * enable : Flag for enabling or disabling the XBMC-based frame grabbing\n"); - strBuf.append("\t/// * xbmcAddress : The IP address of the XBMC-host\n"); - strBuf.append("\t/// * xbmcTcpPort : The TCP-port of the XBMC-server\n"); - strBuf.append("\t/// * grabVideo : Flag indicating that the frame-grabber is on(true) during video playback\n"); - strBuf.append("\t/// * grabPictures : Flag indicating that the frame-grabber is on(true) during picture show\n"); - strBuf.append("\t/// * grabAudio : Flag indicating that the frame-grabber is on(true) during audio playback\n"); - strBuf.append("\t/// * grabMenu : Flag indicating that the frame-grabber is on(true) in the XBMC menu\n"); - - strBuf.append("\t\"xbmcVideoChecker\" :\n"); - strBuf.append("\t{\n"); - strBuf.append(String.format(Locale.ROOT, "\t\t\"enable\" : %s,\n", mXbmcChecker)); - strBuf.append(String.format(Locale.ROOT, "\t\t\"xbmcAddress\" : \"%s\",\n", mXbmcAddress)); - strBuf.append(String.format(Locale.ROOT, "\t\t\"xbmcTcpPort\" : %d,\n", mXbmcTcpPort)); - strBuf.append(String.format(Locale.ROOT, "\t\t\"grabVideo\" : %s,\n", mVideoOn)); - strBuf.append(String.format(Locale.ROOT, "\t\t\"grabPictures\" : %s,\n", mPictureOn)); - strBuf.append(String.format(Locale.ROOT, "\t\t\"grabAudio\" : %s,\n", mAudioOn)); - strBuf.append(String.format(Locale.ROOT, "\t\t\"grabMenu\" : %s\n", mMenuOn)); - strBuf.append("\t},\n"); - strBuf.append("\t/// The boot-sequence configuration, contains the following items: \n"); strBuf.append("\t/// * type : The type of the boot-sequence ('rainbow', 'knight_rider', 'none') \n"); strBuf.append("\t/// * duration_ms : The length of the boot-sequence [ms]\n"); @@ -69,8 +53,8 @@ public class MiscConfig { strBuf.append("\t{\n"); strBuf.append(String.format(Locale.ROOT, "\t\t\"type\" : \"%s\",\n", mBootSequence)); strBuf.append(String.format(Locale.ROOT, "\t\t\"duration_ms\" : %d\n", mBootSequenceLength_ms)); - strBuf.append("\t},\n"); - + strBuf.append("\t},\n\n"); + strBuf.append("\t/// The configuration for the frame-grabber, contains the following items: \n"); strBuf.append("\t/// * width : The width of the grabbed frames [pixels]\n"); @@ -82,10 +66,44 @@ public class MiscConfig { strBuf.append(String.format(Locale.ROOT, "\t\t\"width\" : %d,\n", mFrameGrabberWidth)); strBuf.append(String.format(Locale.ROOT, "\t\t\"height\" : %d,\n", mFrameGrabberHeight)); strBuf.append(String.format(Locale.ROOT, "\t\t\"frequency_Hz\" : %.1f\n", 1000.0/mFrameGrabberInterval_ms)); - strBuf.append("\t}"); + strBuf.append("\t},\n\n"); + + strBuf.append("\t/// The configuration of the XBMC connection used to enable and disable the frame-grabber. Contains the following fields: \n"); + strBuf.append("\t/// * xbmcAddress : The IP address of the XBMC-host\n"); + strBuf.append("\t/// * xbmcTcpPort : The TCP-port of the XBMC-server\n"); + strBuf.append("\t/// * grabVideo : Flag indicating that the frame-grabber is on(true) during video playback\n"); + strBuf.append("\t/// * grabPictures : Flag indicating that the frame-grabber is on(true) during picture show\n"); + strBuf.append("\t/// * grabAudio : Flag indicating that the frame-grabber is on(true) during audio playback\n"); + strBuf.append("\t/// * grabMenu : Flag indicating that the frame-grabber is on(true) in the XBMC menu\n"); + + strBuf.append("\t\"xbmcVideoChecker\" :\n"); + strBuf.append("\t{\n"); + strBuf.append(String.format(Locale.ROOT, "\t\t\"xbmcAddress\" : \"%s\",\n", mXbmcAddress)); + strBuf.append(String.format(Locale.ROOT, "\t\t\"xbmcTcpPort\" : %d,\n", mXbmcTcpPort)); + strBuf.append(String.format(Locale.ROOT, "\t\t\"grabVideo\" : %s,\n", mVideoOn)); + strBuf.append(String.format(Locale.ROOT, "\t\t\"grabPictures\" : %s,\n", mPictureOn)); + strBuf.append(String.format(Locale.ROOT, "\t\t\"grabAudio\" : %s,\n", mAudioOn)); + strBuf.append(String.format(Locale.ROOT, "\t\t\"grabMenu\" : %s\n", mMenuOn)); + strBuf.append("\t},\n\n"); + + + strBuf.append("\t/// The configuration of the Json server which enables the json remote interface\n"); + strBuf.append("\t/// * port : Port at which the json server is started\n"); + strBuf.append("\t\"jsonServer\" :\n"); + strBuf.append("\t{\n"); + strBuf.append(String.format(Locale.ROOT, "\t\t\"port\" : %d\n", mJsonPort)); + strBuf.append("\t},\n\n"); + + + strBuf.append("\t/// The configuration of the Proto server which enables the protobuffer remote interface\n"); + strBuf.append("\t/// * port : Port at which the protobuffer server is started\n"); + + strBuf.append("\t\"protoServer\" :\n"); + strBuf.append("\t{\n"); + strBuf.append(String.format(Locale.ROOT, "\t\t\"port\" : %d\n", mProtoPort)); + strBuf.append("\t}"); + return strBuf.toString(); } - - } diff --git a/src/hyperiond/CMakeLists.txt b/src/hyperiond/CMakeLists.txt index 92f7d83b..01e3488a 100644 --- a/src/hyperiond/CMakeLists.txt +++ b/src/hyperiond/CMakeLists.txt @@ -7,4 +7,5 @@ target_link_libraries(hyperiond hyperion dispmanx-grabber xbmcvideochecker - jsonserver) + jsonserver + protoserver) diff --git a/src/hyperiond/hyperiond.cpp b/src/hyperiond/hyperiond.cpp index 71fc9a84..39e50f36 100644 --- a/src/hyperiond/hyperiond.cpp +++ b/src/hyperiond/hyperiond.cpp @@ -1,4 +1,3 @@ - // C++ includes #include @@ -24,6 +23,9 @@ // JsonServer includes #include +// ProtoServer includes +#include + void signal_handler(const int signum) { QCoreApplication::quit(); @@ -66,7 +68,7 @@ int main(int argc, char** argv) { std::cout << "Missing required configuration file. Usage:" << std::endl; std::cout << "hyperiond [config.file]" << std::endl; - return 0; + return 1; } const std::string configFile = argv[1]; @@ -76,14 +78,24 @@ int main(int argc, char** argv) Hyperion hyperion(config); std::cout << "Hyperion created and initialised" << std::endl; - BootSequence * bootSequence = BootSequenceFactory::createBootSequence(&hyperion, config["bootsequence"]); - if (bootSequence) + // create boot sequence if the configuration is present + BootSequence * bootSequence = nullptr; + if (config.isMember("bootsequence")) { - bootSequence->start(); + bootSequence = BootSequenceFactory::createBootSequence(&hyperion, config["bootsequence"]); + + if (bootSequence != nullptr) + { + bootSequence->start(); + } } - const Json::Value & videoCheckerConfig = config["xbmcVideoChecker"]; - XBMCVideoChecker xbmcVideoChecker( + // create XBMC video checker if the configuration is present + XBMCVideoChecker * xbmcVideoChecker = nullptr; + if (config.isMember("xbmcVideoChecker")) + { + const Json::Value & videoCheckerConfig = config["xbmcVideoChecker"]; + xbmcVideoChecker = new XBMCVideoChecker( videoCheckerConfig["xbmcAddress"].asString(), videoCheckerConfig["xbmcTcpPort"].asUInt(), 1000, @@ -91,38 +103,67 @@ int main(int argc, char** argv) videoCheckerConfig["grabPictures"].asBool(), videoCheckerConfig["grabAudio"].asBool(), videoCheckerConfig["grabMenu"].asBool()); - if (videoCheckerConfig["enable"].asBool()) - { - xbmcVideoChecker.start(); + + xbmcVideoChecker->start(); std::cout << "XBMC video checker created and started" << std::endl; } - // Construct and start the frame-grabber - const Json::Value & frameGrabberConfig = config["framegrabber"]; - DispmanxWrapper dispmanx( + // Construct and start the frame-grabber if the configuration is present + DispmanxWrapper * dispmanx = nullptr; + if (config.isMember("framegrabber")) + { + const Json::Value & frameGrabberConfig = config["framegrabber"]; + dispmanx = new DispmanxWrapper( frameGrabberConfig["width"].asUInt(), frameGrabberConfig["height"].asUInt(), frameGrabberConfig["frequency_Hz"].asUInt(), &hyperion); - QObject::connect(&xbmcVideoChecker, SIGNAL(grabbingMode(GrabbingMode)), &dispmanx, SLOT(setGrabbingMode(GrabbingMode))); - dispmanx.start(); - std::cout << "Frame grabber created and started" << std::endl; - JsonServer jsonServer(&hyperion); - std::cout << "Json server created and started on port " << jsonServer.getPort() << std::endl; + if (xbmcVideoChecker != nullptr) + { + QObject::connect(xbmcVideoChecker, SIGNAL(grabbingMode(GrabbingMode)), dispmanx, SLOT(setGrabbingMode(GrabbingMode))); + } + else + { + dispmanx->setGrabbingMode(GRABBINGMODE_VIDEO); + } + + dispmanx->start(); + std::cout << "Frame grabber created and started" << std::endl; + } + + // Create Json server if configuration is present + JsonServer * jsonServer = nullptr; + if (config.isMember("jsonServer")) + { + const Json::Value & jsonServerConfig = config["jsonServer"]; + jsonServer = new JsonServer(&hyperion, jsonServerConfig["port"].asUInt()); + std::cout << "Json server created and started on port " << jsonServer->getPort() << std::endl; + } + + // Create Proto server if configuration is present + ProtoServer * protoServer = nullptr; + if (config.isMember("protoServer")) + { + const Json::Value & protoServerConfig = config["protoServer"]; + protoServer = new ProtoServer(&hyperion, protoServerConfig["port"].asUInt()); + std::cout << "Proto server created and started on port " << protoServer->getPort() << std::endl; + } // run the application int rc = app.exec(); std::cout << "Application closed" << std::endl; - // Stop the frame grabber - dispmanx.stop(); - - // Delete the boot sequence - delete bootSequence; - // Clear all colors (switchting off all leds) hyperion.clearall(); + // Delete all component + delete bootSequence; + delete dispmanx; + delete xbmcVideoChecker; + delete jsonServer; + delete protoServer; + + // leave application return rc; } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 8a80c9c5..f35f9c94 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -7,7 +7,6 @@ add_executable(test_spi target_link_libraries(test_spi hyperion) - add_executable(test_configfile TestConfigFile.cpp) target_link_libraries(test_configfile @@ -39,3 +38,6 @@ add_executable(test_blackborderprocessor TestBlackBorderProcessor.cpp) target_link_libraries(test_blackborderprocessor hyperion) + +add_executable(spidev_test spidev_test.c) +add_executable(gpio2spi switchPinCtrl.c) diff --git a/test/spidev_test.c b/test/spidev_test.c new file mode 100644 index 00000000..b3a7080f --- /dev/null +++ b/test/spidev_test.c @@ -0,0 +1,210 @@ +/* + * SPI testing utility (using spidev driver) + * + * Copyright (c) 2007 MontaVista Software, Inc. + * Copyright (c) 2007 Anton Vorontsov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License. + * + * Cross-compile with cross-gcc -I/path/to/cross-kernel/include + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) + +static void pabort(const char *s) +{ + perror(s); + abort(); +} + +static const char *device = "/dev/spidev0.0"; +static uint8_t mode; +static uint8_t bits = 8; +static uint32_t speed = 500000; +static uint16_t delay; + +static void transfer(int fd) +{ + int ret; + uint8_t tx[] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x40, 0x00, 0x00, 0x00, 0x00, 0x95, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xDE, 0xAD, 0xBE, 0xEF, 0xBA, 0xAD, + 0xF0, 0x0D, + }; + uint8_t rx[ARRAY_SIZE(tx)] = {0, }; + struct spi_ioc_transfer tr = { + .tx_buf = (unsigned long)tx, + .rx_buf = (unsigned long)rx, + .len = ARRAY_SIZE(tx), + .delay_usecs = delay, + .speed_hz = speed, + .bits_per_word = bits, + }; + + ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr); + if (ret < 1) + pabort("can't send spi message"); + + for (ret = 0; ret < ARRAY_SIZE(tx); ret++) { + if (!(ret % 6)) + puts(""); + printf("%.2X ", rx[ret]); + } + puts(""); +} + +static void print_usage(const char *prog) +{ + printf("Usage: %s [-DsbdlHOLC3]\n", prog); + puts(" -D --device device to use (default /dev/spidev0.0)\n" + " -s --speed max speed (Hz)\n" + " -d --delay delay (usec)\n" + " -b --bpw bits per word \n" + " -l --loop loopback\n" + " -H --cpha clock phase\n" + " -O --cpol clock polarity\n" + " -L --lsb least significant bit first\n" + " -C --cs-high chip select active high\n" + " -3 --3wire SI/SO signals shared\n"); + exit(1); +} + +static void parse_opts(int argc, char *argv[]) +{ + while (1) { + static const struct option lopts[] = { + { "device", 1, 0, 'D' }, + { "speed", 1, 0, 's' }, + { "delay", 1, 0, 'd' }, + { "bpw", 1, 0, 'b' }, + { "loop", 0, 0, 'l' }, + { "cpha", 0, 0, 'H' }, + { "cpol", 0, 0, 'O' }, + { "lsb", 0, 0, 'L' }, + { "cs-high", 0, 0, 'C' }, + { "3wire", 0, 0, '3' }, + { "no-cs", 0, 0, 'N' }, + { "ready", 0, 0, 'R' }, + { NULL, 0, 0, 0 }, + }; + int c; + + c = getopt_long(argc, argv, "D:s:d:b:lHOLC3NR", lopts, NULL); + + if (c == -1) + break; + + switch (c) { + case 'D': + device = optarg; + break; + case 's': + speed = atoi(optarg); + break; + case 'd': + delay = atoi(optarg); + break; + case 'b': + bits = atoi(optarg); + break; + case 'l': + mode |= SPI_LOOP; + break; + case 'H': + mode |= SPI_CPHA; + break; + case 'O': + mode |= SPI_CPOL; + break; + case 'L': + mode |= SPI_LSB_FIRST; + break; + case 'C': + mode |= SPI_CS_HIGH; + break; + case '3': + mode |= SPI_3WIRE; + break; + case 'N': + mode |= SPI_NO_CS; + break; + case 'R': + mode |= SPI_READY; + break; + default: + print_usage(argv[0]); + break; + } + } +} + +int main(int argc, char *argv[]) +{ + int ret = 0; + int fd; + + parse_opts(argc, argv); + + fd = open(device, O_RDWR); + if (fd < 0) + pabort("can't open device"); + + /* + * spi mode + */ + ret = ioctl(fd, SPI_IOC_WR_MODE, &mode); + if (ret == -1) + pabort("can't set spi mode"); + + ret = ioctl(fd, SPI_IOC_RD_MODE, &mode); + if (ret == -1) + pabort("can't get spi mode"); + + /* + * bits per word + */ + ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits); + if (ret == -1) + pabort("can't set bits per word"); + + ret = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits); + if (ret == -1) + pabort("can't get bits per word"); + + /* + * max speed hz + */ + ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed); + if (ret == -1) + pabort("can't set max speed hz"); + + ret = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed); + if (ret == -1) + pabort("can't get max speed hz"); + + printf("spi mode: %d\n", mode); + printf("bits per word: %d\n", bits); + printf("max speed: %d Hz (%d KHz)\n", speed, speed/1000); + + transfer(fd); + + close(fd); + + return ret; +} diff --git a/test/switchPinCtrl.c b/test/switchPinCtrl.c new file mode 100644 index 00000000..0bd5b132 --- /dev/null +++ b/test/switchPinCtrl.c @@ -0,0 +1,177 @@ +// +// Simple byte wise SPI driver +// Demo how to set up memmap and access SPI registers. +// Code seems to be working but has not been much tested. +// G.J. van Loo 15-Jan-2012 +// + + + +// Access from ARM Running Linux + +#define BCM2708_PERI_BASE 0x20000000 +#define UART0_BASE (BCM2708_PERI_BASE + 0x201000) /* Uart 0 */ +#define UART1_BASE (BCM2708_PERI_BASE + 0x215000) /* Uart 1 */ +#define MCORE_BASE (BCM2708_PERI_BASE + 0x0000) /* Fake frame buffer device */ +#define GPIO_BASE (BCM2708_PERI_BASE + 0x200000) /* GPIO controller */ +#define SPI0_BASE (BCM2708_PERI_BASE + 0x204000) /* SPI0 controller */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define PAGE_SIZE (4*1024) +#define BLOCK_SIZE (4*1024) + +int mem_fd; +char *gpio_mem, *gpio_map; +char *spi0_mem, *spi0_map; + + +// I/O access +volatile unsigned *gpio; +volatile unsigned *spi0; + + +// SPI operation + +// GPIO setup macros. Always use INP_GPIO(x) before using OUT_GPIO(x) or SET_GPIO_ALT(x,y) +#define INP_GPIO(g) *(gpio+((g)/10)) &= ~(7<<(((g)%10)*3)) +#define OUT_GPIO(g) *(gpio+((g)/10)) |= (1<<(((g)%10)*3)) +#define SET_GPIO_ALT(g,a) *(gpio+(((g)/10))) |= (((a)<=3?(a)+4:(a)==4?3:2)<<(((g)%10)*3)) + +// +#define SPI0_CNTLSTAT *(spi0 + 0) +#define SPI0_FIFO *(spi0 + 1) +#define SPI0_CLKSPEED *(spi0 + 2) + +// SPI0_CNTLSTAT register bits + +#define SPI0_CS_CS2ACTHIGH 0x00800000 // CS2 active high +#define SPI0_CS_CS1ACTHIGH 0x00400000 // CS1 active high +#define SPI0_CS_CS0ACTHIGH 0x00200000 // CS0 active high +#define SPI0_CS_RXFIFOFULL 0x00100000 // Receive FIFO full +#define SPI0_CS_RXFIFO3_4 0x00080000 // Receive FIFO 3/4 full +#define SPI0_CS_TXFIFOSPCE 0x00040000 // Transmit FIFO has space +#define SPI0_CS_RXFIFODATA 0x00020000 // Receive FIFO has data +#define SPI0_CS_DONE 0x00010000 // SPI transfer done. WRT to CLR! +#define SPI0_CS_MOSI_INPUT 0x00001000 // MOSI is input, read from MOSI (BI-dir mode) +#define SPI0_CS_DEASRT_CS 0x00000800 // De-assert CS at end +#define SPI0_CS_RX_IRQ 0x00000400 // Receive irq enable +#define SPI0_CS_DONE_IRQ 0x00000200 // irq when done +#define SPI0_CS_DMA_ENABLE 0x00000100 // Run in DMA mode +#define SPI0_CS_ACTIVATE 0x00000080 // Activate: be high before starting +#define SPI0_CS_CS_POLARIT 0x00000040 // Chip selects active high +#define SPI0_CS_CLRTXFIFO 0x00000020 // Clear TX FIFO (auto clear bit) +#define SPI0_CS_CLRRXFIFO 0x00000010 // Clear RX FIFO (auto clear bit) +#define SPI0_CS_CLRFIFOS 0x00000030 // Clear BOTH FIFOs (auto clear bit) +#define SPI0_CS_CLK_IDLHI 0x00000008 // Clock pin is high when idle +#define SPI0_CS_CLKTRANS 0x00000004 // 0=first clock in middle of data bit + // 1=first clock at begin of data bit +#define SPI0_CS_CHIPSEL0 0x00000000 // Use chip select 0 +#define SPI0_CS_CHIPSEL1 0x00000001 // Use chip select 1 +#define SPI0_CS_CHIPSEL2 0x00000002 // Use chip select 2 +#define SPI0_CS_CHIPSELN 0x00000003 // No chip select (e.g. use GPIO pin) + +#define SPI0_CS_CLRALL (SPI0_CS_CLRFIFOS|SPI0_CS_DONE) + +#define ISASC(x) ((x)>=0x20 && (x)<=0x7F) + +void setup_io(); + +int main(int argc, char **argv) +{ int g; + + setup_io(); // Set up direct access to I/O for GPIO and SPI + + // Switch GPIO 7..11 to SPI mode (ALT function 0) + + /************************************************************************\ + * You are about to change the GPIO settings of your computer. * + * Mess this up and it will stop working! * + * It might be a good idea to 'sync' before running this program * + * so at least you still have your code changes written to the SD-card! * + \************************************************************************/ + + for (g=7; g<=11; g++) + { + INP_GPIO(g); // clear bits (= input) + SET_GPIO_ALT(g,0); // set function 0 + } + + return 0; + +} // main + + +// +// Set up a memory regions to access GPIO and SPI0 +// +void setup_io() +{ + + /* open /dev/mem */ + if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC) ) < 0) { + printf("can't open /dev/mem \n"); + exit (-1); + } + + /* mmap GPIO */ + if ((gpio_mem = malloc(BLOCK_SIZE + (PAGE_SIZE-1))) == NULL) { + printf("allocation error \n"); + exit (-1); + } + if ((unsigned long)gpio_mem % PAGE_SIZE) + gpio_mem += PAGE_SIZE - ((unsigned long)gpio_mem % PAGE_SIZE); + + gpio_map = (unsigned char *)mmap( + (caddr_t)gpio_mem, + BLOCK_SIZE, + PROT_READ|PROT_WRITE, + MAP_SHARED|MAP_FIXED, + mem_fd, + GPIO_BASE + ); + + if ((long)gpio_map < 0) { + printf("mmap error %d\n", (int)gpio_map); + exit (-1); + } + gpio = (volatile unsigned *)gpio_map; + + /* mmap SPI0 */ + if ((spi0_mem = malloc(BLOCK_SIZE + (PAGE_SIZE-1))) == NULL) { + printf("allocation error \n"); + exit (-1); + } + if ((unsigned long)spi0_mem % PAGE_SIZE) + spi0_mem += PAGE_SIZE - ((unsigned long)spi0_mem % PAGE_SIZE); + + spi0_map = (unsigned char *)mmap( + (caddr_t)spi0_mem, + BLOCK_SIZE, + PROT_READ|PROT_WRITE, + MAP_SHARED|MAP_FIXED, + mem_fd, + SPI0_BASE + ); + + + printf("SPI mapped from 0x%p to 0x%p\n",SPI0_BASE,spi0_map); + + if ((long)spi0_map < 0) { + printf("mmap error %d\n", (int)spi0_map); + exit (-1); + } + spi0 = (volatile unsigned *)spi0_map; + +} // setup_io