From 6d6f4bf6298e0387f4c0f94a4dc2b07a40471c7a Mon Sep 17 00:00:00 2001 From: spudwebb Date: Tue, 22 Apr 2014 14:15:09 -0400 Subject: [PATCH 01/74] add color parameter to the knight rider effect Former-commit-id: 9d4cd0e18b7871acd76a9cafb04efc39ce6fac74 --- effects/knight-rider.json | 3 ++- effects/knight-rider.py | 13 ++++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/effects/knight-rider.json b/effects/knight-rider.json index bf83f897..b4644387 100644 --- a/effects/knight-rider.json +++ b/effects/knight-rider.json @@ -4,6 +4,7 @@ "args" : { "speed" : 1.0, - "fadeFactor" : 0.7 + "fadeFactor" : 0.7, + "color" : [255,0,0] } } diff --git a/effects/knight-rider.py b/effects/knight-rider.py index d2ff3ecc..38c8aabd 100644 --- a/effects/knight-rider.py +++ b/effects/knight-rider.py @@ -2,9 +2,10 @@ import hyperion import time import colorsys -# Get the rotation time +# Get the parameters speed = float(hyperion.args.get('speed', 1.0)) fadeFactor = float(hyperion.args.get('fadeFactor', 0.7)) +color = hyperion.args.get('color', (255,0,0)) # Check parameters speed = max(0.0001, speed) @@ -13,7 +14,9 @@ fadeFactor = max(0.0, min(fadeFactor, 1.0)) # Initialize the led data width = 25 imageData = bytearray(width * (0,0,0)) -imageData[0] = 255 +imageData[0] = color[0] +imageData[1] = color[1] +imageData[2] = color[2] # Calculate the sleep time and rotation increment increment = 1 @@ -41,9 +44,13 @@ while not hyperion.abort(): # Fade the old data for j in range(width): imageData[3*j] = int(fadeFactor * imageData[3*j]) + imageData[3*j+1] = int(fadeFactor * imageData[3*j+1]) + imageData[3*j+2] = int(fadeFactor * imageData[3*j+2]) # Insert new data - imageData[3*position] = 255 + imageData[3*position] = color[0] + imageData[3*position+1] = color[1] + imageData[3*position+2] = color[2] # Sleep for a while time.sleep(sleepTime) From b619fcda5528b55457bdd58553a58ddf2925efc4 Mon Sep 17 00:00:00 2001 From: bimsarck Date: Sat, 26 Apr 2014 00:58:47 +0200 Subject: [PATCH 02/74] add 3D autodetection for xbmc with versions check Former-commit-id: 6fd0195fdae82ebe5a26a6ce1138164e09e8b929 --- include/xbmcvideochecker/XBMCVideoChecker.h | 10 ++- libsrc/xbmcvideochecker/XBMCVideoChecker.cpp | 83 ++++++++++++++++---- 2 files changed, 75 insertions(+), 18 deletions(-) diff --git a/include/xbmcvideochecker/XBMCVideoChecker.h b/include/xbmcvideochecker/XBMCVideoChecker.h index 08fc4236..d016b0d4 100644 --- a/include/xbmcvideochecker/XBMCVideoChecker.h +++ b/include/xbmcvideochecker/XBMCVideoChecker.h @@ -38,7 +38,7 @@ public: /// @param grabPhoto Whether or not to grab when the XBMC photo player is playing /// @param grabAudio Whether or not to grab when the XBMC audio player is playing /// @param grabMenu Whether or not to grab when nothing is playing (in XBMC menu) - /// @param grabScreensaver Whether or not to grab when the XBMC screensaver is activated + /// @param grabScreensaver Whether or not to grab when the XBMC screensaver is activated /// @param enable3DDetection Wheter or not to enable the detection of 3D movies playing /// XBMCVideoChecker(const std::string & address, uint16_t port, bool grabVideo, bool grabPhoto, bool grabAudio, bool grabMenu, bool grabScreensaver, bool enable3DDetection); @@ -96,6 +96,12 @@ private: /// The JSON-RPC message to check the screensaver const QString _checkScreensaverRequest; + /// The JSON-RPC message to check the active stereoscopicmode + const QString _getStereoscopicMode; + + /// The JSON-RPC message to check the xbmc version + const QString _getXbmcVersion; + /// The QT TCP Socket with connection to XBMC QTcpSocket _socket; @@ -111,7 +117,7 @@ private: /// Flag indicating whether or not to grab when XBMC is playing nothing (in menu) const bool _grabMenu; - /// Flag inidcating whether or not to grab when the XBMC screensaver is activated + /// Flag indicating whether or not to grab when the XBMC screensaver is activated const bool _grabScreensaver; /// Flag indicating wheter or not to enable the detection of 3D movies playing diff --git a/libsrc/xbmcvideochecker/XBMCVideoChecker.cpp b/libsrc/xbmcvideochecker/XBMCVideoChecker.cpp index 0c396693..ad7843b2 100644 --- a/libsrc/xbmcvideochecker/XBMCVideoChecker.cpp +++ b/libsrc/xbmcvideochecker/XBMCVideoChecker.cpp @@ -17,6 +17,12 @@ // {"id":668,"jsonrpc":"2.0","method":"XBMC.GetInfoBooleans","params":{"booleans":["System.ScreenSaverActive"]}} // {"id":668,"jsonrpc":"2.0","result":{"System.ScreenSaverActive":false}} +// Request stereoscopicmode example: +// {"jsonrpc":"2.0","method":"GUI.GetProperties","params":{"properties":["stereoscopicmode"]},"id":1} +// {"id":1,"jsonrpc":"2.0","result":{"stereoscopicmode":{"label":"Nebeneinander","mode":"split_vertical"}}} + +int xbmcVersion = 0; + XBMCVideoChecker::XBMCVideoChecker(const std::string & address, uint16_t port, bool grabVideo, bool grabPhoto, bool grabAudio, bool grabMenu, bool grabScreensaver, bool enable3DDetection) : QObject(), _address(QString::fromStdString(address)), @@ -24,6 +30,8 @@ XBMCVideoChecker::XBMCVideoChecker(const std::string & address, uint16_t port, b _activePlayerRequest(R"({"id":666,"jsonrpc":"2.0","method":"Player.GetActivePlayers"})"), _currentPlayingItemRequest(R"({"id":667,"jsonrpc":"2.0","method":"Player.GetItem","params":{"playerid":%1,"properties":["file"]}})"), _checkScreensaverRequest(R"({"id":668,"jsonrpc":"2.0","method":"XBMC.GetInfoBooleans","params":{"booleans":["System.ScreenSaverActive"]}})"), + _getStereoscopicMode(R"({"jsonrpc":"2.0","method":"GUI.GetProperties","params":{"properties":["stereoscopicmode"]},"id":1})"), + _getXbmcVersion(R"({"jsonrpc":"2.0","method":"Application.GetProperties","params":{"properties":["version"]},"id":1})"), _socket(), _grabVideo(grabVideo), _grabPhoto(grabPhoto), @@ -116,24 +124,32 @@ void XBMCVideoChecker::receiveReply() } else if (reply.contains("\"id\":667")) { - // result of Player.GetItem - // TODO: what if the filename contains a '"'. In Json this should have been escaped - QRegExp regex("\"file\":\"((?!\").)*\""); - int pos = regex.indexIn(reply); - if (pos > 0) + if (xbmcVersion >= 13) { - QStringRef filename = QStringRef(&reply, pos+8, regex.matchedLength()-9); - if (filename.contains("3DSBS", Qt::CaseInsensitive) || filename.contains("HSBS", Qt::CaseInsensitive)) + // check of active stereoscopicmode + _socket.write(_getStereoscopicMode.toUtf8()); + } + else + { + // result of Player.GetItem + // TODO: what if the filename contains a '"'. In Json this should have been escaped + QRegExp regex("\"file\":\"((?!\").)*\""); + int pos = regex.indexIn(reply); + if (pos > 0) { - setVideoMode(VIDEO_3DSBS); - } - else if (filename.contains("3DTAB", Qt::CaseInsensitive) || filename.contains("HTAB", Qt::CaseInsensitive)) - { - setVideoMode(VIDEO_3DTAB); - } - else - { - setVideoMode(VIDEO_2D); + QStringRef filename = QStringRef(&reply, pos+8, regex.matchedLength()-9); + if (filename.contains("3DSBS", Qt::CaseInsensitive) || filename.contains("HSBS", Qt::CaseInsensitive)) + { + setVideoMode(VIDEO_3DSBS); + } + else if (filename.contains("3DTAB", Qt::CaseInsensitive) || filename.contains("HTAB", Qt::CaseInsensitive)) + { + setVideoMode(VIDEO_3DTAB); + } + else + { + setVideoMode(VIDEO_2D); + } } } } @@ -142,6 +158,41 @@ void XBMCVideoChecker::receiveReply() // result of System.ScreenSaverActive bool active = reply.contains("\"System.ScreenSaverActive\":true"); setScreensaverMode(!_grabScreensaver && active); + + // check here xbmc version + if (_socket.state() == QTcpSocket::ConnectedState) + { + if (xbmcVersion == 0) + { + _socket.write(_getXbmcVersion.toUtf8()); + } + } + } + else if (reply.contains("\"stereoscopicmode\"")) + { + QRegExp regex("\"mode\":\"(split_vertical|split_horizontal)\""); + int pos = regex.indexIn(reply); + if (pos > 0) + { + QString sMode = regex.cap(1); + if (sMode == "split_vertical") + { + setVideoMode(VIDEO_3DSBS); + } + else if (sMode == "split_horizontal") + { + setVideoMode(VIDEO_3DTAB); + } + } + } + else if (reply.contains("\"version\":")) + { + QRegExp regex("\"major\":(\\d+)"); + int pos = regex.indexIn(reply); + if (pos > 0) + { + xbmcVersion = regex.cap(1).toInt(); + } } } From 88c523518a92d61a4562135fffacf55f28045fa0 Mon Sep 17 00:00:00 2001 From: ntim Date: Sun, 27 Apr 2014 12:59:44 +0200 Subject: [PATCH 03/74] Initial commit of support for the Philips Hue system. Former-commit-id: 5b7d802c326151ee96a5b950badb01e94adfe7f3 --- libsrc/leddevice/CMakeLists.txt | 2 + libsrc/leddevice/LedDeviceFactory.cpp | 6 ++ libsrc/leddevice/LedDevicePhilipsHue.cpp | 81 +++++++++++++++++++++ libsrc/leddevice/LedDevicePhilipsHue.h | 90 ++++++++++++++++++++++++ 4 files changed, 179 insertions(+) mode change 100644 => 100755 libsrc/leddevice/CMakeLists.txt mode change 100644 => 100755 libsrc/leddevice/LedDeviceFactory.cpp create mode 100755 libsrc/leddevice/LedDevicePhilipsHue.cpp create mode 100755 libsrc/leddevice/LedDevicePhilipsHue.h diff --git a/libsrc/leddevice/CMakeLists.txt b/libsrc/leddevice/CMakeLists.txt old mode 100644 new mode 100755 index 443e845d..098ac38c --- a/libsrc/leddevice/CMakeLists.txt +++ b/libsrc/leddevice/CMakeLists.txt @@ -29,6 +29,7 @@ SET(Leddevice_HEADERS ${CURRENT_SOURCE_DIR}/LedDeviceSedu.h ${CURRENT_SOURCE_DIR}/LedDeviceTest.h ${CURRENT_SOURCE_DIR}/LedDeviceHyperionUsbasp.h + ${CURRENT_SOURCE_DIR}/LedDevicePhilipsHue.h ) SET(Leddevice_SOURCES @@ -44,6 +45,7 @@ SET(Leddevice_SOURCES ${CURRENT_SOURCE_DIR}/LedDeviceSedu.cpp ${CURRENT_SOURCE_DIR}/LedDeviceTest.cpp ${CURRENT_SOURCE_DIR}/LedDeviceHyperionUsbasp.cpp + ${CURRENT_SOURCE_DIR}/LedDevicePhilipsHue.cpp ) if(ENABLE_SPIDEV) diff --git a/libsrc/leddevice/LedDeviceFactory.cpp b/libsrc/leddevice/LedDeviceFactory.cpp old mode 100644 new mode 100755 index b5bc6f5e..71aeda06 --- a/libsrc/leddevice/LedDeviceFactory.cpp +++ b/libsrc/leddevice/LedDeviceFactory.cpp @@ -28,6 +28,7 @@ #include "LedDeviceSedu.h" #include "LedDeviceTest.h" #include "LedDeviceHyperionUsbasp.h" +#include "LedDevicePhilipsHue.h" LedDevice * LedDeviceFactory::construct(const Json::Value & deviceConfig) { @@ -159,6 +160,11 @@ LedDevice * LedDeviceFactory::construct(const Json::Value & deviceConfig) deviceHyperionUsbasp->open(); device = deviceHyperionUsbasp; } + else if (type == "philipshue") + { + const std::string output = deviceConfig["output"].asString(); + device = new LedDevicePhilipsHue(output); + } else if (type == "test") { const std::string output = deviceConfig["output"].asString(); diff --git a/libsrc/leddevice/LedDevicePhilipsHue.cpp b/libsrc/leddevice/LedDevicePhilipsHue.cpp new file mode 100755 index 00000000..01334403 --- /dev/null +++ b/libsrc/leddevice/LedDevicePhilipsHue.cpp @@ -0,0 +1,81 @@ +// Local-Hyperion includes +#include "LedDevicePhilipsHue.h" + +#include + +#include +#include +#include +#include + +LedDevicePhilipsHue::LedDevicePhilipsHue(const std::string& output) : + host(output.c_str()), username("newdeveloper") { + http = new QHttp(host, 80); +} + +LedDevicePhilipsHue::~LedDevicePhilipsHue() { + delete http; +} + +int LedDevicePhilipsHue::write(const std::vector & ledValues) { + // Due to rate limiting (max. 30 request per seconds), discard new values if + // the previous request have not been completed yet. + if (http->hasPendingRequests()) { + return -1; + } + unsigned int lightId = 1; + for (const ColorRgb& color : ledValues) { + float x, y, b; + // Scale colors from [0, 255] to [0, 1] and convert to xy space. + rgbToXYBrightness(color.red / 255.0f, color.green / 255.0f, color.blue / 255.0f, x, y, b); + // Send adjust color command in JSON format. + put(getRoute(lightId), QString("{\"xy\": [%1, %2]}").arg(x).arg(y)); + // Send brightness color command in JSON format. + put(getRoute(lightId), QString("{\"bri\": %1}").arg(qRound(b * 255.0f))); + // Next light id. + lightId++; + } + return 0; +} + +int LedDevicePhilipsHue::switchOff() { + return 0; +} + +void LedDevicePhilipsHue::put(QString route, QString content) { + QString url = QString("/api/%1/%2").arg(username).arg(route); + QHttpRequestHeader header("PUT", url); + header.setValue("Host", host); + header.setValue("Accept-Encoding", "identity"); + header.setValue("Content-Length", QString("%1").arg(content.size())); + http->setHost(host); + http->request(header, content.toAscii()); + // std::cout << "LedDevicePhilipsHue::put(): " << header.toString().toUtf8().constData() << std::endl; + // std::cout << "LedDevicePhilipsHue::put(): " << content.toUtf8().constData() << std::endl; +} + +QString LedDevicePhilipsHue::getRoute(unsigned int lightId) { + return QString("lights/%1/state").arg(lightId); +} + +void LedDevicePhilipsHue::rgbToXYBrightness(float red, float green, float blue, float& x, float& y, float& brightness) { + // Apply gamma correction. + red = (red > 0.04045f) ? qPow((red + 0.055f) / (1.0f + 0.055f), 2.4f) : (red / 12.92f); + green = (green > 0.04045f) ? qPow ((green + 0.055f) / (1.0f + 0.055f), 2.4f) : (green / 12.92f); + blue = (blue > 0.04045f) ? qPow ((blue + 0.055f) / (1.0f + 0.055f), 2.4f) : (blue / 12.92f); + // Convert to XYZ space. + float X = red * 0.649926f + green * 0.103455f + blue * 0.197109f; + float Y = red * 0.234327f + green * 0.743075f + blue * 0.022598f; + float Z = red * 0.0000000f + green * 0.053077f + blue * 1.035763f; + // Convert to x,y space. + x = X / (X + Y + Z); + y = Y / (X + Y + Z); + if (isnan(x)) { + x = 0.0f; + } + if (isnan(y)) { + y = 0.0f; + } + // Brightness is simply Y in the XYZ space. + brightness = Y; +} diff --git a/libsrc/leddevice/LedDevicePhilipsHue.h b/libsrc/leddevice/LedDevicePhilipsHue.h new file mode 100755 index 00000000..e5710f40 --- /dev/null +++ b/libsrc/leddevice/LedDevicePhilipsHue.h @@ -0,0 +1,90 @@ +#pragma once + +// STL includes +#include + +// Qt includes +#include +#include + +// Leddevice includes +#include + +/** + * Implementation for the Philips Hue system. + * + * To use set the device to "philipshue". + * Uses the official Philips Hue API (http://developers.meethue.com). + * Framegrabber should be limited to 30 Hz / numer of lights to avoid rate limitation by the hue bridge. + * Create a new API user name "newdeveloper" on the bridge (http://developers.meethue.com/gettingstarted.html) + */ +class LedDevicePhilipsHue : public LedDevice +{ +public: + /// + /// Constructs the device. + /// + /// @param output the ip address of the bridge + /// + LedDevicePhilipsHue(const std::string& output); + + /// + /// Destructor of this device + /// + virtual ~LedDevicePhilipsHue(); + + /// + /// Sends the given led-color values via put request to the hue system + /// + /// @param ledValues The color-value per led + /// + /// @return Zero on success else negative + /// + virtual int write(const std::vector & ledValues); + + /// Switch the leds off + virtual int switchOff(); + +private: + /// Ip address of the bridge + QString host; + /// User name for the API ("newdeveloper") + QString username; + QHttp* http; + + /// + /// Sends a HTTP PUT request + /// + /// @param route the URI of the request + /// + /// @param content content of the request + /// + void put(QString route, QString content); + + /// + /// @param lightId the id of the hue light (starting from 1) + /// + /// @return the URI of the light + /// + QString getRoute(unsigned int lightId); + + /// + /// Converts an RGB color to the Hue xy color space and brightness + /// https://github.com/PhilipsHue/PhilipsHueSDK-iOS-OSX/blob/master/ApplicationDesignNotes/RGB%20to%20xy%20Color%20conversion.md + /// + /// @param red the red component in [0, 1] + /// + /// @param green the green component in [0, 1] + /// + /// @param blue the blue component in [0, 1] + /// + /// @param x converted x component + /// + /// @param y converted y component + /// + /// @param brightness converted brightness component + /// + void rgbToXYBrightness(float red, float green, float blue, + float& x, float& y, float& brightness); + +}; From ebb22cdc87799612bea0bdd2bf111250f0d03ce0 Mon Sep 17 00:00:00 2001 From: ntim Date: Sun, 27 Apr 2014 18:42:26 +0200 Subject: [PATCH 04/74] Removed rate limiting from code. Setting framegrabbing frequency of 10 Hz / number of lights is sufficient. Former-commit-id: 0686a8d18c780038eb017fdf26b5faf4b8053f3a --- libsrc/leddevice/LedDevicePhilipsHue.cpp | 19 ++++++------------- libsrc/leddevice/LedDevicePhilipsHue.h | 2 +- 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/libsrc/leddevice/LedDevicePhilipsHue.cpp b/libsrc/leddevice/LedDevicePhilipsHue.cpp index 01334403..11e99a21 100755 --- a/libsrc/leddevice/LedDevicePhilipsHue.cpp +++ b/libsrc/leddevice/LedDevicePhilipsHue.cpp @@ -18,11 +18,6 @@ LedDevicePhilipsHue::~LedDevicePhilipsHue() { } int LedDevicePhilipsHue::write(const std::vector & ledValues) { - // Due to rate limiting (max. 30 request per seconds), discard new values if - // the previous request have not been completed yet. - if (http->hasPendingRequests()) { - return -1; - } unsigned int lightId = 1; for (const ColorRgb& color : ledValues) { float x, y, b; @@ -50,8 +45,6 @@ void LedDevicePhilipsHue::put(QString route, QString content) { header.setValue("Content-Length", QString("%1").arg(content.size())); http->setHost(host); http->request(header, content.toAscii()); - // std::cout << "LedDevicePhilipsHue::put(): " << header.toString().toUtf8().constData() << std::endl; - // std::cout << "LedDevicePhilipsHue::put(): " << content.toUtf8().constData() << std::endl; } QString LedDevicePhilipsHue::getRoute(unsigned int lightId) { @@ -71,11 +64,11 @@ void LedDevicePhilipsHue::rgbToXYBrightness(float red, float green, float blue, x = X / (X + Y + Z); y = Y / (X + Y + Z); if (isnan(x)) { - x = 0.0f; - } - if (isnan(y)) { - y = 0.0f; - } - // Brightness is simply Y in the XYZ space. + x = 0.0f; + } + if (isnan(y)) { + y = 0.0f; + } + // Brightness is simply Y in the XYZ space. brightness = Y; } diff --git a/libsrc/leddevice/LedDevicePhilipsHue.h b/libsrc/leddevice/LedDevicePhilipsHue.h index e5710f40..a9155899 100755 --- a/libsrc/leddevice/LedDevicePhilipsHue.h +++ b/libsrc/leddevice/LedDevicePhilipsHue.h @@ -15,7 +15,7 @@ * * To use set the device to "philipshue". * Uses the official Philips Hue API (http://developers.meethue.com). - * Framegrabber should be limited to 30 Hz / numer of lights to avoid rate limitation by the hue bridge. + * Framegrabber must be limited to 10 Hz / numer of lights to avoid rate limitation by the hue bridge. * Create a new API user name "newdeveloper" on the bridge (http://developers.meethue.com/gettingstarted.html) */ class LedDevicePhilipsHue : public LedDevice From ab5e17e1057324955260060a01e13a86246ba68d Mon Sep 17 00:00:00 2001 From: Tim Niggemann Date: Mon, 28 Apr 2014 14:32:37 +0200 Subject: [PATCH 05/74] State of all lights is saved and restored on switchOff(). Former-commit-id: 1ee26e8c01d90456424c1b5ea3f113dfd0ff6525 --- libsrc/leddevice/LedDevicePhilipsHue.cpp | 83 +++++++++++++++++++++--- libsrc/leddevice/LedDevicePhilipsHue.h | 46 +++++++++++-- 2 files changed, 113 insertions(+), 16 deletions(-) diff --git a/libsrc/leddevice/LedDevicePhilipsHue.cpp b/libsrc/leddevice/LedDevicePhilipsHue.cpp index 11e99a21..690a7b94 100755 --- a/libsrc/leddevice/LedDevicePhilipsHue.cpp +++ b/libsrc/leddevice/LedDevicePhilipsHue.cpp @@ -1,16 +1,18 @@ // Local-Hyperion includes #include "LedDevicePhilipsHue.h" -#include +// jsoncpp includes +#include +// qt includes #include #include #include -#include +#include LedDevicePhilipsHue::LedDevicePhilipsHue(const std::string& output) : - host(output.c_str()), username("newdeveloper") { - http = new QHttp(host, 80); + host(output.c_str()), username("newdeveloper") { + http = new QHttp(host); } LedDevicePhilipsHue::~LedDevicePhilipsHue() { @@ -18,15 +20,20 @@ LedDevicePhilipsHue::~LedDevicePhilipsHue() { } int LedDevicePhilipsHue::write(const std::vector & ledValues) { + // Save light states if not done before. + if (!statesSaved()) { + saveStates(ledValues.size()); + } + // Iterate through colors and set light states. unsigned int lightId = 1; for (const ColorRgb& color : ledValues) { float x, y, b; // Scale colors from [0, 255] to [0, 1] and convert to xy space. rgbToXYBrightness(color.red / 255.0f, color.green / 255.0f, color.blue / 255.0f, x, y, b); // Send adjust color command in JSON format. - put(getRoute(lightId), QString("{\"xy\": [%1, %2]}").arg(x).arg(y)); + put(getStateRoute(lightId), QString("{\"xy\": [%1, %2]}").arg(x).arg(y)); // Send brightness color command in JSON format. - put(getRoute(lightId), QString("{\"bri\": %1}").arg(qRound(b * 255.0f))); + put(getStateRoute(lightId), QString("{\"bri\": %1}").arg(qRound(b * 255.0f))); // Next light id. lightId++; } @@ -34,6 +41,11 @@ int LedDevicePhilipsHue::write(const std::vector & ledValues) { } int LedDevicePhilipsHue::switchOff() { + // If light states have been saved before, ... + if (statesSaved()) { + // ... restore them. + restoreStates(); + } return 0; } @@ -47,15 +59,68 @@ void LedDevicePhilipsHue::put(QString route, QString content) { http->request(header, content.toAscii()); } -QString LedDevicePhilipsHue::getRoute(unsigned int lightId) { +QByteArray LedDevicePhilipsHue::get(QString route) { + QString url = QString("/api/%1/%2").arg(username).arg(route); + // Event loop to block until request finished. + QEventLoop loop; + // Connect requestFinished signal to quit slot of the loop. + loop.connect(http, SIGNAL(requestFinished(int, bool)), SLOT(quit())); + // Perfrom request + http->get(url); + // Go into the loop until the request is finished. + loop.exec(); + // Read all data of the response. + return http->readAll(); +} + +QString LedDevicePhilipsHue::getStateRoute(unsigned int lightId) { return QString("lights/%1/state").arg(lightId); } +QString LedDevicePhilipsHue::getRoute(unsigned int lightId) { + return QString("lights/%1").arg(lightId); +} + +void LedDevicePhilipsHue::saveStates(unsigned int nLights) { + // Clear saved light states. + states.clear(); + // Use json parser to parse reponse. + Json::Reader reader; + Json::FastWriter writer; + // Iterate lights. + for (unsigned int i = 0; i < nLights; i++) { + // Read the response. + QByteArray response = get(getRoute(i + 1)); + // Parse JSON. + Json::Value state; + if (!reader.parse(QString(response).toStdString(), state)) { + // Error occured, break loop. + break; + } + // Save state object. + states.push_back(QString(writer.write(state["state"]).c_str())); + } +} + +void LedDevicePhilipsHue::restoreStates() { + unsigned int lightId = 1; + for (QString state : states) { + put(getStateRoute(lightId), state); + lightId++; + } + // Clear saved light states. + states.clear(); +} + +bool LedDevicePhilipsHue::statesSaved() { + return !states.empty(); +} + void LedDevicePhilipsHue::rgbToXYBrightness(float red, float green, float blue, float& x, float& y, float& brightness) { // Apply gamma correction. red = (red > 0.04045f) ? qPow((red + 0.055f) / (1.0f + 0.055f), 2.4f) : (red / 12.92f); - green = (green > 0.04045f) ? qPow ((green + 0.055f) / (1.0f + 0.055f), 2.4f) : (green / 12.92f); - blue = (blue > 0.04045f) ? qPow ((blue + 0.055f) / (1.0f + 0.055f), 2.4f) : (blue / 12.92f); + green = (green > 0.04045f) ? qPow((green + 0.055f) / (1.0f + 0.055f), 2.4f) : (green / 12.92f); + blue = (blue > 0.04045f) ? qPow((blue + 0.055f) / (1.0f + 0.055f), 2.4f) : (blue / 12.92f); // Convert to XYZ space. float X = red * 0.649926f + green * 0.103455f + blue * 0.197109f; float Y = red * 0.234327f + green * 0.743075f + blue * 0.022598f; diff --git a/libsrc/leddevice/LedDevicePhilipsHue.h b/libsrc/leddevice/LedDevicePhilipsHue.h index a9155899..0bb71182 100755 --- a/libsrc/leddevice/LedDevicePhilipsHue.h +++ b/libsrc/leddevice/LedDevicePhilipsHue.h @@ -9,7 +9,7 @@ // Leddevice includes #include - + /** * Implementation for the Philips Hue system. * @@ -18,8 +18,7 @@ * Framegrabber must be limited to 10 Hz / numer of lights to avoid rate limitation by the hue bridge. * Create a new API user name "newdeveloper" on the bridge (http://developers.meethue.com/gettingstarted.html) */ -class LedDevicePhilipsHue : public LedDevice -{ +class LedDevicePhilipsHue: public LedDevice { public: /// /// Constructs the device. @@ -46,14 +45,26 @@ public: virtual int switchOff(); private: + /// Array to save the light states. + std::vector states; /// Ip address of the bridge QString host; /// User name for the API ("newdeveloper") QString username; + /// Qhttp object for sending requests. QHttp* http; /// - /// Sends a HTTP PUT request + /// Sends a HTTP GET request (blocking). + /// + /// @param route the URI of the request + /// + /// @return response of the request + /// + QByteArray get(QString route); + + /// + /// Sends a HTTP PUT request (non-blocking). /// /// @param route the URI of the request /// @@ -64,10 +75,32 @@ private: /// /// @param lightId the id of the hue light (starting from 1) /// - /// @return the URI of the light + /// @return the URI of the light state for PUT requests. + /// + QString getStateRoute(unsigned int lightId); + + /// + /// @param lightId the id of the hue light (starting from 1) + /// + /// @return the URI of the light for GET requests. /// QString getRoute(unsigned int lightId); + /// + /// Queries the status of all lights and saves it. + /// + /// @param nLights the number of lights + /// + void saveStates(unsigned int nLights); + + /// Restores the status of all lights. + void restoreStates(); + + /// + /// @return true if light states have been saved. + /// + bool statesSaved(); + /// /// Converts an RGB color to the Hue xy color space and brightness /// https://github.com/PhilipsHue/PhilipsHueSDK-iOS-OSX/blob/master/ApplicationDesignNotes/RGB%20to%20xy%20Color%20conversion.md @@ -84,7 +117,6 @@ private: /// /// @param brightness converted brightness component /// - void rgbToXYBrightness(float red, float green, float blue, - float& x, float& y, float& brightness); + void rgbToXYBrightness(float red, float green, float blue, float& x, float& y, float& brightness); }; From 1e66045d3ed27fd5480de04e4391bd259d0ee264 Mon Sep 17 00:00:00 2001 From: johan Date: Wed, 30 Apr 2014 22:38:59 +0200 Subject: [PATCH 06/74] Added author tag for Philps Hue device Former-commit-id: 6fc5dc0ac7177ddd23e601c8ca1f65981ff0e778 --- libsrc/leddevice/LedDevicePhilipsHue.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libsrc/leddevice/LedDevicePhilipsHue.h b/libsrc/leddevice/LedDevicePhilipsHue.h index 0bb71182..2152f625 100755 --- a/libsrc/leddevice/LedDevicePhilipsHue.h +++ b/libsrc/leddevice/LedDevicePhilipsHue.h @@ -12,11 +12,13 @@ /** * Implementation for the Philips Hue system. - * + * * To use set the device to "philipshue". * Uses the official Philips Hue API (http://developers.meethue.com). * Framegrabber must be limited to 10 Hz / numer of lights to avoid rate limitation by the hue bridge. * Create a new API user name "newdeveloper" on the bridge (http://developers.meethue.com/gettingstarted.html) + * + * @author ntim (github) */ class LedDevicePhilipsHue: public LedDevice { public: From 8d75a57f18a1ffae1781c11b134e96c59fe910ea Mon Sep 17 00:00:00 2001 From: johan Date: Wed, 30 Apr 2014 22:46:26 +0200 Subject: [PATCH 07/74] Cleanup of XBMC 3D checker code Former-commit-id: 26acf7dceb3e26a2e59af82736dec9fdf09c26d5 --- include/xbmcvideochecker/XBMCVideoChecker.h | 3 ++ libsrc/xbmcvideochecker/XBMCVideoChecker.cpp | 29 ++++++++++---------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/include/xbmcvideochecker/XBMCVideoChecker.h b/include/xbmcvideochecker/XBMCVideoChecker.h index d016b0d4..01401093 100644 --- a/include/xbmcvideochecker/XBMCVideoChecker.h +++ b/include/xbmcvideochecker/XBMCVideoChecker.h @@ -131,4 +131,7 @@ private: /// Previous emitted video mode VideoMode _previousVideoMode; + + /// XBMC version number + int _xbmcVersion; }; diff --git a/libsrc/xbmcvideochecker/XBMCVideoChecker.cpp b/libsrc/xbmcvideochecker/XBMCVideoChecker.cpp index ad7843b2..a540c08b 100644 --- a/libsrc/xbmcvideochecker/XBMCVideoChecker.cpp +++ b/libsrc/xbmcvideochecker/XBMCVideoChecker.cpp @@ -18,10 +18,8 @@ // {"id":668,"jsonrpc":"2.0","result":{"System.ScreenSaverActive":false}} // Request stereoscopicmode example: -// {"jsonrpc":"2.0","method":"GUI.GetProperties","params":{"properties":["stereoscopicmode"]},"id":1} -// {"id":1,"jsonrpc":"2.0","result":{"stereoscopicmode":{"label":"Nebeneinander","mode":"split_vertical"}}} - -int xbmcVersion = 0; +// {"jsonrpc":"2.0","method":"GUI.GetProperties","params":{"properties":["stereoscopicmode"]},"id":669} +// {"id":669,"jsonrpc":"2.0","result":{"stereoscopicmode":{"label":"Nebeneinander","mode":"split_vertical"}}} XBMCVideoChecker::XBMCVideoChecker(const std::string & address, uint16_t port, bool grabVideo, bool grabPhoto, bool grabAudio, bool grabMenu, bool grabScreensaver, bool enable3DDetection) : QObject(), @@ -30,8 +28,8 @@ XBMCVideoChecker::XBMCVideoChecker(const std::string & address, uint16_t port, b _activePlayerRequest(R"({"id":666,"jsonrpc":"2.0","method":"Player.GetActivePlayers"})"), _currentPlayingItemRequest(R"({"id":667,"jsonrpc":"2.0","method":"Player.GetItem","params":{"playerid":%1,"properties":["file"]}})"), _checkScreensaverRequest(R"({"id":668,"jsonrpc":"2.0","method":"XBMC.GetInfoBooleans","params":{"booleans":["System.ScreenSaverActive"]}})"), - _getStereoscopicMode(R"({"jsonrpc":"2.0","method":"GUI.GetProperties","params":{"properties":["stereoscopicmode"]},"id":1})"), - _getXbmcVersion(R"({"jsonrpc":"2.0","method":"Application.GetProperties","params":{"properties":["version"]},"id":1})"), + _getStereoscopicMode(R"({"jsonrpc":"2.0","method":"GUI.GetProperties","params":{"properties":["stereoscopicmode"]},"id":669})"), + _getXbmcVersion(R"({"jsonrpc":"2.0","method":"Application.GetProperties","params":{"properties":["version"]},"id":670})"), _socket(), _grabVideo(grabVideo), _grabPhoto(grabPhoto), @@ -41,7 +39,8 @@ XBMCVideoChecker::XBMCVideoChecker(const std::string & address, uint16_t port, b _enable3DDetection(enable3DDetection), _previousScreensaverMode(false), _previousGrabbingMode(GRABBINGMODE_INVALID), - _previousVideoMode(VIDEO_2D) + _previousVideoMode(VIDEO_2D), + _xbmcVersion(0) { // setup socket connect(&_socket, SIGNAL(readyRead()), this, SLOT(receiveReply())); @@ -124,10 +123,10 @@ void XBMCVideoChecker::receiveReply() } else if (reply.contains("\"id\":667")) { - if (xbmcVersion >= 13) + if (_xbmcVersion >= 13) { - // check of active stereoscopicmode - _socket.write(_getStereoscopicMode.toUtf8()); + // check of active stereoscopicmode + _socket.write(_getStereoscopicMode.toUtf8()); } else { @@ -162,13 +161,13 @@ void XBMCVideoChecker::receiveReply() // check here xbmc version if (_socket.state() == QTcpSocket::ConnectedState) { - if (xbmcVersion == 0) - { + if (_xbmcVersion == 0) + { _socket.write(_getXbmcVersion.toUtf8()); } } } - else if (reply.contains("\"stereoscopicmode\"")) + else if (reply.contains("\"id\":669")) { QRegExp regex("\"mode\":\"(split_vertical|split_horizontal)\""); int pos = regex.indexIn(reply); @@ -185,13 +184,13 @@ void XBMCVideoChecker::receiveReply() } } } - else if (reply.contains("\"version\":")) + else if (reply.contains("\"id\":670")) { QRegExp regex("\"major\":(\\d+)"); int pos = regex.indexIn(reply); if (pos > 0) { - xbmcVersion = regex.cap(1).toInt(); + _xbmcVersion = regex.cap(1).toInt(); } } } From d5597d55a778d21e6d57f1193219dc4c3a99864c Mon Sep 17 00:00:00 2001 From: johan Date: Wed, 30 Apr 2014 22:53:05 +0200 Subject: [PATCH 08/74] Disable the blackborder detector for effects Former-commit-id: 2d4660f48c17977aabff52b7cbbc8d832b216f00 --- include/hyperion/ImageProcessor.h | 5 ++++- libsrc/effectengine/Effect.cpp | 3 +++ libsrc/hyperion/ImageProcessor.cpp | 5 +++++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/include/hyperion/ImageProcessor.h b/include/hyperion/ImageProcessor.h index deb40df1..5049ac03 100644 --- a/include/hyperion/ImageProcessor.h +++ b/include/hyperion/ImageProcessor.h @@ -37,6 +37,9 @@ public: /// void setSize(const unsigned width, const unsigned height); + /// Enable or disable the black border detector + void enableBalckBorderDetector(bool enable); + /// /// Processes the image to a list of led colors. This will update the size of the buffer-image /// if required and call the image-to-leds mapping to determine the mean color per led. @@ -142,7 +145,7 @@ private: const LedString _ledString; /// Flag the enables(true)/disabled(false) blackborder detector - const bool _enableBlackBorderRemoval; + bool _enableBlackBorderRemoval; /// The processor for black border detection hyperion::BlackBorderProcessor * _borderProcessor; diff --git a/libsrc/effectengine/Effect.cpp b/libsrc/effectengine/Effect.cpp index 09411c87..72abbf3c 100644 --- a/libsrc/effectengine/Effect.cpp +++ b/libsrc/effectengine/Effect.cpp @@ -64,6 +64,9 @@ Effect::Effect(PyThreadState * mainThreadState, int priority, int timeout, const { _colors.resize(_imageProcessor->getLedCount(), ColorRgb::BLACK); + // disable the black border detector for effects + _imageProcessor->enableBalckBorderDetector(false); + // connect the finished signal connect(this, SIGNAL(finished()), this, SLOT(effectFinished())); } diff --git a/libsrc/hyperion/ImageProcessor.cpp b/libsrc/hyperion/ImageProcessor.cpp index 66b02e74..25784d0a 100644 --- a/libsrc/hyperion/ImageProcessor.cpp +++ b/libsrc/hyperion/ImageProcessor.cpp @@ -43,6 +43,11 @@ void ImageProcessor::setSize(const unsigned width, const unsigned height) _imageToLeds = new ImageToLedsMap(width, height, 0, 0, _ledString.leds()); } +void ImageProcessor::enableBalckBorderDetector(bool enable) +{ + _enableBlackBorderRemoval = enable; +} + bool ImageProcessor::getScanParameters(size_t led, double &hscanBegin, double &hscanEnd, double &vscanBegin, double &vscanEnd) const { if (led < _ledString.leds().size()) From 8c1f14f8dc3c0d12b0773f06fa133690b3ea5a10 Mon Sep 17 00:00:00 2001 From: johan Date: Wed, 30 Apr 2014 22:55:39 +0200 Subject: [PATCH 09/74] Update binaries for RPi Former-commit-id: dadd309b43dd982d8ddb1452b9e3c5d656391246 --- deploy/hyperion.tar.gz.REMOVED.git-id | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy/hyperion.tar.gz.REMOVED.git-id b/deploy/hyperion.tar.gz.REMOVED.git-id index a3a391be..a48b195a 100644 --- a/deploy/hyperion.tar.gz.REMOVED.git-id +++ b/deploy/hyperion.tar.gz.REMOVED.git-id @@ -1 +1 @@ -d61b685eca164580cb39eb5bc3cf65b89afad410 \ No newline at end of file +e80ba1bef0cc5983a767b293f0c5915c2535a47f \ No newline at end of file From 9220c346bbd8a0a6c95626edc89771e85420e21b Mon Sep 17 00:00:00 2001 From: ntim Date: Thu, 1 May 2014 00:02:10 +0200 Subject: [PATCH 10/74] Blocking PUT requests. Only save the brightness and [x,y] state of the lights. Former-commit-id: 5887fdf1b350172dcdd52c46bd8da74dab521444 --- libsrc/leddevice/LedDevicePhilipsHue.cpp | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/libsrc/leddevice/LedDevicePhilipsHue.cpp b/libsrc/leddevice/LedDevicePhilipsHue.cpp index 690a7b94..2cedc8a5 100755 --- a/libsrc/leddevice/LedDevicePhilipsHue.cpp +++ b/libsrc/leddevice/LedDevicePhilipsHue.cpp @@ -54,9 +54,15 @@ void LedDevicePhilipsHue::put(QString route, QString content) { QHttpRequestHeader header("PUT", url); header.setValue("Host", host); header.setValue("Accept-Encoding", "identity"); + header.setValue("Connection", "keep-alive"); header.setValue("Content-Length", QString("%1").arg(content.size())); - http->setHost(host); + QEventLoop loop; + // Connect requestFinished signal to quit slot of the loop. + loop.connect(http, SIGNAL(requestFinished(int, bool)), SLOT(quit())); + // Perfrom request http->request(header, content.toAscii()); + // Go into the loop until the request is finished. + loop.exec(); } QByteArray LedDevicePhilipsHue::get(QString route) { @@ -92,13 +98,17 @@ void LedDevicePhilipsHue::saveStates(unsigned int nLights) { // Read the response. QByteArray response = get(getRoute(i + 1)); // Parse JSON. - Json::Value state; - if (!reader.parse(QString(response).toStdString(), state)) { + Json::Value json; + if (!reader.parse(QString(response).toStdString(), json)) { // Error occured, break loop. break; } + // Save state object values which are subject to change. + Json::Value state(Json::objectValue); + state["xy"] = json["state"]["xy"]; + state["bri"] = json["state"]["bri"]; // Save state object. - states.push_back(QString(writer.write(state["state"]).c_str())); + states.push_back(QString(writer.write(state).c_str()).trimmed()); } } From 8cca280fdcc1384567a7162a611c46127e9fa953 Mon Sep 17 00:00:00 2001 From: ntim Date: Sat, 3 May 2014 10:12:41 +0200 Subject: [PATCH 11/74] Implemented timeout for restoring original state. Former-commit-id: 925b063aba1a859b592ead9b75ce67d90fb81e28 --- libsrc/leddevice/CMakeLists.txt | 2 +- libsrc/leddevice/LedDevicePhilipsHue.cpp | 5 +++++ libsrc/leddevice/LedDevicePhilipsHue.h | 14 ++++++++++---- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/libsrc/leddevice/CMakeLists.txt b/libsrc/leddevice/CMakeLists.txt index 098ac38c..5a5d2c12 100755 --- a/libsrc/leddevice/CMakeLists.txt +++ b/libsrc/leddevice/CMakeLists.txt @@ -14,6 +14,7 @@ include_directories( # Group the headers that go through the MOC compiler SET(Leddevice_QT_HEADERS ${CURRENT_SOURCE_DIR}/LedDeviceAdalight.h + ${CURRENT_SOURCE_DIR}/LedDevicePhilipsHue.h ) SET(Leddevice_HEADERS @@ -29,7 +30,6 @@ SET(Leddevice_HEADERS ${CURRENT_SOURCE_DIR}/LedDeviceSedu.h ${CURRENT_SOURCE_DIR}/LedDeviceTest.h ${CURRENT_SOURCE_DIR}/LedDeviceHyperionUsbasp.h - ${CURRENT_SOURCE_DIR}/LedDevicePhilipsHue.h ) SET(Leddevice_SOURCES diff --git a/libsrc/leddevice/LedDevicePhilipsHue.cpp b/libsrc/leddevice/LedDevicePhilipsHue.cpp index 2cedc8a5..35f045f2 100755 --- a/libsrc/leddevice/LedDevicePhilipsHue.cpp +++ b/libsrc/leddevice/LedDevicePhilipsHue.cpp @@ -13,6 +13,9 @@ LedDevicePhilipsHue::LedDevicePhilipsHue(const std::string& output) : host(output.c_str()), username("newdeveloper") { http = new QHttp(host); + timer.setInterval(1000); + timer.setSingleShot(true); + connect(&timer, SIGNAL(timeout()), this, SLOT(restoreStates()())); } LedDevicePhilipsHue::~LedDevicePhilipsHue() { @@ -37,10 +40,12 @@ int LedDevicePhilipsHue::write(const std::vector & ledValues) { // Next light id. lightId++; } + timer.start(); return 0; } int LedDevicePhilipsHue::switchOff() { + timer.stop(); // If light states have been saved before, ... if (statesSaved()) { // ... restore them. diff --git a/libsrc/leddevice/LedDevicePhilipsHue.h b/libsrc/leddevice/LedDevicePhilipsHue.h index 0bb71182..7a0e86cd 100755 --- a/libsrc/leddevice/LedDevicePhilipsHue.h +++ b/libsrc/leddevice/LedDevicePhilipsHue.h @@ -4,8 +4,10 @@ #include // Qt includes +#include #include #include +#include // Leddevice includes #include @@ -18,7 +20,8 @@ * Framegrabber must be limited to 10 Hz / numer of lights to avoid rate limitation by the hue bridge. * Create a new API user name "newdeveloper" on the bridge (http://developers.meethue.com/gettingstarted.html) */ -class LedDevicePhilipsHue: public LedDevice { +class LedDevicePhilipsHue: public QObject, public LedDevice { +Q_OBJECT public: /// /// Constructs the device. @@ -44,6 +47,10 @@ public: /// Switch the leds off virtual int switchOff(); +private slots: + /// Restores the status of all lights. + void restoreStates(); + private: /// Array to save the light states. std::vector states; @@ -53,6 +60,8 @@ private: QString username; /// Qhttp object for sending requests. QHttp* http; + /// Use timer to reset lights when we got into "GRABBINGMODE_OFF". + QTimer timer; /// /// Sends a HTTP GET request (blocking). @@ -93,9 +102,6 @@ private: /// void saveStates(unsigned int nLights); - /// Restores the status of all lights. - void restoreStates(); - /// /// @return true if light states have been saved. /// From 3eb29146dd26568de7661d361e21c9a41e165a35 Mon Sep 17 00:00:00 2001 From: johan Date: Sun, 4 May 2014 11:31:13 +0200 Subject: [PATCH 12/74] Added a possible delay after connecting an Adalight device Former-commit-id: 756fae2ebc57455bf6360dc96bf2ae5469460172 --- deploy/hyperion.tar.gz.REMOVED.git-id | 2 +- include/utils/Sleep.h | 10 ++++++++++ libsrc/leddevice/LedDeviceAdalight.cpp | 4 ++-- libsrc/leddevice/LedDeviceAdalight.h | 2 +- libsrc/leddevice/LedDeviceFactory.cpp | 3 ++- libsrc/leddevice/LedRs232Device.cpp | 19 +++++++++++++------ libsrc/leddevice/LedRs232Device.h | 10 +++++++--- libsrc/utils/CMakeLists.txt | 1 + 8 files changed, 37 insertions(+), 14 deletions(-) create mode 100644 include/utils/Sleep.h diff --git a/deploy/hyperion.tar.gz.REMOVED.git-id b/deploy/hyperion.tar.gz.REMOVED.git-id index a48b195a..288cb926 100644 --- a/deploy/hyperion.tar.gz.REMOVED.git-id +++ b/deploy/hyperion.tar.gz.REMOVED.git-id @@ -1 +1 @@ -e80ba1bef0cc5983a767b293f0c5915c2535a47f \ No newline at end of file +ab3338d1164469e008bd9635c817659b6e528a1f \ No newline at end of file diff --git a/include/utils/Sleep.h b/include/utils/Sleep.h new file mode 100644 index 00000000..a2c55815 --- /dev/null +++ b/include/utils/Sleep.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +class Sleep : protected QThread { +public: + static inline void msleep(unsigned long msecs) { + QThread::msleep(msecs); + } +}; diff --git a/libsrc/leddevice/LedDeviceAdalight.cpp b/libsrc/leddevice/LedDeviceAdalight.cpp index f19bb4be..47c09278 100644 --- a/libsrc/leddevice/LedDeviceAdalight.cpp +++ b/libsrc/leddevice/LedDeviceAdalight.cpp @@ -11,8 +11,8 @@ // hyperion local includes #include "LedDeviceAdalight.h" -LedDeviceAdalight::LedDeviceAdalight(const std::string& outputDevice, const unsigned baudrate) : - LedRs232Device(outputDevice, baudrate), +LedDeviceAdalight::LedDeviceAdalight(const std::string& outputDevice, const unsigned baudrate, int delayAfterConnect_ms) : + LedRs232Device(outputDevice, baudrate, delayAfterConnect_ms), _ledBuffer(0), _timer() { diff --git a/libsrc/leddevice/LedDeviceAdalight.h b/libsrc/leddevice/LedDeviceAdalight.h index 30e7bfb7..3621c5ca 100644 --- a/libsrc/leddevice/LedDeviceAdalight.h +++ b/libsrc/leddevice/LedDeviceAdalight.h @@ -23,7 +23,7 @@ public: /// @param outputDevice The name of the output device (eg '/dev/ttyS0') /// @param baudrate The used baudrate for writing to the output device /// - LedDeviceAdalight(const std::string& outputDevice, const unsigned baudrate); + LedDeviceAdalight(const std::string& outputDevice, const unsigned baudrate, int delayAfterConnect_ms); /// /// Writes the led color values to the led-device diff --git a/libsrc/leddevice/LedDeviceFactory.cpp b/libsrc/leddevice/LedDeviceFactory.cpp index 71aeda06..76eb0137 100755 --- a/libsrc/leddevice/LedDeviceFactory.cpp +++ b/libsrc/leddevice/LedDeviceFactory.cpp @@ -43,8 +43,9 @@ LedDevice * LedDeviceFactory::construct(const Json::Value & deviceConfig) { const std::string output = deviceConfig["output"].asString(); const unsigned rate = deviceConfig["rate"].asInt(); + const int delay_ms = deviceConfig["delayAfterConnect"].asInt(); - LedDeviceAdalight* deviceAdalight = new LedDeviceAdalight(output, rate); + LedDeviceAdalight* deviceAdalight = new LedDeviceAdalight(output, rate, delay_ms); deviceAdalight->open(); device = deviceAdalight; diff --git a/libsrc/leddevice/LedRs232Device.cpp b/libsrc/leddevice/LedRs232Device.cpp index 6bc3560b..c8c185a4 100644 --- a/libsrc/leddevice/LedRs232Device.cpp +++ b/libsrc/leddevice/LedRs232Device.cpp @@ -9,11 +9,13 @@ // Local Hyperion includes #include "LedRs232Device.h" +#include "utils/Sleep.h" -LedRs232Device::LedRs232Device(const std::string& outputDevice, const unsigned baudrate) : - mDeviceName(outputDevice), - mBaudRate_Hz(baudrate), +LedRs232Device::LedRs232Device(const std::string& outputDevice, const unsigned baudrate, int delayAfterConnect_ms) : + _deviceName(outputDevice), + _baudRate_Hz(baudrate), + _delayAfterConnect_ms(delayAfterConnect_ms), _rs232Port() { // empty @@ -31,10 +33,15 @@ int LedRs232Device::open() { try { - std::cout << "Opening UART: " << mDeviceName << std::endl; - _rs232Port.setPort(mDeviceName); - _rs232Port.setBaudrate(mBaudRate_Hz); + std::cout << "Opening UART: " << _deviceName << std::endl; + _rs232Port.setPort(_deviceName); + _rs232Port.setBaudrate(_baudRate_Hz); _rs232Port.open(); + + if (_delayAfterConnect_ms > 0) + { + Sleep::msleep(_delayAfterConnect_ms); + } } catch (const std::exception& e) { diff --git a/libsrc/leddevice/LedRs232Device.h b/libsrc/leddevice/LedRs232Device.h index 856a80fe..f8154581 100644 --- a/libsrc/leddevice/LedRs232Device.h +++ b/libsrc/leddevice/LedRs232Device.h @@ -18,7 +18,7 @@ public: /// @param[in] outputDevice The name of the output device (eg '/etc/ttyS0') /// @param[in] baudrate The used baudrate for writing to the output device /// - LedRs232Device(const std::string& outputDevice, const unsigned baudrate); + LedRs232Device(const std::string& outputDevice, const unsigned baudrate, int delayAfterConnect_ms = 0); /// /// Destructor of the LedDevice; closes the output device if it is open @@ -45,9 +45,13 @@ protected: private: /// The name of the output device - const std::string mDeviceName; + const std::string _deviceName; + /// The used baudrate of the output device - const int mBaudRate_Hz; + const int _baudRate_Hz; + + /// Sleep after the connect before continuing + const int _delayAfterConnect_ms; /// The RS232 serial-device serial::Serial _rs232Port; diff --git a/libsrc/utils/CMakeLists.txt b/libsrc/utils/CMakeLists.txt index 52094a61..b1f0709e 100644 --- a/libsrc/utils/CMakeLists.txt +++ b/libsrc/utils/CMakeLists.txt @@ -11,6 +11,7 @@ add_library(hyperion-utils ${CURRENT_HEADER_DIR}/ColorRgba.h ${CURRENT_SOURCE_DIR}/ColorRgba.cpp ${CURRENT_HEADER_DIR}/Image.h + ${CURRENT_HEADER_DIR}/Sleep.h ${CURRENT_HEADER_DIR}/HsvTransform.h ${CURRENT_SOURCE_DIR}/HsvTransform.cpp From 1e2160279876ee001dcdfe0c674dbbcf3e53ce1f Mon Sep 17 00:00:00 2001 From: johan Date: Sun, 4 May 2014 11:41:55 +0200 Subject: [PATCH 13/74] Changed connection delay to something non-blocking Former-commit-id: b313005a29cb42eb6839060bc4c48e4cbd927d3b --- deploy/hyperion.tar.gz.REMOVED.git-id | 2 +- libsrc/leddevice/CMakeLists.txt | 3 +-- libsrc/leddevice/LedDeviceAdalight.h | 2 +- libsrc/leddevice/LedRs232Device.cpp | 23 +++++++++++++++++++---- libsrc/leddevice/LedRs232Device.h | 12 +++++++++++- 5 files changed, 33 insertions(+), 9 deletions(-) diff --git a/deploy/hyperion.tar.gz.REMOVED.git-id b/deploy/hyperion.tar.gz.REMOVED.git-id index 288cb926..a76f2a2a 100644 --- a/deploy/hyperion.tar.gz.REMOVED.git-id +++ b/deploy/hyperion.tar.gz.REMOVED.git-id @@ -1 +1 @@ -ab3338d1164469e008bd9635c817659b6e528a1f \ No newline at end of file +e7867d56a269136c5c795a55b831dca58e962139 \ No newline at end of file diff --git a/libsrc/leddevice/CMakeLists.txt b/libsrc/leddevice/CMakeLists.txt index 098ac38c..ec1ee381 100755 --- a/libsrc/leddevice/CMakeLists.txt +++ b/libsrc/leddevice/CMakeLists.txt @@ -13,6 +13,7 @@ include_directories( # Group the headers that go through the MOC compiler SET(Leddevice_QT_HEADERS + ${CURRENT_SOURCE_DIR}/LedRs232Device.h ${CURRENT_SOURCE_DIR}/LedDeviceAdalight.h ) @@ -20,8 +21,6 @@ SET(Leddevice_HEADERS ${CURRENT_HEADER_DIR}/LedDevice.h ${CURRENT_HEADER_DIR}/LedDeviceFactory.h - ${CURRENT_SOURCE_DIR}/LedRs232Device.h - ${CURRENT_SOURCE_DIR}/LedDeviceLightpack.h ${CURRENT_SOURCE_DIR}/LedDeviceMultiLightpack.h ${CURRENT_SOURCE_DIR}/LedDevicePaintpack.h diff --git a/libsrc/leddevice/LedDeviceAdalight.h b/libsrc/leddevice/LedDeviceAdalight.h index 3621c5ca..bc08200b 100644 --- a/libsrc/leddevice/LedDeviceAdalight.h +++ b/libsrc/leddevice/LedDeviceAdalight.h @@ -12,7 +12,7 @@ /// /// Implementation of the LedDevice interface for writing to an Adalight led device. /// -class LedDeviceAdalight : public QObject, public LedRs232Device +class LedDeviceAdalight : public LedRs232Device { Q_OBJECT diff --git a/libsrc/leddevice/LedRs232Device.cpp b/libsrc/leddevice/LedRs232Device.cpp index c8c185a4..a4b058d2 100644 --- a/libsrc/leddevice/LedRs232Device.cpp +++ b/libsrc/leddevice/LedRs232Device.cpp @@ -4,19 +4,21 @@ #include #include +// Qt includes +#include + // Serial includes #include // Local Hyperion includes #include "LedRs232Device.h" -#include "utils/Sleep.h" - LedRs232Device::LedRs232Device(const std::string& outputDevice, const unsigned baudrate, int delayAfterConnect_ms) : _deviceName(outputDevice), _baudRate_Hz(baudrate), _delayAfterConnect_ms(delayAfterConnect_ms), - _rs232Port() + _rs232Port(), + _blockedForDelay(false) { // empty } @@ -40,7 +42,9 @@ int LedRs232Device::open() if (_delayAfterConnect_ms > 0) { - Sleep::msleep(_delayAfterConnect_ms); + _blockedForDelay = true; + QTimer::singleShot(_delayAfterConnect_ms, this, SLOT(unblockAfterDelay())); + std::cout << "Device blocked for " << _delayAfterConnect_ms << " ms" << std::endl; } } catch (const std::exception& e) @@ -54,6 +58,11 @@ int LedRs232Device::open() int LedRs232Device::writeBytes(const unsigned size, const uint8_t * data) { + if (_blockedForDelay) + { + return 0; + } + if (!_rs232Port.isOpen()) { return -1; @@ -102,3 +111,9 @@ int LedRs232Device::writeBytes(const unsigned size, const uint8_t * data) return 0; } + +void LedRs232Device::unblockAfterDelay() +{ + std::cout << "Device unblocked" << std::endl; + _blockedForDelay = false; +} diff --git a/libsrc/leddevice/LedRs232Device.h b/libsrc/leddevice/LedRs232Device.h index f8154581..e11d0a8a 100644 --- a/libsrc/leddevice/LedRs232Device.h +++ b/libsrc/leddevice/LedRs232Device.h @@ -1,5 +1,7 @@ #pragma once +#include + // Serial includes #include @@ -9,8 +11,10 @@ /// /// The LedRs232Device implements an abstract base-class for LedDevices using a RS232-device. /// -class LedRs232Device : public LedDevice +class LedRs232Device : public QObject, public LedDevice { + Q_OBJECT + public: /// /// Constructs the LedDevice attached to a RS232-device @@ -43,6 +47,10 @@ protected: */ int writeBytes(const unsigned size, const uint8_t *data); +private slots: + /// Unblock the device after a connection delay + void unblockAfterDelay(); + private: /// The name of the output device const std::string _deviceName; @@ -55,4 +63,6 @@ private: /// The RS232 serial-device serial::Serial _rs232Port; + + bool _blockedForDelay; }; From f9906845e0efb0919d5476d199c0a4a90d6c4d17 Mon Sep 17 00:00:00 2001 From: johan Date: Sun, 4 May 2014 14:20:25 +0200 Subject: [PATCH 14/74] Update the install script - follow =github redirections when using curl under OpenElec - Use raw.githubusercontent.com iso raw.github.com to prevent a redirection Former-commit-id: af76999acd598d4383b3a64b09e1407cd666ff99 --- bin/install_hyperion.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/bin/install_hyperion.sh b/bin/install_hyperion.sh index 53dd28e6..f697c2f2 100755 --- a/bin/install_hyperion.sh +++ b/bin/install_hyperion.sh @@ -34,13 +34,13 @@ fi echo 'Downloading hyperion' if [ $IS_OPENELEC -eq 1 ]; then # OpenELEC has a readonly file system. Use alternative location - curl --get https://raw.github.com/tvdzwan/hyperion/master/deploy/hyperion.tar.gz | tar -C /storage -xz - curl --get https://raw.github.com/tvdzwan/hyperion/master/deploy/hyperion.deps.openelec-rpi.tar.gz | tar -C /storage/hyperion/bin -xz + curl -L --get https://raw.githubusercontent.com/tvdzwan/hyperion/master/deploy/hyperion.tar.gz | tar -C /storage -xz + curl -L --get https://raw.githubusercontent.com/tvdzwan/hyperion/master/deploy/hyperion.deps.openelec-rpi.tar.gz | tar -C /storage/hyperion/bin -xz # modify the default config to have a correct effect path sed -i 's:/opt:/storage:g' /storage/hyperion/config/hyperion.config.json else - wget https://raw.github.com/tvdzwan/hyperion/master/deploy/hyperion.tar.gz -O - | tar -C /opt -xz + wget https://raw.githubusercontent.com/tvdzwan/hyperion/master/deploy/hyperion.tar.gz -O - | tar -C /opt -xz fi # create links to the binaries @@ -68,9 +68,9 @@ fi if [ $USE_INITCTL -eq 1 ]; then echo 'Installing initctl script' if [ $IS_RASPBMC -eq 1 ]; then - wget -N https://raw.github.com/tvdzwan/hyperion/master/deploy/hyperion.conf -P /etc/init/ + wget -N https://raw.githubusercontent.com/tvdzwan/hyperion/master/deploy/hyperion.conf -P /etc/init/ else - wget -N https://raw.github.com/tvdzwan/hyperion/master/deploy/hyperion.xbian.conf -O /etc/init/hyperion.conf + wget -N https://raw.githubusercontent.com/tvdzwan/hyperion/master/deploy/hyperion.xbian.conf -O /etc/init/hyperion.conf fi elif [ $USE_SERVICE -eq 1 ]; then echo 'Installing startup script in init.d' From 106257181b50b9945cf38cfac31b1922d02dbcea Mon Sep 17 00:00:00 2001 From: Marc Dahlem Date: Tue, 6 May 2014 18:36:34 +0200 Subject: [PATCH 15/74] Added ability to define effect arguments for the bootsequence Former-commit-id: b9827bb7af6c2cfdd8b1b0388d16541612b360d4 --- src/hyperiond/hyperiond.cpp | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/src/hyperiond/hyperiond.cpp b/src/hyperiond/hyperiond.cpp index c6174fa9..b6365019 100644 --- a/src/hyperiond/hyperiond.cpp +++ b/src/hyperiond/hyperiond.cpp @@ -112,14 +112,31 @@ int main(int argc, char** argv) const std::string effectName = effectConfig["effect"].asString(); const unsigned duration_ms = effectConfig["duration_ms"].asUInt(); const int priority = 0; - - if (hyperion.setEffect(effectName, priority, duration_ms) == 0) + + if (effectConfig.isMember("args")) { - std::cout << "Boot sequence(" << effectName << ") created and started" << std::endl; + const Json::Value effectConfigArgs = effectConfig["args"]; + if (hyperion.setEffect(effectName, effectConfigArgs, priority, duration_ms) == 0) + { + std::cout << "Boot sequence(" << effectName << ") with user-defined arguments created and started" << std::endl; + } + else + { + std::cout << "Failed to start boot sequence: " << effectName << " with user-defined arguments" << std::endl; + } + } else { - std::cout << "Failed to start boot sequence: " << effectName << std::endl; + + if (hyperion.setEffect(effectName, priority, duration_ms) == 0) + { + std::cout << "Boot sequence(" << effectName << ") created and started" << std::endl; + } + else + { + std::cout << "Failed to start boot sequence: " << effectName << std::endl; + } } } From d50b46bda2c1787b62adf252c235fc58203dfbe4 Mon Sep 17 00:00:00 2001 From: johan Date: Tue, 6 May 2014 22:02:40 +0200 Subject: [PATCH 16/74] Fix integration area = 0 for a led Former-commit-id: b365c4c9605a84ea6b7f88043f6f9c70b7122931 --- libsrc/hyperion/ImageToLedsMap.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libsrc/hyperion/ImageToLedsMap.cpp b/libsrc/hyperion/ImageToLedsMap.cpp index 202f22ff..aa309768 100644 --- a/libsrc/hyperion/ImageToLedsMap.cpp +++ b/libsrc/hyperion/ImageToLedsMap.cpp @@ -35,15 +35,16 @@ ImageToLedsMap::ImageToLedsMap( // skip leds without area if ((led.maxX_frac-led.minX_frac) < 1e-6 || (led.maxY_frac-led.minY_frac) < 1e-6) { + mColorsMap.emplace_back(); continue; } - + // Compute the index boundaries for this led 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) From dca9371f6d7261d02e336935c7ef2dc41e2c52af Mon Sep 17 00:00:00 2001 From: johan Date: Tue, 6 May 2014 22:05:41 +0200 Subject: [PATCH 17/74] Update binaries Former-commit-id: d1dc28de9db9a90ecfc185882f994d917bce8e6f --- deploy/hyperion.tar.gz.REMOVED.git-id | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy/hyperion.tar.gz.REMOVED.git-id b/deploy/hyperion.tar.gz.REMOVED.git-id index a76f2a2a..7f4560e3 100644 --- a/deploy/hyperion.tar.gz.REMOVED.git-id +++ b/deploy/hyperion.tar.gz.REMOVED.git-id @@ -1 +1 @@ -e7867d56a269136c5c795a55b831dca58e962139 \ No newline at end of file +e3e4ea5204c555e64aa909d5bbbd6ac95ebec0dc \ No newline at end of file From 853d00289425d90e71331005d152400a7ae69156 Mon Sep 17 00:00:00 2001 From: ntim Date: Wed, 7 May 2014 15:22:13 +0200 Subject: [PATCH 18/74] Timer to restore light state properly implemented. Lights are not switched on after state has been saved. Former-commit-id: 04959ea3731eb7de9f59b80a789f03cfeb2d4ba3 --- libsrc/leddevice/LedDevicePhilipsHue.cpp | 18 ++++++++++++++---- libsrc/leddevice/LedDevicePhilipsHue.h | 9 ++++++++- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/libsrc/leddevice/LedDevicePhilipsHue.cpp b/libsrc/leddevice/LedDevicePhilipsHue.cpp index 35f045f2..f8a9698a 100755 --- a/libsrc/leddevice/LedDevicePhilipsHue.cpp +++ b/libsrc/leddevice/LedDevicePhilipsHue.cpp @@ -13,9 +13,9 @@ LedDevicePhilipsHue::LedDevicePhilipsHue(const std::string& output) : host(output.c_str()), username("newdeveloper") { http = new QHttp(host); - timer.setInterval(1000); + timer.setInterval(3000); timer.setSingleShot(true); - connect(&timer, SIGNAL(timeout()), this, SLOT(restoreStates()())); + connect(&timer, SIGNAL(timeout()), this, SLOT(restoreStates())); } LedDevicePhilipsHue::~LedDevicePhilipsHue() { @@ -26,6 +26,7 @@ int LedDevicePhilipsHue::write(const std::vector & ledValues) { // Save light states if not done before. if (!statesSaved()) { saveStates(ledValues.size()); + switchOn(ledValues.size()); } // Iterate through colors and set light states. unsigned int lightId = 1; @@ -110,13 +111,22 @@ void LedDevicePhilipsHue::saveStates(unsigned int nLights) { } // Save state object values which are subject to change. Json::Value state(Json::objectValue); - state["xy"] = json["state"]["xy"]; - state["bri"] = json["state"]["bri"]; + state["on"] = json["state"]["on"]; + if (json["state"]["on"] == true) { + state["xy"] = json["state"]["xy"]; + state["bri"] = json["state"]["bri"]; + } // Save state object. states.push_back(QString(writer.write(state).c_str()).trimmed()); } } +void LedDevicePhilipsHue::switchOn(unsigned int nLights) { + for (unsigned int i = 0; i < nLights; i++) { + put(getStateRoute(i + 1), "{\"on\": true}"); + } +} + void LedDevicePhilipsHue::restoreStates() { unsigned int lightId = 1; for (QString state : states) { diff --git a/libsrc/leddevice/LedDevicePhilipsHue.h b/libsrc/leddevice/LedDevicePhilipsHue.h index 435fc800..84e7bd35 100755 --- a/libsrc/leddevice/LedDevicePhilipsHue.h +++ b/libsrc/leddevice/LedDevicePhilipsHue.h @@ -46,7 +46,7 @@ public: /// virtual int write(const std::vector & ledValues); - /// Switch the leds off + /// Restores the original state of the leds. virtual int switchOff(); private slots: @@ -104,6 +104,13 @@ private: /// void saveStates(unsigned int nLights); + /// + /// Switches the leds on. + /// + /// @param nLights the number of lights + /// + void switchOn(unsigned int nLights); + /// /// @return true if light states have been saved. /// From 2b3a3be0d3b7d3a8c9a734bb61535309c01afd8a Mon Sep 17 00:00:00 2001 From: Fabian Hertwig Date: Sat, 24 May 2014 13:03:46 +0200 Subject: [PATCH 19/74] Added the possibility to change the base color of the mood blobs over time. The base Color is moved 1 degree in baseColorChangeRate seconds if activated. It is moved between baseColorRangeLeft and baseColorRangeRight. These Values are in degrees. When these borders are set to the full circle (eg. 0 and 360), the base color moves around the colorwheel, otherwise it moves from left to right and back again. Furthermore there are three effects script for this feature: "Full color mood blobs" which moves around the full circle, "Warm mood blobs" and "Cold mood blobs" which only shows the warm, cold colors. This update wont change the functionality of the old scripts. Former-commit-id: 0c7a2ad280e49cd1ac0d6a9fbc9d1a9ff0eea236 --- effects/mood-blobs-cold.json | 16 +++++++ effects/mood-blobs-full.json | 16 +++++++ effects/mood-blobs-warm.json | 16 +++++++ effects/mood-blobs.py | 93 ++++++++++++++++++++++++++++-------- 4 files changed, 121 insertions(+), 20 deletions(-) create mode 100644 effects/mood-blobs-cold.json create mode 100644 effects/mood-blobs-full.json create mode 100644 effects/mood-blobs-warm.json diff --git a/effects/mood-blobs-cold.json b/effects/mood-blobs-cold.json new file mode 100644 index 00000000..d259b9f1 --- /dev/null +++ b/effects/mood-blobs-cold.json @@ -0,0 +1,16 @@ +{ + "name" : "Cold mood blobs", + "script" : "mood-blobs.py", + "args" : + { + "rotationTime" : 60.0, + "color" : [0,0,255], + "hueChange" : 30.0, + "blobs" : 5, + "reverse" : false, + "baseChange" : true, + "baseColorRangeLeft" : 160, + "baseColorRangeRight" : 320, + "baseColorChangeRate" : 2.0 + } +} diff --git a/effects/mood-blobs-full.json b/effects/mood-blobs-full.json new file mode 100644 index 00000000..8b230010 --- /dev/null +++ b/effects/mood-blobs-full.json @@ -0,0 +1,16 @@ +{ + "name" : "Full color mood blobs", + "script" : "mood-blobs.py", + "args" : + { + "rotationTime" : 60.0, + "color" : [0,0,255], + "hueChange" : 30.0, + "blobs" : 5, + "reverse" : false, + "baseChange" : true, + "baseColorRangeLeft" : 0, + "baseColorRangeRight" : 360, + "baseColorChangeRate" : 0.2 + } +} diff --git a/effects/mood-blobs-warm.json b/effects/mood-blobs-warm.json new file mode 100644 index 00000000..39443092 --- /dev/null +++ b/effects/mood-blobs-warm.json @@ -0,0 +1,16 @@ +{ + "name" : "Warm mood blobs", + "script" : "mood-blobs.py", + "args" : + { + "rotationTime" : 60.0, + "color" : [255,0,0], + "hueChange" : 30.0, + "blobs" : 5, + "reverse" : false, + "baseChange" : true, + "baseColorRangeLeft" : 333, + "baseColorRangeRight" : 151, + "baseColorChangeRate" : 2.0 + } +} diff --git a/effects/mood-blobs.py b/effects/mood-blobs.py index c0ce3d15..b638f3d3 100644 --- a/effects/mood-blobs.py +++ b/effects/mood-blobs.py @@ -6,14 +6,31 @@ import math # Get the parameters rotationTime = float(hyperion.args.get('rotationTime', 20.0)) color = hyperion.args.get('color', (0,0,255)) -hueChange = float(hyperion.args.get('hueChange', 60.0)) / 360.0 +hueChange = float(hyperion.args.get('hueChange', 60.0)) blobs = int(hyperion.args.get('blobs', 5)) reverse = bool(hyperion.args.get('reverse', False)) +baseColorChange = bool(hyperion.args.get('baseChange', False)) +baseColorRangeLeft = float(hyperion.args.get('baseColorRangeLeft',0.0)) # Degree +baseColorRangeRight = float(hyperion.args.get('baseColorRangeRight',360.0)) # Degree +baseColorChangeRate = float(hyperion.args.get('baseColorChangeRate',10.0)) # Seconds for one Degree + +# switch baseColor change off if left and right are too close together to see a difference in color +if (baseColorRangeRight > baseColorRangeLeft and (baseColorRangeRight - baseColorRangeLeft) < 10) or \ + (baseColorRangeLeft > baseColorRangeRight and ((baseColorRangeRight + 360) - baseColorRangeLeft) < 10): + baseColorChange = False + +# 360 -> 1 +fullColorWheelAvailable = (baseColorRangeRight % 360) == (baseColorRangeLeft % 360) +baseColorChangeIncreaseValue = 1.0 / 360.0 # 1 degree +hueChange /= 360.0 +baseColorRangeLeft = (baseColorRangeLeft / 360.0) +baseColorRangeRight = (baseColorRangeRight / 360.0) # Check parameters rotationTime = max(0.1, rotationTime) hueChange = max(0.0, min(abs(hueChange), .5)) blobs = max(1, blobs) +baseColorChangeRate = max(0, baseColorChangeRate) # > 0 # Calculate the color data baseHsv = colorsys.rgb_to_hsv(color[0]/255.0, color[1]/255.0, color[2]/255.0) @@ -27,6 +44,7 @@ for i in range(hyperion.ledCount): sleepTime = 0.1 amplitudePhaseIncrement = blobs * math.pi * sleepTime / rotationTime colorDataIncrement = 3 +baseColorChangeRate /= sleepTime # Switch direction if needed if reverse: @@ -39,23 +57,58 @@ colors = bytearray(hyperion.ledCount * (0,0,0)) # Start the write data loop amplitudePhase = 0.0 rotateColors = False -while not hyperion.abort(): - # Calculate new colors - for i in range(hyperion.ledCount): - amplitude = max(0.0, math.sin(-amplitudePhase + 2*math.pi * blobs * i / hyperion.ledCount)) - colors[3*i+0] = int(colorData[3*i+0] * amplitude) - colors[3*i+1] = int(colorData[3*i+1] * amplitude) - colors[3*i+2] = int(colorData[3*i+2] * amplitude) +baseColorChangeStepCount = 0 +baseHSVValue = baseHsv[0] +numberOfRotates = 0 - # set colors - hyperion.setColor(colors) - - # increment the phase - amplitudePhase = (amplitudePhase + amplitudePhaseIncrement) % (2*math.pi) - - if rotateColors: - colorData = colorData[-colorDataIncrement:] + colorData[:-colorDataIncrement] - rotateColors = not rotateColors - - # sleep for a while - time.sleep(sleepTime) +while not hyperion.abort(): + + # move the basecolor + if baseColorChange: + # every baseColorChangeRate seconds + if baseColorChangeStepCount >= baseColorChangeRate: + baseColorChangeStepCount = 0 + # cyclic increment when the full colorwheel is available, move up and down otherwise + if fullColorWheelAvailable: + baseHSVValue = (baseHSVValue + baseColorChangeIncreaseValue) % baseColorRangeRight + else: + # switch increment direction if baseHSV <= left or baseHSV >= right + if baseColorChangeIncreaseValue < 0 and baseHSVValue > baseColorRangeLeft and (baseHSVValue + baseColorChangeIncreaseValue) <= baseColorRangeLeft: + baseColorChangeIncreaseValue = abs(baseColorChangeIncreaseValue) + elif baseColorChangeIncreaseValue > 0 and baseHSVValue < baseColorRangeRight and (baseHSVValue + baseColorChangeIncreaseValue) >= baseColorRangeRight : + baseColorChangeIncreaseValue = -abs(baseColorChangeIncreaseValue) + + baseHSVValue = (baseHSVValue + baseColorChangeIncreaseValue) % 1.0 + + # update color values + colorData = bytearray() + for i in range(hyperion.ledCount): + hue = (baseHSVValue + hueChange * math.sin(2*math.pi * i / hyperion.ledCount)) % 1.0 + rgb = colorsys.hsv_to_rgb(hue, baseHsv[1], baseHsv[2]) + colorData += bytearray((int(255*rgb[0]), int(255*rgb[1]), int(255*rgb[2]))) + + # set correct rotation after reinitialisation of the array + colorData = colorData[-colorDataIncrement*numberOfRotates:] + colorData[:-colorDataIncrement*numberOfRotates] + + baseColorChangeStepCount += 1 + + # Calculate new colors + for i in range(hyperion.ledCount): + amplitude = max(0.0, math.sin(-amplitudePhase + 2*math.pi * blobs * i / hyperion.ledCount)) + colors[3*i+0] = int(colorData[3*i+0] * amplitude) + colors[3*i+1] = int(colorData[3*i+1] * amplitude) + colors[3*i+2] = int(colorData[3*i+2] * amplitude) + + # set colors + hyperion.setColor(colors) + + # increment the phase + amplitudePhase = (amplitudePhase + amplitudePhaseIncrement) % (2*math.pi) + + if rotateColors: + colorData = colorData[-colorDataIncrement:] + colorData[:-colorDataIncrement] + numberOfRotates = (numberOfRotates + 1) % hyperion.ledCount + rotateColors = not rotateColors + + # sleep for a while + time.sleep(sleepTime) From 8f1bb8e9db1b43ab5cb9710cbd805b1e04ee2d98 Mon Sep 17 00:00:00 2001 From: "T. van der Zwan" Date: Sat, 24 May 2014 21:05:59 +0200 Subject: [PATCH 20/74] Updated hyperion release to include philips hue Former-commit-id: d46ccf12d1f2a8059d45f946fe0f4c43e8b5bc9e --- deploy/hyperion.tar.gz.REMOVED.git-id | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy/hyperion.tar.gz.REMOVED.git-id b/deploy/hyperion.tar.gz.REMOVED.git-id index 7f4560e3..b04b8f6e 100644 --- a/deploy/hyperion.tar.gz.REMOVED.git-id +++ b/deploy/hyperion.tar.gz.REMOVED.git-id @@ -1 +1 @@ -e3e4ea5204c555e64aa909d5bbbd6ac95ebec0dc \ No newline at end of file +bfe399d8f3299c6110179fddb9f2b06b475d6a53 \ No newline at end of file From 98dfe7997f9eefa86da2164edd8e659372ec1005 Mon Sep 17 00:00:00 2001 From: "T. van der Zwan" Date: Mon, 2 Jun 2014 21:38:41 +0200 Subject: [PATCH 21/74] Updated release with updated blob effect Former-commit-id: 3d859fb7283961152ff67eb8101db89bdcd21847 --- deploy/hyperion.tar.gz.REMOVED.git-id | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy/hyperion.tar.gz.REMOVED.git-id b/deploy/hyperion.tar.gz.REMOVED.git-id index dde0b4dd..35028a43 100644 --- a/deploy/hyperion.tar.gz.REMOVED.git-id +++ b/deploy/hyperion.tar.gz.REMOVED.git-id @@ -1 +1 @@ -e6c826638971667596af0fb18e819514d8390286 \ No newline at end of file +d9eb8f0ef98c76bc54a43cc572183f7c54fc4dc9 \ No newline at end of file From 1d046ab35a8021c7bf5719ed508e50e1c07850f0 Mon Sep 17 00:00:00 2001 From: Gamadril Date: Sun, 15 Jun 2014 02:19:09 +0200 Subject: [PATCH 22/74] Added support for tpm2 protocol. One frame used for all LEDs Former-commit-id: 5b2ed33a50d90999c6f9508ba3782ad73838fb56 --- libsrc/leddevice/CMakeLists.txt | 2 ++ libsrc/leddevice/LedDeviceFactory.cpp | 10 +++++++ libsrc/leddevice/LedDeviceTpm2.cpp | 42 +++++++++++++++++++++++++++ libsrc/leddevice/LedDeviceTpm2.h | 38 ++++++++++++++++++++++++ 4 files changed, 92 insertions(+) create mode 100644 libsrc/leddevice/LedDeviceTpm2.cpp create mode 100644 libsrc/leddevice/LedDeviceTpm2.h diff --git a/libsrc/leddevice/CMakeLists.txt b/libsrc/leddevice/CMakeLists.txt index 093975db..441618d2 100755 --- a/libsrc/leddevice/CMakeLists.txt +++ b/libsrc/leddevice/CMakeLists.txt @@ -29,6 +29,7 @@ SET(Leddevice_HEADERS ${CURRENT_SOURCE_DIR}/LedDeviceSedu.h ${CURRENT_SOURCE_DIR}/LedDeviceTest.h ${CURRENT_SOURCE_DIR}/LedDeviceHyperionUsbasp.h + ${CURRENT_SOURCE_DIR}/LedDeviceTpm2.h ) SET(Leddevice_SOURCES @@ -45,6 +46,7 @@ SET(Leddevice_SOURCES ${CURRENT_SOURCE_DIR}/LedDeviceTest.cpp ${CURRENT_SOURCE_DIR}/LedDeviceHyperionUsbasp.cpp ${CURRENT_SOURCE_DIR}/LedDevicePhilipsHue.cpp + ${CURRENT_SOURCE_DIR}/LedDeviceTpm2.cpp ) if(ENABLE_SPIDEV) diff --git a/libsrc/leddevice/LedDeviceFactory.cpp b/libsrc/leddevice/LedDeviceFactory.cpp index 76eb0137..9c42384a 100755 --- a/libsrc/leddevice/LedDeviceFactory.cpp +++ b/libsrc/leddevice/LedDeviceFactory.cpp @@ -29,6 +29,7 @@ #include "LedDeviceTest.h" #include "LedDeviceHyperionUsbasp.h" #include "LedDevicePhilipsHue.h" +#include "LedDeviceTpm2.h" LedDevice * LedDeviceFactory::construct(const Json::Value & deviceConfig) { @@ -171,6 +172,15 @@ LedDevice * LedDeviceFactory::construct(const Json::Value & deviceConfig) const std::string output = deviceConfig["output"].asString(); device = new LedDeviceTest(output); } + else if (type == "tpm2") + { + const std::string output = deviceConfig["output"].asString(); + const unsigned rate = deviceConfig["rate"].asInt(); + + LedDeviceTpm2* deviceTpm2 = new LedDeviceTpm2(output, rate); + deviceTpm2->open(); + device = deviceTpm2; + } else { std::cout << "Unable to create device " << type << std::endl; diff --git a/libsrc/leddevice/LedDeviceTpm2.cpp b/libsrc/leddevice/LedDeviceTpm2.cpp new file mode 100644 index 00000000..49bb6121 --- /dev/null +++ b/libsrc/leddevice/LedDeviceTpm2.cpp @@ -0,0 +1,42 @@ + +// STL includes +#include +#include +#include + +// Linux includes +#include +#include + +// hyperion local includes +#include "LedDeviceTpm2.h" + +LedDeviceTpm2::LedDeviceTpm2(const std::string& outputDevice, const unsigned baudrate) : + LedRs232Device(outputDevice, baudrate), + _ledBuffer(0) +{ + // empty +} + +int LedDeviceTpm2::write(const std::vector &ledValues) +{ + if (_ledBuffer.size() == 0) + { + _ledBuffer.resize(5 + 3*ledValues.size()); + _ledBuffer[0] = 0xC9; // block-start byte + _ledBuffer[1] = 0xDA; // DATA frame + _ledBuffer[2] = ((3 * ledValues.size()) >> 8) & 0xFF; // LED count high byte + _ledBuffer[3] = (3 * ledValues.size()) & 0xFF; // LED count low byte + _ledBuffer.back() = 0x36; // block-end byte + } + + // write data + memcpy(4 + _ledBuffer.data(), ledValues.data(), ledValues.size() * 3); + return writeBytes(_ledBuffer.size(), _ledBuffer.data()); +} + +int LedDeviceTpm2::switchOff() +{ + memset(4 + _ledBuffer.data(), 0, _ledBuffer.size() - 5); + return writeBytes(_ledBuffer.size(), _ledBuffer.data()); +} diff --git a/libsrc/leddevice/LedDeviceTpm2.h b/libsrc/leddevice/LedDeviceTpm2.h new file mode 100644 index 00000000..f7ca8680 --- /dev/null +++ b/libsrc/leddevice/LedDeviceTpm2.h @@ -0,0 +1,38 @@ +#pragma once + +// STL includes +#include + +// hyperion incluse +#include "LedRs232Device.h" + +/// +/// Implementation of the LedDevice interface for writing to serial device using tpm2 protocol. +/// +class LedDeviceTpm2 : public LedRs232Device +{ +public: + /// + /// Constructs the LedDevice for attached serial device using supporting tpm2 protocol + /// All LEDs in the stripe are handled as one frame + /// + /// @param outputDevice The name of the output device (eg '/dev/ttyAMA0') + /// @param baudrate The used baudrate for writing to the output device + /// + LedDeviceTpm2(const std::string& outputDevice, const unsigned baudrate); + + /// + /// Writes the led color values to the led-device + /// + /// @param ledValues The color-value per led + /// @return Zero on succes else negative + /// + virtual int write(const std::vector &ledValues); + + /// Switch the leds off + virtual int switchOff(); + +private: + /// The buffer containing the packed RGB values + std::vector _ledBuffer; +}; From 3135b89255f529737da02ce4bde073328a5ee509 Mon Sep 17 00:00:00 2001 From: Gamadril Date: Wed, 18 Jun 2014 13:49:27 +0200 Subject: [PATCH 23/74] Update LedDeviceTpm2.cpp Former-commit-id: 8d51eb00044460002e29687468786e3056afd0bb --- libsrc/leddevice/LedDeviceTpm2.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libsrc/leddevice/LedDeviceTpm2.cpp b/libsrc/leddevice/LedDeviceTpm2.cpp index 49bb6121..a0fcf869 100644 --- a/libsrc/leddevice/LedDeviceTpm2.cpp +++ b/libsrc/leddevice/LedDeviceTpm2.cpp @@ -22,11 +22,11 @@ int LedDeviceTpm2::write(const std::vector &ledValues) { if (_ledBuffer.size() == 0) { - _ledBuffer.resize(5 + 3*ledValues.size()); + _ledBuffer.resize(5 + 3*ledValues.size()); _ledBuffer[0] = 0xC9; // block-start byte _ledBuffer[1] = 0xDA; // DATA frame - _ledBuffer[2] = ((3 * ledValues.size()) >> 8) & 0xFF; // LED count high byte - _ledBuffer[3] = (3 * ledValues.size()) & 0xFF; // LED count low byte + _ledBuffer[2] = ((3 * ledValues.size()) >> 8) & 0xFF; // frame size high byte + _ledBuffer[3] = (3 * ledValues.size()) & 0xFF; // frame size low byte _ledBuffer.back() = 0x36; // block-end byte } From ba4d2b45d5ae3a6b90c0c5ef2e5216328293b1db Mon Sep 17 00:00:00 2001 From: "T. van der Zwan" Date: Thu, 19 Jun 2014 22:16:03 +0200 Subject: [PATCH 24/74] Updated release package to include new device (tpm2) Former-commit-id: 24decda30a774b4d3702ec8fa375bc1ac4b849d7 --- deploy/hyperion.tar.gz.REMOVED.git-id | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy/hyperion.tar.gz.REMOVED.git-id b/deploy/hyperion.tar.gz.REMOVED.git-id index 35028a43..7729eb84 100644 --- a/deploy/hyperion.tar.gz.REMOVED.git-id +++ b/deploy/hyperion.tar.gz.REMOVED.git-id @@ -1 +1 @@ -d9eb8f0ef98c76bc54a43cc572183f7c54fc4dc9 \ No newline at end of file +5e8d795d2aa82337e42924c1a5292203d7d4271a \ No newline at end of file From c4c7ed03319220e50ad78e37502d14dcc896cdd3 Mon Sep 17 00:00:00 2001 From: bimsarck Date: Fri, 4 Jul 2014 12:11:37 +0200 Subject: [PATCH 25/74] Improved philip hue device: add lamp types autodetection add full xy-Colorspace implementations reduce http requests to the hue bridge. This prevents DDOS -> 503 add color black -> lamps off save state is temporary disabled Former-commit-id: 5a0328fe80a06a9f670c5190190e239857cbd15c --- libsrc/leddevice/LedDevicePhilipsHue.cpp | 231 +++++++++++++++++++---- libsrc/leddevice/LedDevicePhilipsHue.h | 33 +++- 2 files changed, 220 insertions(+), 44 deletions(-) diff --git a/libsrc/leddevice/LedDevicePhilipsHue.cpp b/libsrc/leddevice/LedDevicePhilipsHue.cpp index f8a9698a..ed549b35 100755 --- a/libsrc/leddevice/LedDevicePhilipsHue.cpp +++ b/libsrc/leddevice/LedDevicePhilipsHue.cpp @@ -1,3 +1,4 @@ +#include // Local-Hyperion includes #include "LedDevicePhilipsHue.h" @@ -10,43 +11,91 @@ #include #include -LedDevicePhilipsHue::LedDevicePhilipsHue(const std::string& output) : - host(output.c_str()), username("newdeveloper") { +LedDevicePhilipsHue::LedDevicePhilipsHue(const std::string &output) : + host(output.c_str()), username("newdeveloper") { http = new QHttp(host); - timer.setInterval(3000); +/* timer.setInterval(3000); timer.setSingleShot(true); - connect(&timer, SIGNAL(timeout()), this, SLOT(restoreStates())); + connect(&timer, SIGNAL(timeout()), this, SLOT(restoreStates()));*/ } LedDevicePhilipsHue::~LedDevicePhilipsHue() { delete http; } -int LedDevicePhilipsHue::write(const std::vector & ledValues) { +int LedDevicePhilipsHue::write(const std::vector &ledValues) { // Save light states if not done before. - if (!statesSaved()) { + if (!statesSaved()) saveStates(ledValues.size()); - switchOn(ledValues.size()); - } // Iterate through colors and set light states. - unsigned int lightId = 1; - for (const ColorRgb& color : ledValues) { - float x, y, b; - // Scale colors from [0, 255] to [0, 1] and convert to xy space. - rgbToXYBrightness(color.red / 255.0f, color.green / 255.0f, color.blue / 255.0f, x, y, b); - // Send adjust color command in JSON format. - put(getStateRoute(lightId), QString("{\"xy\": [%1, %2]}").arg(x).arg(y)); - // Send brightness color command in JSON format. - put(getStateRoute(lightId), QString("{\"bri\": %1}").arg(qRound(b * 255.0f))); - // Next light id. + unsigned int lightId = 0; + for (const ColorRgb &color : ledValues) { lightId++; + // Send only request to the brigde if color changed (prevents DDOS --> 503) + if (!oldLedValues.empty()) + if(!hasColorChanged(lightId, &color)) + continue; + + float r = color.red / 255.0f; + float g = color.green / 255.0f; + float b = color.blue / 255.0f; + + //set color gamut triangle + if(std::find(hueBulbs.begin(), hueBulbs.end(), modelIds[(lightId - 1)]) != hueBulbs.end()) { + Red = {0.675f, 0.322f}; + Green = {0.4091f, 0.518f}; + Blue = {0.167f, 0.04f}; + } else if (std::find(livingColors.begin(), + livingColors.end(), modelIds[(lightId - 1)]) != livingColors.end()) { + Red = {0.703f, 0.296f}; + Green = {0.214f, 0.709f}; + Blue = {0.139f, 0.081f}; + } else { + Red = {1.0f, 0.0f}; + Green = {0.0f, 1.0f}; + Blue = {0.0f, 0.0f}; + } + // if color equal black, switch off lamp ... + if (r == 0.0f && g == 0.0f && b == 0.0f) { + switchLampOff(lightId); + continue; + } + // ... and if lamp off, switch on + if (!checkOnStatus(states[(lightId - 1)])) + switchLampOn(lightId); + + float bri; + CGPoint p = CGPointMake(0, 0); + // Scale colors from [0, 255] to [0, 1] and convert to xy space. + rgbToXYBrightness(r, g, b, &p, bri); + // Send adjust color and brightness command in JSON format. + put(getStateRoute(lightId), + QString("{\"xy\": [%1, %2], \"bri\": %3}").arg(p.x).arg(p.y).arg(qRound(b * 255.0f))); } - timer.start(); + oldLedValues = ledValues; + //timer.start(); return 0; } +bool LedDevicePhilipsHue::hasColorChanged(unsigned int lightId, const ColorRgb *color) { + bool matchFound = true; + const ColorRgb &tmpOldColor = oldLedValues[(lightId - 1)]; + if ((*color).red == tmpOldColor.red) + matchFound = false; + if (!matchFound && (*color).green == tmpOldColor.green) + matchFound = false; + else + matchFound = true; + if (!matchFound && (*color).blue == tmpOldColor.blue) + matchFound = false; + else + matchFound = true; + + return matchFound; +} + int LedDevicePhilipsHue::switchOff() { - timer.stop(); + //timer.stop(); // If light states have been saved before, ... if (statesSaved()) { // ... restore them. @@ -55,6 +104,10 @@ int LedDevicePhilipsHue::switchOff() { return 0; } +bool LedDevicePhilipsHue::checkOnStatus(QString status) { + return status.contains("\"on\":true"); +} + void LedDevicePhilipsHue::put(QString route, QString content) { QString url = QString("/api/%1/%2").arg(username).arg(route); QHttpRequestHeader header("PUT", url); @@ -69,6 +122,7 @@ void LedDevicePhilipsHue::put(QString route, QString content) { http->request(header, content.toAscii()); // Go into the loop until the request is finished. loop.exec(); + //std::cout << http->readAll().data() << std::endl; } QByteArray LedDevicePhilipsHue::get(QString route) { @@ -96,6 +150,7 @@ QString LedDevicePhilipsHue::getRoute(unsigned int lightId) { void LedDevicePhilipsHue::saveStates(unsigned int nLights) { // Clear saved light states. states.clear(); + modelIds.clear(); // Use json parser to parse reponse. Json::Reader reader; Json::FastWriter writer; @@ -117,14 +172,19 @@ void LedDevicePhilipsHue::saveStates(unsigned int nLights) { state["bri"] = json["state"]["bri"]; } // Save state object. + modelIds.push_back(QString(writer.write(json["modelid"]).c_str()).trimmed().replace("\"", "")); states.push_back(QString(writer.write(state).c_str()).trimmed()); } } -void LedDevicePhilipsHue::switchOn(unsigned int nLights) { - for (unsigned int i = 0; i < nLights; i++) { - put(getStateRoute(i + 1), "{\"on\": true}"); - } +void LedDevicePhilipsHue::switchLampOn(unsigned int lightId) { + put(getStateRoute(lightId), "{\"on\": true}"); + states[(lightId - 1)].replace("\"on\":false", "\"on\":true"); +} + +void LedDevicePhilipsHue::switchLampOff(unsigned int lightId) { + put(getStateRoute(lightId), "{\"on\": false}"); + states[(lightId - 1)].replace("\"on\":true", "\"on\":false"); } void LedDevicePhilipsHue::restoreStates() { @@ -135,30 +195,119 @@ void LedDevicePhilipsHue::restoreStates() { } // Clear saved light states. states.clear(); + modelIds.clear(); + oldLedValues.clear(); } bool LedDevicePhilipsHue::statesSaved() { return !states.empty(); } -void LedDevicePhilipsHue::rgbToXYBrightness(float red, float green, float blue, float& x, float& y, float& brightness) { - // Apply gamma correction. - red = (red > 0.04045f) ? qPow((red + 0.055f) / (1.0f + 0.055f), 2.4f) : (red / 12.92f); - green = (green > 0.04045f) ? qPow((green + 0.055f) / (1.0f + 0.055f), 2.4f) : (green / 12.92f); - blue = (blue > 0.04045f) ? qPow((blue + 0.055f) / (1.0f + 0.055f), 2.4f) : (blue / 12.92f); - // Convert to XYZ space. - float X = red * 0.649926f + green * 0.103455f + blue * 0.197109f; - float Y = red * 0.234327f + green * 0.743075f + blue * 0.022598f; - float Z = red * 0.0000000f + green * 0.053077f + blue * 1.035763f; - // Convert to x,y space. - x = X / (X + Y + Z); - y = Y / (X + Y + Z); - if (isnan(x)) { - x = 0.0f; - } - if (isnan(y)) { - y = 0.0f; +CGPoint LedDevicePhilipsHue::CGPointMake(float x, float y) { + CGPoint p; + p.x = x; + p.y = y; + + return p; +} + +float LedDevicePhilipsHue::CrossProduct(CGPoint p1, CGPoint p2) { + return (p1.x * p2.y - p1.y * p2.x); +} + +bool LedDevicePhilipsHue::CheckPointInLampsReach(CGPoint p) { + CGPoint v1 = CGPointMake(Green.x - Red.x, Green.y - Red.y); + CGPoint v2 = CGPointMake(Blue.x - Red.x, Blue.y - Red.y); + + CGPoint q = CGPointMake(p.x - Red.x, p.y - Red.y); + + float s = CrossProduct(q, v2) / CrossProduct(v1, v2); + float t = CrossProduct(v1, q) / CrossProduct(v1, v2); + if ((s >= 0.0f) && (t >= 0.0f) && (s + t <= 1.0f)) + return true; + else + return false; +} + +CGPoint LedDevicePhilipsHue::GetClosestPointToPoint(CGPoint A, CGPoint B, CGPoint P) { + CGPoint AP = CGPointMake(P.x - A.x, P.y - A.y); + CGPoint AB = CGPointMake(B.x - A.x, B.y - A.y); + float ab2 = AB.x * AB.x + AB.y * AB.y; + float ap_ab = AP.x * AB.x + AP.y * AB.y; + + float t = ap_ab / ab2; + + if (t < 0.0f) + t = 0.0f; + else if (t > 1.0f) + t = 1.0f; + + return CGPointMake(A.x + AB.x * t, A.y + AB.y * t); +} + +float LedDevicePhilipsHue::GetDistanceBetweenTwoPoints(CGPoint one, CGPoint two) { + float dx = one.x - two.x; // horizontal difference + float dy = one.y - two.y; // vertical difference + float dist = sqrt(dx * dx + dy * dy); + + return dist; +} + +void LedDevicePhilipsHue::rgbToXYBrightness(float red, float green, float blue, CGPoint *xyPoint, float &brightness) { + //Apply gamma correction. + float r = (red > 0.04045f) ? powf((red + 0.055f) / (1.0f + 0.055f), 2.4f) : (red / 12.92f); + float g = (green > 0.04045f) ? powf((green + 0.055f) / (1.0f + 0.055f), 2.4f) : (green / 12.92f); + float b = (blue > 0.04045f) ? powf((blue + 0.055f) / (1.0f + 0.055f), 2.4f) : (blue / 12.92f); + //Convert to XYZ space. + float X = r * 0.649926f + g * 0.103455f + b * 0.197109f; + float Y = r * 0.234327f + g * 0.743075f + b * 0.022598f; + float Z = r * 0.0000000f + g * 0.053077f + b * 1.035763f; + //Convert to x,y space. + float cx = X / (X + Y + Z + 0.0000001f); + float cy = Y / (X + Y + Z + 0.0000001f); + + if (isnan(cx)) + cx = 0.0f; + if (isnan(cy)) + cy = 0.0f; + + (*xyPoint).x = cx; + (*xyPoint).y = cy; + + //Check if the given XY value is within the colourreach of our lamps. + bool inReachOfLamps = CheckPointInLampsReach(*xyPoint); + + if (!inReachOfLamps) { + //It seems the colour is out of reach + //let's find the closes colour we can produce with our lamp and send this XY value out. + + //Find the closest point on each line in the triangle. + CGPoint pAB = GetClosestPointToPoint(Red, Green, *xyPoint); + CGPoint pAC = GetClosestPointToPoint(Blue, Red, *xyPoint); + CGPoint pBC = GetClosestPointToPoint(Green, Blue, *xyPoint); + + //Get the distances per point and see which point is closer to our Point. + float dAB = GetDistanceBetweenTwoPoints(*xyPoint, pAB); + float dAC = GetDistanceBetweenTwoPoints(*xyPoint, pAC); + float dBC = GetDistanceBetweenTwoPoints(*xyPoint, pBC); + + float lowest = dAB; + CGPoint closestPoint = pAB; + + if (dAC < lowest) { + lowest = dAC; + closestPoint = pAC; + } + if (dBC < lowest) { + lowest = dBC; + closestPoint = pBC; + } + + //Change the xy value to a value which is within the reach of the lamp. + (*xyPoint).x = closestPoint.x; + (*xyPoint).y = closestPoint.y; } + // Brightness is simply Y in the XYZ space. brightness = Y; } diff --git a/libsrc/leddevice/LedDevicePhilipsHue.h b/libsrc/leddevice/LedDevicePhilipsHue.h index 84e7bd35..9760b7ad 100755 --- a/libsrc/leddevice/LedDevicePhilipsHue.h +++ b/libsrc/leddevice/LedDevicePhilipsHue.h @@ -22,6 +22,11 @@ * * @author ntim (github) */ +struct CGPoint; +struct CGPoint { + float x; + float y; +}; class LedDevicePhilipsHue: public QObject, public LedDevice { Q_OBJECT public: @@ -44,7 +49,7 @@ public: /// /// @return Zero on success else negative /// - virtual int write(const std::vector & ledValues); + virtual int write(const std::vector &ledValues); /// Restores the original state of the leds. virtual int switchOff(); @@ -54,6 +59,19 @@ private slots: void restoreStates(); private: + // ModelIds + const std::vector hueBulbs = {"LCT001", "LCT002", "LCT003"}; + const std::vector livingColors = {"LLC001", "LLC005", "LLC006", "LLC007", + "LLC011", "LLC012", "LLC013", "LST001"}; + /// LivingColors color gamut triangle + CGPoint Red , Green, Blue; + + CGPoint CGPointMake(float x, float y); + float CrossProduct(CGPoint p1, CGPoint p2); + bool CheckPointInLampsReach(CGPoint p); + CGPoint GetClosestPointToPoint(CGPoint A, CGPoint B, CGPoint P); + float GetDistanceBetweenTwoPoints(CGPoint one, CGPoint two); + /// Array to save the light states. std::vector states; /// Ip address of the bridge @@ -65,6 +83,13 @@ private: /// Use timer to reset lights when we got into "GRABBINGMODE_OFF". QTimer timer; + std::vector oldLedValues; + std::vector modelIds; + + bool hasColorChanged(unsigned int lightId, const ColorRgb *color); + + bool checkOnStatus(QString status); + /// /// Sends a HTTP GET request (blocking). /// @@ -109,7 +134,9 @@ private: /// /// @param nLights the number of lights /// - void switchOn(unsigned int nLights); + void switchLampOn(unsigned int lightId); + + void switchLampOff(unsigned int lightId); /// /// @return true if light states have been saved. @@ -132,6 +159,6 @@ private: /// /// @param brightness converted brightness component /// - void rgbToXYBrightness(float red, float green, float blue, float& x, float& y, float& brightness); + void rgbToXYBrightness(float red, float green, float blue, CGPoint *xyPoint, float &brightness); }; From e6d39b047cc6bff6c3b526f3ae48a935f1c58f6a Mon Sep 17 00:00:00 2001 From: bimsarck Date: Sat, 12 Jul 2014 14:56:39 +0200 Subject: [PATCH 26/74] Remove unnecessary code - reenable timer Former-commit-id: b1a175a1f1e5c3a7da753240030815252b1b22bb --- libsrc/leddevice/LedDevicePhilipsHue.cpp | 67 ++++++++++-------------- libsrc/leddevice/LedDevicePhilipsHue.h | 17 +++--- 2 files changed, 37 insertions(+), 47 deletions(-) diff --git a/libsrc/leddevice/LedDevicePhilipsHue.cpp b/libsrc/leddevice/LedDevicePhilipsHue.cpp index ed549b35..1fb89e6e 100755 --- a/libsrc/leddevice/LedDevicePhilipsHue.cpp +++ b/libsrc/leddevice/LedDevicePhilipsHue.cpp @@ -14,16 +14,16 @@ LedDevicePhilipsHue::LedDevicePhilipsHue(const std::string &output) : host(output.c_str()), username("newdeveloper") { http = new QHttp(host); -/* timer.setInterval(3000); + timer.setInterval(3000); timer.setSingleShot(true); - connect(&timer, SIGNAL(timeout()), this, SLOT(restoreStates()));*/ + connect(&timer, SIGNAL(timeout()), this, SLOT(restoreStates())); } LedDevicePhilipsHue::~LedDevicePhilipsHue() { delete http; } -int LedDevicePhilipsHue::write(const std::vector &ledValues) { +int LedDevicePhilipsHue::write(const std::vector & ledValues) { // Save light states if not done before. if (!statesSaved()) saveStates(ledValues.size()); @@ -65,15 +65,15 @@ int LedDevicePhilipsHue::write(const std::vector &ledValues) { switchLampOn(lightId); float bri; - CGPoint p = CGPointMake(0, 0); + CGPoint p = {0.0f, 0.0f}; // Scale colors from [0, 255] to [0, 1] and convert to xy space. - rgbToXYBrightness(r, g, b, &p, bri); + rgbToXYBrightness(r, g, b, p, bri); // Send adjust color and brightness command in JSON format. put(getStateRoute(lightId), QString("{\"xy\": [%1, %2], \"bri\": %3}").arg(p.x).arg(p.y).arg(qRound(b * 255.0f))); } oldLedValues = ledValues; - //timer.start(); + timer.start(); return 0; } @@ -95,7 +95,7 @@ bool LedDevicePhilipsHue::hasColorChanged(unsigned int lightId, const ColorRgb * } int LedDevicePhilipsHue::switchOff() { - //timer.stop(); + timer.stop(); // If light states have been saved before, ... if (statesSaved()) { // ... restore them. @@ -122,7 +122,6 @@ void LedDevicePhilipsHue::put(QString route, QString content) { http->request(header, content.toAscii()); // Go into the loop until the request is finished. loop.exec(); - //std::cout << http->readAll().data() << std::endl; } QByteArray LedDevicePhilipsHue::get(QString route) { @@ -203,23 +202,15 @@ bool LedDevicePhilipsHue::statesSaved() { return !states.empty(); } -CGPoint LedDevicePhilipsHue::CGPointMake(float x, float y) { - CGPoint p; - p.x = x; - p.y = y; - - return p; -} - -float LedDevicePhilipsHue::CrossProduct(CGPoint p1, CGPoint p2) { +float LedDevicePhilipsHue::CrossProduct(CGPoint& p1, CGPoint& p2) { return (p1.x * p2.y - p1.y * p2.x); } -bool LedDevicePhilipsHue::CheckPointInLampsReach(CGPoint p) { - CGPoint v1 = CGPointMake(Green.x - Red.x, Green.y - Red.y); - CGPoint v2 = CGPointMake(Blue.x - Red.x, Blue.y - Red.y); +bool LedDevicePhilipsHue::CheckPointInLampsReach(CGPoint& p) { + CGPoint v1 = {Green.x - Red.x, Green.y - Red.y}; + CGPoint v2 = {Blue.x - Red.x, Blue.y - Red.y}; - CGPoint q = CGPointMake(p.x - Red.x, p.y - Red.y); + CGPoint q = {p.x - Red.x, p.y - Red.y}; float s = CrossProduct(q, v2) / CrossProduct(v1, v2); float t = CrossProduct(v1, q) / CrossProduct(v1, v2); @@ -229,9 +220,9 @@ bool LedDevicePhilipsHue::CheckPointInLampsReach(CGPoint p) { return false; } -CGPoint LedDevicePhilipsHue::GetClosestPointToPoint(CGPoint A, CGPoint B, CGPoint P) { - CGPoint AP = CGPointMake(P.x - A.x, P.y - A.y); - CGPoint AB = CGPointMake(B.x - A.x, B.y - A.y); +CGPoint LedDevicePhilipsHue::GetClosestPointToPoint(CGPoint& A, CGPoint& B, CGPoint& P) { + CGPoint AP = {P.x - A.x, P.y - A.y}; + CGPoint AB = {B.x - A.x, B.y - A.y}; float ab2 = AB.x * AB.x + AB.y * AB.y; float ap_ab = AP.x * AB.x + AP.y * AB.y; @@ -242,10 +233,10 @@ CGPoint LedDevicePhilipsHue::GetClosestPointToPoint(CGPoint A, CGPoint B, CGPoin else if (t > 1.0f) t = 1.0f; - return CGPointMake(A.x + AB.x * t, A.y + AB.y * t); + return {A.x + AB.x * t, A.y + AB.y * t}; } -float LedDevicePhilipsHue::GetDistanceBetweenTwoPoints(CGPoint one, CGPoint two) { +float LedDevicePhilipsHue::GetDistanceBetweenTwoPoints(CGPoint& one, CGPoint& two) { float dx = one.x - two.x; // horizontal difference float dy = one.y - two.y; // vertical difference float dist = sqrt(dx * dx + dy * dy); @@ -253,7 +244,7 @@ float LedDevicePhilipsHue::GetDistanceBetweenTwoPoints(CGPoint one, CGPoint two) return dist; } -void LedDevicePhilipsHue::rgbToXYBrightness(float red, float green, float blue, CGPoint *xyPoint, float &brightness) { +void LedDevicePhilipsHue::rgbToXYBrightness(float red, float green, float blue, CGPoint& xyPoint, float& brightness) { //Apply gamma correction. float r = (red > 0.04045f) ? powf((red + 0.055f) / (1.0f + 0.055f), 2.4f) : (red / 12.92f); float g = (green > 0.04045f) ? powf((green + 0.055f) / (1.0f + 0.055f), 2.4f) : (green / 12.92f); @@ -271,25 +262,25 @@ void LedDevicePhilipsHue::rgbToXYBrightness(float red, float green, float blue, if (isnan(cy)) cy = 0.0f; - (*xyPoint).x = cx; - (*xyPoint).y = cy; + xyPoint.x = cx; + xyPoint.y = cy; //Check if the given XY value is within the colourreach of our lamps. - bool inReachOfLamps = CheckPointInLampsReach(*xyPoint); + bool inReachOfLamps = CheckPointInLampsReach(xyPoint); if (!inReachOfLamps) { //It seems the colour is out of reach //let's find the closes colour we can produce with our lamp and send this XY value out. //Find the closest point on each line in the triangle. - CGPoint pAB = GetClosestPointToPoint(Red, Green, *xyPoint); - CGPoint pAC = GetClosestPointToPoint(Blue, Red, *xyPoint); - CGPoint pBC = GetClosestPointToPoint(Green, Blue, *xyPoint); + CGPoint pAB = GetClosestPointToPoint(Red, Green, xyPoint); + CGPoint pAC = GetClosestPointToPoint(Blue, Red, xyPoint); + CGPoint pBC = GetClosestPointToPoint(Green, Blue, xyPoint); //Get the distances per point and see which point is closer to our Point. - float dAB = GetDistanceBetweenTwoPoints(*xyPoint, pAB); - float dAC = GetDistanceBetweenTwoPoints(*xyPoint, pAC); - float dBC = GetDistanceBetweenTwoPoints(*xyPoint, pBC); + float dAB = GetDistanceBetweenTwoPoints(xyPoint, pAB); + float dAC = GetDistanceBetweenTwoPoints(xyPoint, pAC); + float dBC = GetDistanceBetweenTwoPoints(xyPoint, pBC); float lowest = dAB; CGPoint closestPoint = pAB; @@ -304,8 +295,8 @@ void LedDevicePhilipsHue::rgbToXYBrightness(float red, float green, float blue, } //Change the xy value to a value which is within the reach of the lamp. - (*xyPoint).x = closestPoint.x; - (*xyPoint).y = closestPoint.y; + xyPoint.x = closestPoint.x; + xyPoint.y = closestPoint.y; } // Brightness is simply Y in the XYZ space. diff --git a/libsrc/leddevice/LedDevicePhilipsHue.h b/libsrc/leddevice/LedDevicePhilipsHue.h index 9760b7ad..11ec4abd 100755 --- a/libsrc/leddevice/LedDevicePhilipsHue.h +++ b/libsrc/leddevice/LedDevicePhilipsHue.h @@ -49,7 +49,7 @@ public: /// /// @return Zero on success else negative /// - virtual int write(const std::vector &ledValues); + virtual int write(const std::vector & ledValues); /// Restores the original state of the leds. virtual int switchOff(); @@ -59,18 +59,17 @@ private slots: void restoreStates(); private: - // ModelIds + /// Available modelIds const std::vector hueBulbs = {"LCT001", "LCT002", "LCT003"}; const std::vector livingColors = {"LLC001", "LLC005", "LLC006", "LLC007", "LLC011", "LLC012", "LLC013", "LST001"}; - /// LivingColors color gamut triangle + /// Color gamut triangle CGPoint Red , Green, Blue; - CGPoint CGPointMake(float x, float y); - float CrossProduct(CGPoint p1, CGPoint p2); - bool CheckPointInLampsReach(CGPoint p); - CGPoint GetClosestPointToPoint(CGPoint A, CGPoint B, CGPoint P); - float GetDistanceBetweenTwoPoints(CGPoint one, CGPoint two); + float CrossProduct(CGPoint& p1, CGPoint& p2); + bool CheckPointInLampsReach(CGPoint& p); + CGPoint GetClosestPointToPoint(CGPoint& A, CGPoint& B, CGPoint& P); + float GetDistanceBetweenTwoPoints(CGPoint& one, CGPoint& two); /// Array to save the light states. std::vector states; @@ -159,6 +158,6 @@ private: /// /// @param brightness converted brightness component /// - void rgbToXYBrightness(float red, float green, float blue, CGPoint *xyPoint, float &brightness); + void rgbToXYBrightness(float red, float green, float blue, CGPoint& xyPoint, float& brightness); }; From c1998da2ec91515c328d024649751c0e97083dee Mon Sep 17 00:00:00 2001 From: bimsarck Date: Sun, 13 Jul 2014 13:48:49 +0200 Subject: [PATCH 27/74] Fixes saveState feature Former-commit-id: 8158c77521727bf74875a5998c803212aece0645 --- libsrc/leddevice/LedDevicePhilipsHue.cpp | 29 ++++++++++++++---------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/libsrc/leddevice/LedDevicePhilipsHue.cpp b/libsrc/leddevice/LedDevicePhilipsHue.cpp index 1fb89e6e..6b837eb0 100755 --- a/libsrc/leddevice/LedDevicePhilipsHue.cpp +++ b/libsrc/leddevice/LedDevicePhilipsHue.cpp @@ -11,7 +11,7 @@ #include #include -LedDevicePhilipsHue::LedDevicePhilipsHue(const std::string &output) : +LedDevicePhilipsHue::LedDevicePhilipsHue(const std::string& output) : host(output.c_str()), username("newdeveloper") { http = new QHttp(host); timer.setInterval(3000); @@ -30,23 +30,24 @@ int LedDevicePhilipsHue::write(const std::vector & ledValues) { // Iterate through colors and set light states. unsigned int lightId = 0; for (const ColorRgb &color : ledValues) { - lightId++; // Send only request to the brigde if color changed (prevents DDOS --> 503) if (!oldLedValues.empty()) - if(!hasColorChanged(lightId, &color)) + if(!hasColorChanged(lightId, &color)) { + lightId++; continue; + } float r = color.red / 255.0f; float g = color.green / 255.0f; float b = color.blue / 255.0f; //set color gamut triangle - if(std::find(hueBulbs.begin(), hueBulbs.end(), modelIds[(lightId - 1)]) != hueBulbs.end()) { + if(std::find(hueBulbs.begin(), hueBulbs.end(), modelIds[lightId]) != hueBulbs.end()) { Red = {0.675f, 0.322f}; Green = {0.4091f, 0.518f}; Blue = {0.167f, 0.04f}; } else if (std::find(livingColors.begin(), - livingColors.end(), modelIds[(lightId - 1)]) != livingColors.end()) { + livingColors.end(), modelIds[lightId]) != livingColors.end()) { Red = {0.703f, 0.296f}; Green = {0.214f, 0.709f}; Blue = {0.139f, 0.081f}; @@ -58,10 +59,11 @@ int LedDevicePhilipsHue::write(const std::vector & ledValues) { // if color equal black, switch off lamp ... if (r == 0.0f && g == 0.0f && b == 0.0f) { switchLampOff(lightId); + lightId++; continue; } // ... and if lamp off, switch on - if (!checkOnStatus(states[(lightId - 1)])) + if (!checkOnStatus(states[lightId])) switchLampOn(lightId); float bri; @@ -71,6 +73,7 @@ int LedDevicePhilipsHue::write(const std::vector & ledValues) { // Send adjust color and brightness command in JSON format. put(getStateRoute(lightId), QString("{\"xy\": [%1, %2], \"bri\": %3}").arg(p.x).arg(p.y).arg(qRound(b * 255.0f))); + lightId++; } oldLedValues = ledValues; timer.start(); @@ -79,7 +82,7 @@ int LedDevicePhilipsHue::write(const std::vector & ledValues) { bool LedDevicePhilipsHue::hasColorChanged(unsigned int lightId, const ColorRgb *color) { bool matchFound = true; - const ColorRgb &tmpOldColor = oldLedValues[(lightId - 1)]; + const ColorRgb &tmpOldColor = oldLedValues[lightId]; if ((*color).red == tmpOldColor.red) matchFound = false; if (!matchFound && (*color).green == tmpOldColor.green) @@ -139,7 +142,7 @@ QByteArray LedDevicePhilipsHue::get(QString route) { } QString LedDevicePhilipsHue::getStateRoute(unsigned int lightId) { - return QString("lights/%1/state").arg(lightId); + return QString("lights/%1/state").arg(lightId + 1); } QString LedDevicePhilipsHue::getRoute(unsigned int lightId) { @@ -178,18 +181,20 @@ void LedDevicePhilipsHue::saveStates(unsigned int nLights) { void LedDevicePhilipsHue::switchLampOn(unsigned int lightId) { put(getStateRoute(lightId), "{\"on\": true}"); - states[(lightId - 1)].replace("\"on\":false", "\"on\":true"); + states[lightId].replace("\"on\":false", "\"on\":true"); } void LedDevicePhilipsHue::switchLampOff(unsigned int lightId) { put(getStateRoute(lightId), "{\"on\": false}"); - states[(lightId - 1)].replace("\"on\":true", "\"on\":false"); + states[lightId].replace("\"on\":true", "\"on\":false"); } void LedDevicePhilipsHue::restoreStates() { - unsigned int lightId = 1; + unsigned int lightId = 0; for (QString state : states) { - put(getStateRoute(lightId), state); + if (!checkOnStatus(states[lightId])) + switchLampOn(lightId); + put(getStateRoute(lightId), states[lightId]); lightId++; } // Clear saved light states. From f5a8174783bab0029751accb5c40250f211195b2 Mon Sep 17 00:00:00 2001 From: Tim Niggemann Date: Tue, 15 Jul 2014 08:51:07 +0200 Subject: [PATCH 28/74] Implemented color triangle calculations. Former-commit-id: dbde6635077a82ace5f4ed1fdf89458a28e7bf05 --- libsrc/leddevice/LedDevicePhilipsHue.cpp | 145 +++++++++++++++++++---- libsrc/leddevice/LedDevicePhilipsHue.h | 27 ++++- 2 files changed, 143 insertions(+), 29 deletions(-) diff --git a/libsrc/leddevice/LedDevicePhilipsHue.cpp b/libsrc/leddevice/LedDevicePhilipsHue.cpp index f8a9698a..b2525033 100755 --- a/libsrc/leddevice/LedDevicePhilipsHue.cpp +++ b/libsrc/leddevice/LedDevicePhilipsHue.cpp @@ -10,6 +10,8 @@ #include #include +#include + LedDevicePhilipsHue::LedDevicePhilipsHue(const std::string& output) : host(output.c_str()), username("newdeveloper") { http = new QHttp(host); @@ -24,18 +26,21 @@ LedDevicePhilipsHue::~LedDevicePhilipsHue() { int LedDevicePhilipsHue::write(const std::vector & ledValues) { // Save light states if not done before. - if (!statesSaved()) { - saveStates(ledValues.size()); - switchOn(ledValues.size()); + if (!areStatesSaved()) { + saveStates((unsigned int) ledValues.size()); + switchOn((unsigned int) ledValues.size()); } // Iterate through colors and set light states. unsigned int lightId = 1; for (const ColorRgb& color : ledValues) { - float x, y, b; + // Find triangle. + CGTriangle triangle = triangles.at(lightId - 1); // Scale colors from [0, 255] to [0, 1] and convert to xy space. - rgbToXYBrightness(color.red / 255.0f, color.green / 255.0f, color.blue / 255.0f, x, y, b); + CGPoint xy; + float b; + rgbToXYBrightness(color.red / 255.0f, color.green / 255.0f, color.blue / 255.0f, triangle, xy, b); // Send adjust color command in JSON format. - put(getStateRoute(lightId), QString("{\"xy\": [%1, %2]}").arg(x).arg(y)); + put(getStateRoute(lightId), QString("{\"xy\": [%1, %2]}").arg(xy.x).arg(xy.y)); // Send brightness color command in JSON format. put(getStateRoute(lightId), QString("{\"bri\": %1}").arg(qRound(b * 255.0f))); // Next light id. @@ -48,7 +53,7 @@ int LedDevicePhilipsHue::write(const std::vector & ledValues) { int LedDevicePhilipsHue::switchOff() { timer.stop(); // If light states have been saved before, ... - if (statesSaved()) { + if (areStatesSaved()) { // ... restore them. restoreStates(); } @@ -93,6 +98,27 @@ QString LedDevicePhilipsHue::getRoute(unsigned int lightId) { return QString("lights/%1").arg(lightId); } +CGTriangle LedDevicePhilipsHue::getTriangle(QString modelId) { + const std::set HUE_BULBS_MODEL_IDS = { "LCT001", "LCT002", "LCT003" }; + const std::set LIVING_COLORS_MODEL_IDS = { "LLC001", "LLC005", "LLC006", "LLC007", "LLC011", "LLC012", + "LLC013", "LST001" }; + CGTriangle triangle; + if (HUE_BULBS_MODEL_IDS.find(modelId) != HUE_BULBS_MODEL_IDS.end()) { + triangle.red = {0.675f, 0.322f}; + triangle.green = {0.4091f, 0.518f}; + triangle.blue = {0.167f, 0.04f}; + } else if (LIVING_COLORS_MODEL_IDS.find(modelId) != LIVING_COLORS_MODEL_IDS.end()) { + triangle.red = {0.703f, 0.296f}; + triangle.green = {0.214f, 0.709f}; + triangle.blue = {0.139f, 0.081f}; + } else { + triangle.red = {1.0f, 0.0f}; + triangle.green = {0.0f, 1.0f}; + triangle.blue = {0.0f, 0.0f}; + } + return triangle; +} + void LedDevicePhilipsHue::saveStates(unsigned int nLights) { // Clear saved light states. states.clear(); @@ -116,8 +142,12 @@ void LedDevicePhilipsHue::saveStates(unsigned int nLights) { state["xy"] = json["state"]["xy"]; state["bri"] = json["state"]["bri"]; } + // Save id. + ids.push_back(QString(writer.write(json["modelid"]).c_str()).trimmed().replace("\"", "")); // Save state object. states.push_back(QString(writer.write(state).c_str()).trimmed()); + // Determine triangle. + triangles.push_back(getTriangle(ids.back())); } } @@ -137,27 +167,94 @@ void LedDevicePhilipsHue::restoreStates() { states.clear(); } -bool LedDevicePhilipsHue::statesSaved() { +bool LedDevicePhilipsHue::areStatesSaved() { return !states.empty(); } -void LedDevicePhilipsHue::rgbToXYBrightness(float red, float green, float blue, float& x, float& y, float& brightness) { - // Apply gamma correction. - red = (red > 0.04045f) ? qPow((red + 0.055f) / (1.0f + 0.055f), 2.4f) : (red / 12.92f); - green = (green > 0.04045f) ? qPow((green + 0.055f) / (1.0f + 0.055f), 2.4f) : (green / 12.92f); - blue = (blue > 0.04045f) ? qPow((blue + 0.055f) / (1.0f + 0.055f), 2.4f) : (blue / 12.92f); - // Convert to XYZ space. - float X = red * 0.649926f + green * 0.103455f + blue * 0.197109f; - float Y = red * 0.234327f + green * 0.743075f + blue * 0.022598f; - float Z = red * 0.0000000f + green * 0.053077f + blue * 1.035763f; - // Convert to x,y space. - x = X / (X + Y + Z); - y = Y / (X + Y + Z); - if (isnan(x)) { - x = 0.0f; +float LedDevicePhilipsHue::crossProduct(CGPoint p1, CGPoint p2) { + return p1.x * p2.y - p1.y * p2.x; +} + +bool LedDevicePhilipsHue::isPointInLampsReach(CGTriangle triangle, CGPoint p) { + CGPoint v1 = { triangle.green.x - triangle.red.x, triangle.green.y - triangle.red.y }; + CGPoint v2 = { triangle.blue.x - triangle.red.x, triangle.blue.y - triangle.red.y }; + CGPoint q = { p.x - triangle.red.x, p.y - triangle.red.y }; + float s = crossProduct(q, v2) / crossProduct(v1, v2); + float t = crossProduct(v1, q) / crossProduct(v1, v2); + if ((s >= 0.0f) && (t >= 0.0f) && (s + t <= 1.0f)) { + return true; + } else { + return false; } - if (isnan(y)) { - y = 0.0f; +} + +CGPoint LedDevicePhilipsHue::getClosestPointToPoint(CGPoint A, CGPoint B, CGPoint P) { + CGPoint AP = { P.x - A.x, P.y - A.y }; + CGPoint AB = { B.x - A.x, B.y - A.y }; + float ab2 = AB.x * AB.x + AB.y * AB.y; + float ap_ab = AP.x * AB.x + AP.y * AB.y; + float t = ap_ab / ab2; + if (t < 0.0f) { + t = 0.0f; + } else if (t > 1.0f) { + t = 1.0f; + } + return {A.x + AB.x * t, A.y + AB.y * t}; +} + +float LedDevicePhilipsHue::getDistanceBetweenTwoPoints(CGPoint one, CGPoint two) { + // Horizontal difference. + float dx = one.x - two.x; + // Vertical difference. + float dy = one.y - two.y; + float dist = sqrt(dx * dx + dy * dy); + return dist; +} + +void LedDevicePhilipsHue::rgbToXYBrightness(float red, float green, float blue, CGTriangle triangle, CGPoint& xyPoint, + float& brightness) { + // Apply gamma correction. + float r = (red > 0.04045f) ? powf((red + 0.055f) / (1.0f + 0.055f), 2.4f) : (red / 12.92f); + float g = (green > 0.04045f) ? powf((green + 0.055f) / (1.0f + 0.055f), 2.4f) : (green / 12.92f); + float b = (blue > 0.04045f) ? powf((blue + 0.055f) / (1.0f + 0.055f), 2.4f) : (blue / 12.92f); + // Convert to XYZ space. + float X = r * 0.649926f + g * 0.103455f + b * 0.197109f; + float Y = r * 0.234327f + g * 0.743075f + b * 0.022598f; + float Z = r * 0.0000000f + g * 0.053077f + b * 1.035763f; + // Convert to x,y space. + float cx = X / (X + Y + Z + 0.0000001f); + float cy = Y / (X + Y + Z + 0.0000001f); + if (isnan(cx)) { + cx = 0.0f; + } + if (isnan(cy)) { + cy = 0.0f; + } + xyPoint.x = cx; + xyPoint.y = cy; + // Check if the given XY value is within the colourreach of our lamps. + if (!isPointInLampsReach(triangle, xyPoint)) { + // It seems the colour is out of reach let's find the closes colour we can produce with our lamp and send this XY value out. + CGPoint pAB = getClosestPointToPoint(triangle.red, triangle.green, xyPoint); + CGPoint pAC = getClosestPointToPoint(triangle.blue, triangle.red, xyPoint); + CGPoint pBC = getClosestPointToPoint(triangle.green, triangle.blue, xyPoint); + // Get the distances per point and see which point is closer to our Point. + float dAB = getDistanceBetweenTwoPoints(xyPoint, pAB); + float dAC = getDistanceBetweenTwoPoints(xyPoint, pAC); + float dBC = getDistanceBetweenTwoPoints(xyPoint, pBC); + float lowest = dAB; + CGPoint closestPoint = pAB; + if (dAC < lowest) { + lowest = dAC; + closestPoint = pAC; + } + if (dBC < lowest) { + lowest = dBC; + closestPoint = pBC; + } + // Change the xy value to a value which is within the reach of the lamp. + xyPoint.x = closestPoint.x; + xyPoint.y = closestPoint.y; } // Brightness is simply Y in the XYZ space. brightness = Y; diff --git a/libsrc/leddevice/LedDevicePhilipsHue.h b/libsrc/leddevice/LedDevicePhilipsHue.h index 84e7bd35..cc988e97 100755 --- a/libsrc/leddevice/LedDevicePhilipsHue.h +++ b/libsrc/leddevice/LedDevicePhilipsHue.h @@ -12,6 +12,15 @@ // Leddevice includes #include +struct CGPoint { + float x; + float y; +}; + +struct CGTriangle { + CGPoint red, green, blue; +}; + /** * Implementation for the Philips Hue system. * @@ -56,6 +65,10 @@ private slots: private: /// Array to save the light states. std::vector states; + /// Array to save model ids. + std::vector ids; + /// Color triangles. + std::vector triangles; /// Ip address of the bridge QString host; /// User name for the API ("newdeveloper") @@ -114,7 +127,7 @@ private: /// /// @return true if light states have been saved. /// - bool statesSaved(); + bool areStatesSaved(); /// /// Converts an RGB color to the Hue xy color space and brightness @@ -126,12 +139,16 @@ private: /// /// @param blue the blue component in [0, 1] /// - /// @param x converted x component - /// - /// @param y converted y component + /// @param xyPoint converted xy component /// /// @param brightness converted brightness component /// - void rgbToXYBrightness(float red, float green, float blue, float& x, float& y, float& brightness); + void rgbToXYBrightness(float red, float green, float blue, CGTriangle triangle, CGPoint& xyPoint, float& brightness); + + CGTriangle getTriangle(QString modelId); + float crossProduct(CGPoint p1, CGPoint p2); + bool isPointInLampsReach(CGTriangle triangle, CGPoint p); + CGPoint getClosestPointToPoint(CGPoint a, CGPoint b, CGPoint p); + float getDistanceBetweenTwoPoints(CGPoint one, CGPoint two); }; From 9269b0a1e3c54b3c20f90f2be1b138c3b64715d3 Mon Sep 17 00:00:00 2001 From: Tim Niggemann Date: Tue, 15 Jul 2014 08:54:40 +0200 Subject: [PATCH 29/74] Removed saving of model ids. Save the corresponding color triangles instead for speedup. Former-commit-id: 72e6031234e12a488a5425e80e73dc8b03ec364f --- libsrc/leddevice/LedDevicePhilipsHue.cpp | 7 ++++--- libsrc/leddevice/LedDevicePhilipsHue.h | 2 -- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/libsrc/leddevice/LedDevicePhilipsHue.cpp b/libsrc/leddevice/LedDevicePhilipsHue.cpp index b2525033..5c12b667 100755 --- a/libsrc/leddevice/LedDevicePhilipsHue.cpp +++ b/libsrc/leddevice/LedDevicePhilipsHue.cpp @@ -122,6 +122,7 @@ CGTriangle LedDevicePhilipsHue::getTriangle(QString modelId) { void LedDevicePhilipsHue::saveStates(unsigned int nLights) { // Clear saved light states. states.clear(); + triangles.clear(); // Use json parser to parse reponse. Json::Reader reader; Json::FastWriter writer; @@ -142,12 +143,11 @@ void LedDevicePhilipsHue::saveStates(unsigned int nLights) { state["xy"] = json["state"]["xy"]; state["bri"] = json["state"]["bri"]; } - // Save id. - ids.push_back(QString(writer.write(json["modelid"]).c_str()).trimmed().replace("\"", "")); // Save state object. states.push_back(QString(writer.write(state).c_str()).trimmed()); // Determine triangle. - triangles.push_back(getTriangle(ids.back())); + QString modelId = QString(writer.write(json["modelid"]).c_str()).trimmed().replace("\"", ""); + triangles.push_back(getTriangle(modelId)); } } @@ -165,6 +165,7 @@ void LedDevicePhilipsHue::restoreStates() { } // Clear saved light states. states.clear(); + triangles.clear(); } bool LedDevicePhilipsHue::areStatesSaved() { diff --git a/libsrc/leddevice/LedDevicePhilipsHue.h b/libsrc/leddevice/LedDevicePhilipsHue.h index cc988e97..81781db1 100755 --- a/libsrc/leddevice/LedDevicePhilipsHue.h +++ b/libsrc/leddevice/LedDevicePhilipsHue.h @@ -65,8 +65,6 @@ private slots: private: /// Array to save the light states. std::vector states; - /// Array to save model ids. - std::vector ids; /// Color triangles. std::vector triangles; /// Ip address of the bridge From 67970fce08d60e3bdf10d4126c3c0a43d18a318b Mon Sep 17 00:00:00 2001 From: Tim Niggemann Date: Tue, 15 Jul 2014 09:55:58 +0200 Subject: [PATCH 30/74] Created HueLamp class holding the color space as well as the original state and current color. Former-commit-id: 129c34f6008a68bca6cafb63eb0c0ad6a37f5179 --- libsrc/leddevice/LedDevicePhilipsHue.cpp | 169 ++++++++++++----------- libsrc/leddevice/LedDevicePhilipsHue.h | 37 +++-- 2 files changed, 114 insertions(+), 92 deletions(-) diff --git a/libsrc/leddevice/LedDevicePhilipsHue.cpp b/libsrc/leddevice/LedDevicePhilipsHue.cpp index 5c12b667..a678480d 100755 --- a/libsrc/leddevice/LedDevicePhilipsHue.cpp +++ b/libsrc/leddevice/LedDevicePhilipsHue.cpp @@ -31,18 +31,22 @@ int LedDevicePhilipsHue::write(const std::vector & ledValues) { switchOn((unsigned int) ledValues.size()); } // Iterate through colors and set light states. - unsigned int lightId = 1; + unsigned int lightId = 0; for (const ColorRgb& color : ledValues) { - // Find triangle. - CGTriangle triangle = triangles.at(lightId - 1); + // Get lamp. + HueLamp& lamp = lamps.at(lightId); // Scale colors from [0, 255] to [0, 1] and convert to xy space. - CGPoint xy; - float b; - rgbToXYBrightness(color.red / 255.0f, color.green / 255.0f, color.blue / 255.0f, triangle, xy, b); - // Send adjust color command in JSON format. - put(getStateRoute(lightId), QString("{\"xy\": [%1, %2]}").arg(xy.x).arg(xy.y)); - // Send brightness color command in JSON format. - put(getStateRoute(lightId), QString("{\"bri\": %1}").arg(qRound(b * 255.0f))); + ColorPoint xy; + rgbToXYBrightness(color.red / 255.0f, color.green / 255.0f, color.blue / 255.0f, lamp, xy); + // Write color if color has been changed. + if (xy != lamp.color) { + // Send adjust color command in JSON format. + put(getStateRoute(lightId), QString("{\"xy\": [%1, %2]}").arg(xy.x).arg(xy.y)); + // Send brightness color command in JSON format. + put(getStateRoute(lightId), QString("{\"bri\": %1}").arg(qRound(xy.bri * 255.0f))); + // Remember written color. + lamp.color = xy; + } // Next light id. lightId++; } @@ -98,31 +102,9 @@ QString LedDevicePhilipsHue::getRoute(unsigned int lightId) { return QString("lights/%1").arg(lightId); } -CGTriangle LedDevicePhilipsHue::getTriangle(QString modelId) { - const std::set HUE_BULBS_MODEL_IDS = { "LCT001", "LCT002", "LCT003" }; - const std::set LIVING_COLORS_MODEL_IDS = { "LLC001", "LLC005", "LLC006", "LLC007", "LLC011", "LLC012", - "LLC013", "LST001" }; - CGTriangle triangle; - if (HUE_BULBS_MODEL_IDS.find(modelId) != HUE_BULBS_MODEL_IDS.end()) { - triangle.red = {0.675f, 0.322f}; - triangle.green = {0.4091f, 0.518f}; - triangle.blue = {0.167f, 0.04f}; - } else if (LIVING_COLORS_MODEL_IDS.find(modelId) != LIVING_COLORS_MODEL_IDS.end()) { - triangle.red = {0.703f, 0.296f}; - triangle.green = {0.214f, 0.709f}; - triangle.blue = {0.139f, 0.081f}; - } else { - triangle.red = {1.0f, 0.0f}; - triangle.green = {0.0f, 1.0f}; - triangle.blue = {0.0f, 0.0f}; - } - return triangle; -} - void LedDevicePhilipsHue::saveStates(unsigned int nLights) { - // Clear saved light states. - states.clear(); - triangles.clear(); + // Clear saved lamps. + lamps.clear(); // Use json parser to parse reponse. Json::Reader reader; Json::FastWriter writer; @@ -136,50 +118,48 @@ void LedDevicePhilipsHue::saveStates(unsigned int nLights) { // Error occured, break loop. break; } - // Save state object values which are subject to change. + // Get state object values which are subject to change. Json::Value state(Json::objectValue); state["on"] = json["state"]["on"]; if (json["state"]["on"] == true) { state["xy"] = json["state"]["xy"]; state["bri"] = json["state"]["bri"]; } - // Save state object. - states.push_back(QString(writer.write(state).c_str()).trimmed()); - // Determine triangle. + // Determine the model id. QString modelId = QString(writer.write(json["modelid"]).c_str()).trimmed().replace("\"", ""); - triangles.push_back(getTriangle(modelId)); + QString originalState = QString(writer.write(state).c_str()).trimmed(); + // Save state object. + lamps.push_back(HueLamp(i + 1, originalState, modelId)); } } void LedDevicePhilipsHue::switchOn(unsigned int nLights) { - for (unsigned int i = 0; i < nLights; i++) { - put(getStateRoute(i + 1), "{\"on\": true}"); + for (HueLamp lamp : lamps) { + put(getStateRoute(lamp.id), "{\"on\": true}"); } } void LedDevicePhilipsHue::restoreStates() { - unsigned int lightId = 1; - for (QString state : states) { - put(getStateRoute(lightId), state); - lightId++; + for (HueLamp lamp : lamps) { + put(getStateRoute(lamp.id), lamp.originalState); } // Clear saved light states. - states.clear(); - triangles.clear(); + lamps.clear(); } bool LedDevicePhilipsHue::areStatesSaved() { - return !states.empty(); + return !lamps.empty(); } -float LedDevicePhilipsHue::crossProduct(CGPoint p1, CGPoint p2) { +float LedDevicePhilipsHue::crossProduct(ColorPoint p1, ColorPoint p2) { return p1.x * p2.y - p1.y * p2.x; } -bool LedDevicePhilipsHue::isPointInLampsReach(CGTriangle triangle, CGPoint p) { - CGPoint v1 = { triangle.green.x - triangle.red.x, triangle.green.y - triangle.red.y }; - CGPoint v2 = { triangle.blue.x - triangle.red.x, triangle.blue.y - triangle.red.y }; - CGPoint q = { p.x - triangle.red.x, p.y - triangle.red.y }; +bool LedDevicePhilipsHue::isPointInLampsReach(HueLamp lamp, ColorPoint p) { + ColorTriangle& triangle = lamp.colorSpace; + ColorPoint v1 = { triangle.green.x - triangle.red.x, triangle.green.y - triangle.red.y }; + ColorPoint v2 = { triangle.blue.x - triangle.red.x, triangle.blue.y - triangle.red.y }; + ColorPoint q = { p.x - triangle.red.x, p.y - triangle.red.y }; float s = crossProduct(q, v2) / crossProduct(v1, v2); float t = crossProduct(v1, q) / crossProduct(v1, v2); if ((s >= 0.0f) && (t >= 0.0f) && (s + t <= 1.0f)) { @@ -189,9 +169,9 @@ bool LedDevicePhilipsHue::isPointInLampsReach(CGTriangle triangle, CGPoint p) { } } -CGPoint LedDevicePhilipsHue::getClosestPointToPoint(CGPoint A, CGPoint B, CGPoint P) { - CGPoint AP = { P.x - A.x, P.y - A.y }; - CGPoint AB = { B.x - A.x, B.y - A.y }; +ColorPoint LedDevicePhilipsHue::getClosestPointToPoint(ColorPoint a, ColorPoint b, ColorPoint p) { + ColorPoint AP = { p.x - a.x, p.y - a.y }; + ColorPoint AB = { b.x - a.x, b.y - a.y }; float ab2 = AB.x * AB.x + AB.y * AB.y; float ap_ab = AP.x * AB.x + AP.y * AB.y; float t = ap_ab / ab2; @@ -200,20 +180,19 @@ CGPoint LedDevicePhilipsHue::getClosestPointToPoint(CGPoint A, CGPoint B, CGPoin } else if (t > 1.0f) { t = 1.0f; } - return {A.x + AB.x * t, A.y + AB.y * t}; + return {a.x + AB.x * t, a.y + AB.y * t}; } -float LedDevicePhilipsHue::getDistanceBetweenTwoPoints(CGPoint one, CGPoint two) { +float LedDevicePhilipsHue::getDistanceBetweenTwoPoints(ColorPoint p1, ColorPoint p2) { // Horizontal difference. - float dx = one.x - two.x; + float dx = p1.x - p2.x; // Vertical difference. - float dy = one.y - two.y; - float dist = sqrt(dx * dx + dy * dy); - return dist; + float dy = p1.y - p2.y; + // Absolute value. + return sqrt(dx * dx + dy * dy); } -void LedDevicePhilipsHue::rgbToXYBrightness(float red, float green, float blue, CGTriangle triangle, CGPoint& xyPoint, - float& brightness) { +void LedDevicePhilipsHue::rgbToXYBrightness(float red, float green, float blue, HueLamp lamp, ColorPoint& xy) { // Apply gamma correction. float r = (red > 0.04045f) ? powf((red + 0.055f) / (1.0f + 0.055f), 2.4f) : (red / 12.92f); float g = (green > 0.04045f) ? powf((green + 0.055f) / (1.0f + 0.055f), 2.4f) : (green / 12.92f); @@ -231,20 +210,20 @@ void LedDevicePhilipsHue::rgbToXYBrightness(float red, float green, float blue, if (isnan(cy)) { cy = 0.0f; } - xyPoint.x = cx; - xyPoint.y = cy; - // Check if the given XY value is within the colourreach of our lamps. - if (!isPointInLampsReach(triangle, xyPoint)) { - // It seems the colour is out of reach let's find the closes colour we can produce with our lamp and send this XY value out. - CGPoint pAB = getClosestPointToPoint(triangle.red, triangle.green, xyPoint); - CGPoint pAC = getClosestPointToPoint(triangle.blue, triangle.red, xyPoint); - CGPoint pBC = getClosestPointToPoint(triangle.green, triangle.blue, xyPoint); + xy.x = cx; + xy.y = cy; + // Check if the given XY value is within the color reach of our lamps. + if (!isPointInLampsReach(lamp, xy)) { + // It seems the color is out of reach let's find the closes colour we can produce with our lamp and send this XY value out. + ColorPoint pAB = getClosestPointToPoint(lamp.colorSpace.red, lamp.colorSpace.green, xy); + ColorPoint pAC = getClosestPointToPoint(lamp.colorSpace.blue, lamp.colorSpace.red, xy); + ColorPoint pBC = getClosestPointToPoint(lamp.colorSpace.green, lamp.colorSpace.blue, xy); // Get the distances per point and see which point is closer to our Point. - float dAB = getDistanceBetweenTwoPoints(xyPoint, pAB); - float dAC = getDistanceBetweenTwoPoints(xyPoint, pAC); - float dBC = getDistanceBetweenTwoPoints(xyPoint, pBC); + float dAB = getDistanceBetweenTwoPoints(xy, pAB); + float dAC = getDistanceBetweenTwoPoints(xy, pAC); + float dBC = getDistanceBetweenTwoPoints(xy, pBC); float lowest = dAB; - CGPoint closestPoint = pAB; + ColorPoint closestPoint = pAB; if (dAC < lowest) { lowest = dAC; closestPoint = pAC; @@ -254,9 +233,41 @@ void LedDevicePhilipsHue::rgbToXYBrightness(float red, float green, float blue, closestPoint = pBC; } // Change the xy value to a value which is within the reach of the lamp. - xyPoint.x = closestPoint.x; - xyPoint.y = closestPoint.y; + xy.x = closestPoint.x; + xy.y = closestPoint.y; } // Brightness is simply Y in the XYZ space. - brightness = Y; + xy.bri = Y; +} + +HueLamp::HueLamp(unsigned int id, QString originalState, QString modelId) : + id(id), originalState(originalState) { + /// Hue system model ids. + const std::set HUE_BULBS_MODEL_IDS = { "LCT001", "LCT002", "LCT003" }; + const std::set LIVING_COLORS_MODEL_IDS = { "LLC001", "LLC005", "LLC006", "LLC007", "LLC011", "LLC012", + "LLC013", "LST001" }; + /// Find id in the sets and set the appropiate color space. + if (HUE_BULBS_MODEL_IDS.find(modelId) != HUE_BULBS_MODEL_IDS.end()) { + colorSpace.red = {0.675f, 0.322f}; + colorSpace.green = {0.4091f, 0.518f}; + colorSpace.blue = {0.167f, 0.04f}; + } else if (LIVING_COLORS_MODEL_IDS.find(modelId) != LIVING_COLORS_MODEL_IDS.end()) { + colorSpace.red = {0.703f, 0.296f}; + colorSpace.green = {0.214f, 0.709f}; + colorSpace.blue = {0.139f, 0.081f}; + } else { + colorSpace.red = {1.0f, 0.0f}; + colorSpace.green = {0.0f, 1.0f}; + colorSpace.blue = {0.0f, 0.0f}; + } + /// Initialize color with black + color = {0.0f, 0.0f, 0.0f}; +} + +bool operator ==(ColorPoint p1, ColorPoint p2) { + return (p1.x == p2.x) && (p1.y == p2.y) && (p1.bri == p2.bri); +} + +bool operator !=(ColorPoint p1, ColorPoint p2) { + return !(p1 == p2); } diff --git a/libsrc/leddevice/LedDevicePhilipsHue.h b/libsrc/leddevice/LedDevicePhilipsHue.h index 81781db1..219cce85 100755 --- a/libsrc/leddevice/LedDevicePhilipsHue.h +++ b/libsrc/leddevice/LedDevicePhilipsHue.h @@ -12,13 +12,27 @@ // Leddevice includes #include -struct CGPoint { +struct ColorPoint { float x; float y; + float bri; }; -struct CGTriangle { - CGPoint red, green, blue; +bool operator==(ColorPoint p1, ColorPoint p2); +bool operator!=(ColorPoint p1, ColorPoint p2); + +struct ColorTriangle { + ColorPoint red, green, blue; +}; + +class HueLamp { +public: + unsigned int id; + ColorPoint color; + ColorTriangle colorSpace; + QString originalState; + + HueLamp(unsigned int id, QString originalState, QString modelId); }; /** @@ -63,10 +77,8 @@ private slots: void restoreStates(); private: - /// Array to save the light states. - std::vector states; - /// Color triangles. - std::vector triangles; + /// Array to save the lamps. + std::vector lamps; /// Ip address of the bridge QString host; /// User name for the API ("newdeveloper") @@ -141,12 +153,11 @@ private: /// /// @param brightness converted brightness component /// - void rgbToXYBrightness(float red, float green, float blue, CGTriangle triangle, CGPoint& xyPoint, float& brightness); + void rgbToXYBrightness(float red, float green, float blue, HueLamp lamp, ColorPoint& xy); - CGTriangle getTriangle(QString modelId); - float crossProduct(CGPoint p1, CGPoint p2); - bool isPointInLampsReach(CGTriangle triangle, CGPoint p); - CGPoint getClosestPointToPoint(CGPoint a, CGPoint b, CGPoint p); - float getDistanceBetweenTwoPoints(CGPoint one, CGPoint two); + float crossProduct(ColorPoint p1, ColorPoint p2); + bool isPointInLampsReach(HueLamp lamp, ColorPoint p); + ColorPoint getClosestPointToPoint(ColorPoint a, ColorPoint b, ColorPoint p); + float getDistanceBetweenTwoPoints(ColorPoint one, ColorPoint two); }; From 4af2b11d8fa846c19344685e93175d406574db5e Mon Sep 17 00:00:00 2001 From: Tim Niggemann Date: Tue, 15 Jul 2014 09:58:29 +0200 Subject: [PATCH 31/74] Get the light id from the lamp object. Former-commit-id: dc7aa992386c2511261614a2a8fe3ccf15d9a591 --- libsrc/leddevice/LedDevicePhilipsHue.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libsrc/leddevice/LedDevicePhilipsHue.cpp b/libsrc/leddevice/LedDevicePhilipsHue.cpp index a678480d..a78c7fca 100755 --- a/libsrc/leddevice/LedDevicePhilipsHue.cpp +++ b/libsrc/leddevice/LedDevicePhilipsHue.cpp @@ -41,9 +41,9 @@ int LedDevicePhilipsHue::write(const std::vector & ledValues) { // Write color if color has been changed. if (xy != lamp.color) { // Send adjust color command in JSON format. - put(getStateRoute(lightId), QString("{\"xy\": [%1, %2]}").arg(xy.x).arg(xy.y)); + put(getStateRoute(lamp.id), QString("{\"xy\": [%1, %2]}").arg(xy.x).arg(xy.y)); // Send brightness color command in JSON format. - put(getStateRoute(lightId), QString("{\"bri\": %1}").arg(qRound(xy.bri * 255.0f))); + put(getStateRoute(lamp.id), QString("{\"bri\": %1}").arg(qRound(xy.bri * 255.0f))); // Remember written color. lamp.color = xy; } From d2542142be8d37c3d290bd5b41e351206448dc36 Mon Sep 17 00:00:00 2001 From: Tim Niggemann Date: Tue, 15 Jul 2014 09:59:01 +0200 Subject: [PATCH 32/74] Get the light id from the lamp object. Former-commit-id: 7120af8551c185979c94b2a186f09c883784a882 --- libsrc/leddevice/LedDevicePhilipsHue.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libsrc/leddevice/LedDevicePhilipsHue.cpp b/libsrc/leddevice/LedDevicePhilipsHue.cpp index a78c7fca..94b6bdab 100755 --- a/libsrc/leddevice/LedDevicePhilipsHue.cpp +++ b/libsrc/leddevice/LedDevicePhilipsHue.cpp @@ -31,10 +31,10 @@ int LedDevicePhilipsHue::write(const std::vector & ledValues) { switchOn((unsigned int) ledValues.size()); } // Iterate through colors and set light states. - unsigned int lightId = 0; + unsigned int idx = 0; for (const ColorRgb& color : ledValues) { // Get lamp. - HueLamp& lamp = lamps.at(lightId); + HueLamp& lamp = lamps.at(idx); // Scale colors from [0, 255] to [0, 1] and convert to xy space. ColorPoint xy; rgbToXYBrightness(color.red / 255.0f, color.green / 255.0f, color.blue / 255.0f, lamp, xy); @@ -48,7 +48,7 @@ int LedDevicePhilipsHue::write(const std::vector & ledValues) { lamp.color = xy; } // Next light id. - lightId++; + idx++; } timer.start(); return 0; From 7e049273a84afcf808e510c4847e0e79c1fa16c2 Mon Sep 17 00:00:00 2001 From: Tim Niggemann Date: Tue, 15 Jul 2014 12:06:26 +0200 Subject: [PATCH 33/74] Comments. Former-commit-id: bb4573afa8072bf03a3ae7c1b8ece721c7ea91ff --- libsrc/leddevice/LedDevicePhilipsHue.cpp | 18 ++++---- libsrc/leddevice/LedDevicePhilipsHue.h | 59 ++++++++++++++++++++++-- 2 files changed, 64 insertions(+), 13 deletions(-) diff --git a/libsrc/leddevice/LedDevicePhilipsHue.cpp b/libsrc/leddevice/LedDevicePhilipsHue.cpp index 94b6bdab..fcb6b2a8 100755 --- a/libsrc/leddevice/LedDevicePhilipsHue.cpp +++ b/libsrc/leddevice/LedDevicePhilipsHue.cpp @@ -36,8 +36,7 @@ int LedDevicePhilipsHue::write(const std::vector & ledValues) { // Get lamp. HueLamp& lamp = lamps.at(idx); // Scale colors from [0, 255] to [0, 1] and convert to xy space. - ColorPoint xy; - rgbToXYBrightness(color.red / 255.0f, color.green / 255.0f, color.blue / 255.0f, lamp, xy); + ColorPoint xy = rgbToXYBrightness(color.red / 255.0f, color.green / 255.0f, color.blue / 255.0f, lamp); // Write color if color has been changed. if (xy != lamp.color) { // Send adjust color command in JSON format. @@ -192,7 +191,7 @@ float LedDevicePhilipsHue::getDistanceBetweenTwoPoints(ColorPoint p1, ColorPoint return sqrt(dx * dx + dy * dy); } -void LedDevicePhilipsHue::rgbToXYBrightness(float red, float green, float blue, HueLamp lamp, ColorPoint& xy) { +ColorPoint LedDevicePhilipsHue::rgbToXYBrightness(float red, float green, float blue, HueLamp lamp) { // Apply gamma correction. float r = (red > 0.04045f) ? powf((red + 0.055f) / (1.0f + 0.055f), 2.4f) : (red / 12.92f); float g = (green > 0.04045f) ? powf((green + 0.055f) / (1.0f + 0.055f), 2.4f) : (green / 12.92f); @@ -202,16 +201,15 @@ void LedDevicePhilipsHue::rgbToXYBrightness(float red, float green, float blue, float Y = r * 0.234327f + g * 0.743075f + b * 0.022598f; float Z = r * 0.0000000f + g * 0.053077f + b * 1.035763f; // Convert to x,y space. - float cx = X / (X + Y + Z + 0.0000001f); - float cy = Y / (X + Y + Z + 0.0000001f); + float cx = X / (X + Y + Z); + float cy = Y / (X + Y + Z); if (isnan(cx)) { cx = 0.0f; } if (isnan(cy)) { cy = 0.0f; } - xy.x = cx; - xy.y = cy; + ColorPoint xy = {cx, cy}; // Check if the given XY value is within the color reach of our lamps. if (!isPointInLampsReach(lamp, xy)) { // It seems the color is out of reach let's find the closes colour we can produce with our lamp and send this XY value out. @@ -242,11 +240,11 @@ void LedDevicePhilipsHue::rgbToXYBrightness(float red, float green, float blue, HueLamp::HueLamp(unsigned int id, QString originalState, QString modelId) : id(id), originalState(originalState) { - /// Hue system model ids. + // Hue system model ids. const std::set HUE_BULBS_MODEL_IDS = { "LCT001", "LCT002", "LCT003" }; const std::set LIVING_COLORS_MODEL_IDS = { "LLC001", "LLC005", "LLC006", "LLC007", "LLC011", "LLC012", "LLC013", "LST001" }; - /// Find id in the sets and set the appropiate color space. + // Find id in the sets and set the appropiate color space. if (HUE_BULBS_MODEL_IDS.find(modelId) != HUE_BULBS_MODEL_IDS.end()) { colorSpace.red = {0.675f, 0.322f}; colorSpace.green = {0.4091f, 0.518f}; @@ -260,7 +258,7 @@ HueLamp::HueLamp(unsigned int id, QString originalState, QString modelId) : colorSpace.green = {0.0f, 1.0f}; colorSpace.blue = {0.0f, 0.0f}; } - /// Initialize color with black + // Initialize color with black color = {0.0f, 0.0f, 0.0f}; } diff --git a/libsrc/leddevice/LedDevicePhilipsHue.h b/libsrc/leddevice/LedDevicePhilipsHue.h index 219cce85..59d86594 100755 --- a/libsrc/leddevice/LedDevicePhilipsHue.h +++ b/libsrc/leddevice/LedDevicePhilipsHue.h @@ -12,6 +12,9 @@ // Leddevice includes #include +/** + * A color point in the color space of the hue system. + */ struct ColorPoint { float x; float y; @@ -21,10 +24,16 @@ struct ColorPoint { bool operator==(ColorPoint p1, ColorPoint p2); bool operator!=(ColorPoint p1, ColorPoint p2); +/** + * Color triangle to define an available color space for the hue lamps. + */ struct ColorTriangle { ColorPoint red, green, blue; }; +/** + * Simple class to hold the id, the latest color, the color space and the original state. + */ class HueLamp { public: unsigned int id; @@ -32,6 +41,15 @@ public: ColorTriangle colorSpace; QString originalState; + /// + /// Constructs the lamp. + /// + /// @param id the light id + /// + /// @param originalState the json string of the original state + /// + /// @param modelId the model id of the hue lamp which is used to determine the color space + /// HueLamp(unsigned int id, QString originalState, QString modelId); }; @@ -149,15 +167,50 @@ private: /// /// @param blue the blue component in [0, 1] /// - /// @param xyPoint converted xy component + /// @param lamp the hue lamp instance used for color space checks. /// - /// @param brightness converted brightness component + /// @return color point /// - void rgbToXYBrightness(float red, float green, float blue, HueLamp lamp, ColorPoint& xy); + ColorPoint rgbToXYBrightness(float red, float green, float blue, HueLamp lamp); + /// + /// @param p1 point one + /// + /// @param p2 point tow + /// + /// @return the cross product between p1 and p2 + /// float crossProduct(ColorPoint p1, ColorPoint p2); + + + /// + /// @param lamp the hue lamp instance + /// + /// @param p the color point to check + /// + /// @return true if the color point is covered by the lamp color space + /// bool isPointInLampsReach(HueLamp lamp, ColorPoint p); + + + /// + /// @param a reference point one + /// + /// @param b reference point two + /// + /// @param p the point to which the closest point is to be found + /// + /// @return the closest color point of p to a and b + /// ColorPoint getClosestPointToPoint(ColorPoint a, ColorPoint b, ColorPoint p); + + /// + /// @param p1 point one + /// + /// @param p2 point tow + /// + /// @return the distance between the two points + /// float getDistanceBetweenTwoPoints(ColorPoint one, ColorPoint two); }; From fcb2ff66671fcb9b8a800e62a7da13ef608ed012 Mon Sep 17 00:00:00 2001 From: Tim Niggemann Date: Tue, 15 Jul 2014 12:08:27 +0200 Subject: [PATCH 34/74] Renamed method parameters. Former-commit-id: e10705dd6d93f5c1398a213583bbe349833d2648 --- libsrc/leddevice/LedDevicePhilipsHue.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libsrc/leddevice/LedDevicePhilipsHue.h b/libsrc/leddevice/LedDevicePhilipsHue.h index 59d86594..d60c2b32 100755 --- a/libsrc/leddevice/LedDevicePhilipsHue.h +++ b/libsrc/leddevice/LedDevicePhilipsHue.h @@ -158,7 +158,7 @@ private: bool areStatesSaved(); /// - /// Converts an RGB color to the Hue xy color space and brightness + /// Converts an RGB color to the Hue xy color space and brightness. /// https://github.com/PhilipsHue/PhilipsHueSDK-iOS-OSX/blob/master/ApplicationDesignNotes/RGB%20to%20xy%20Color%20conversion.md /// /// @param red the red component in [0, 1] @@ -211,6 +211,6 @@ private: /// /// @return the distance between the two points /// - float getDistanceBetweenTwoPoints(ColorPoint one, ColorPoint two); + float getDistanceBetweenTwoPoints(ColorPoint p1, ColorPoint p2); }; From b055578759bb7a1f81f08b94c7a6cf8fca90aa31 Mon Sep 17 00:00:00 2001 From: Tim Niggemann Date: Wed, 16 Jul 2014 11:49:34 +0200 Subject: [PATCH 35/74] Added config switch for turing off the lamps if the color black is written. Former-commit-id: bb4f4bc74c035c10a8dc678a11052ea276ea0149 --- libsrc/leddevice/LedDeviceFactory.cpp | 3 ++- libsrc/leddevice/LedDevicePhilipsHue.cpp | 23 ++++++++++++++++------- libsrc/leddevice/LedDevicePhilipsHue.h | 7 ++++++- 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/libsrc/leddevice/LedDeviceFactory.cpp b/libsrc/leddevice/LedDeviceFactory.cpp index 76eb0137..5a1bd123 100755 --- a/libsrc/leddevice/LedDeviceFactory.cpp +++ b/libsrc/leddevice/LedDeviceFactory.cpp @@ -164,7 +164,8 @@ LedDevice * LedDeviceFactory::construct(const Json::Value & deviceConfig) else if (type == "philipshue") { const std::string output = deviceConfig["output"].asString(); - device = new LedDevicePhilipsHue(output); + const bool switchOffOnBlack = deviceConfig.get("switch_off_on_black", false).asBool(); + device = new LedDevicePhilipsHue(output, switchOffOnBlack); } else if (type == "test") { diff --git a/libsrc/leddevice/LedDevicePhilipsHue.cpp b/libsrc/leddevice/LedDevicePhilipsHue.cpp index fcb6b2a8..beda2d71 100755 --- a/libsrc/leddevice/LedDevicePhilipsHue.cpp +++ b/libsrc/leddevice/LedDevicePhilipsHue.cpp @@ -12,8 +12,10 @@ #include -LedDevicePhilipsHue::LedDevicePhilipsHue(const std::string& output) : - host(output.c_str()), username("newdeveloper") { +const ColorPoint LedDevicePhilipsHue::BLACK = {0.0f, 0.0f, 0.0f}; + +LedDevicePhilipsHue::LedDevicePhilipsHue(const std::string& output, bool switchOffOnBlack) : + host(output.c_str()), username("newdeveloper"), switchOffOnBlack(switchOffOnBlack) { http = new QHttp(host); timer.setInterval(3000); timer.setSingleShot(true); @@ -37,12 +39,19 @@ int LedDevicePhilipsHue::write(const std::vector & ledValues) { HueLamp& lamp = lamps.at(idx); // Scale colors from [0, 255] to [0, 1] and convert to xy space. ColorPoint xy = rgbToXYBrightness(color.red / 255.0f, color.green / 255.0f, color.blue / 255.0f, lamp); + // Switch lamp off if switchOffOnBlack is enabled and the lamp is currently on. + if (switchOffOnBlack && xy == BLACK && lamp.color != BLACK) { + put(getStateRoute(lamp.id), QString("{\"on\": false}")); + } // Write color if color has been changed. - if (xy != lamp.color) { - // Send adjust color command in JSON format. - put(getStateRoute(lamp.id), QString("{\"xy\": [%1, %2]}").arg(xy.x).arg(xy.y)); - // Send brightness color command in JSON format. - put(getStateRoute(lamp.id), QString("{\"bri\": %1}").arg(qRound(xy.bri * 255.0f))); + else if (xy != lamp.color) { + // Switch on if the lamp has been previously switched off. + if (switchOffOnBlack && lamp.color == BLACK) { + put(getStateRoute(lamp.id), QString("{\"on\": true}")); + } + // Send adjust color and brightness command in JSON format. + put(getStateRoute(lamp.id), QString("{\"xy\": [%1, %2], \"bri\": %1}").arg(xy.x).arg(xy.y) + .arg(qRound(xy.bri * 255.0f))); // Remember written color. lamp.color = xy; } diff --git a/libsrc/leddevice/LedDevicePhilipsHue.h b/libsrc/leddevice/LedDevicePhilipsHue.h index d60c2b32..ecbecf3a 100755 --- a/libsrc/leddevice/LedDevicePhilipsHue.h +++ b/libsrc/leddevice/LedDevicePhilipsHue.h @@ -71,7 +71,9 @@ public: /// /// @param output the ip address of the bridge /// - LedDevicePhilipsHue(const std::string& output); + /// @param switchOffOnBlack kill lights for black + /// + LedDevicePhilipsHue(const std::string& output, bool switchOffOnBlack); /// /// Destructor of this device @@ -95,6 +97,7 @@ private slots: void restoreStates(); private: + const static ColorPoint BLACK; /// Array to save the lamps. std::vector lamps; /// Ip address of the bridge @@ -105,6 +108,8 @@ private: QHttp* http; /// Use timer to reset lights when we got into "GRABBINGMODE_OFF". QTimer timer; + /// + bool switchOffOnBlack; /// /// Sends a HTTP GET request (blocking). From f76b5ffbd885b9190f7f637e40cf88f1cca5717e Mon Sep 17 00:00:00 2001 From: Tim Niggemann Date: Wed, 16 Jul 2014 16:21:11 +0200 Subject: [PATCH 36/74] Added author tag, added missing return statement. Former-commit-id: 4d0a29a8ba3d33de6f86b90a4eaf2f0de12ea59a --- libsrc/leddevice/LedDevicePhilipsHue.cpp | 16 ++++++++-------- libsrc/leddevice/LedDevicePhilipsHue.h | 4 +--- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/libsrc/leddevice/LedDevicePhilipsHue.cpp b/libsrc/leddevice/LedDevicePhilipsHue.cpp index beda2d71..3e960509 100755 --- a/libsrc/leddevice/LedDevicePhilipsHue.cpp +++ b/libsrc/leddevice/LedDevicePhilipsHue.cpp @@ -12,7 +12,7 @@ #include -const ColorPoint LedDevicePhilipsHue::BLACK = {0.0f, 0.0f, 0.0f}; +const ColorPoint LedDevicePhilipsHue::BLACK = { 0.0f, 0.0f, 0.0f }; LedDevicePhilipsHue::LedDevicePhilipsHue(const std::string& output, bool switchOffOnBlack) : host(output.c_str()), username("newdeveloper"), switchOffOnBlack(switchOffOnBlack) { @@ -42,7 +42,7 @@ int LedDevicePhilipsHue::write(const std::vector & ledValues) { // Switch lamp off if switchOffOnBlack is enabled and the lamp is currently on. if (switchOffOnBlack && xy == BLACK && lamp.color != BLACK) { put(getStateRoute(lamp.id), QString("{\"on\": false}")); - } + } // Write color if color has been changed. else if (xy != lamp.color) { // Switch on if the lamp has been previously switched off. @@ -50,8 +50,8 @@ int LedDevicePhilipsHue::write(const std::vector & ledValues) { put(getStateRoute(lamp.id), QString("{\"on\": true}")); } // Send adjust color and brightness command in JSON format. - put(getStateRoute(lamp.id), QString("{\"xy\": [%1, %2], \"bri\": %1}").arg(xy.x).arg(xy.y) - .arg(qRound(xy.bri * 255.0f))); + put(getStateRoute(lamp.id), + QString("{\"xy\": [%1, %2], \"bri\": %1}").arg(xy.x).arg(xy.y).arg(qRound(xy.bri * 255.0f))); // Remember written color. lamp.color = xy; } @@ -218,10 +218,11 @@ ColorPoint LedDevicePhilipsHue::rgbToXYBrightness(float red, float green, float if (isnan(cy)) { cy = 0.0f; } - ColorPoint xy = {cx, cy}; + // Brightness is simply Y in the XYZ space. + ColorPoint xy = { cx, cy, Y }; // Check if the given XY value is within the color reach of our lamps. if (!isPointInLampsReach(lamp, xy)) { - // It seems the color is out of reach let's find the closes colour we can produce with our lamp and send this XY value out. + // It seems the color is out of reach let's find the closes color we can produce with our lamp and send this XY value out. ColorPoint pAB = getClosestPointToPoint(lamp.colorSpace.red, lamp.colorSpace.green, xy); ColorPoint pAC = getClosestPointToPoint(lamp.colorSpace.blue, lamp.colorSpace.red, xy); ColorPoint pBC = getClosestPointToPoint(lamp.colorSpace.green, lamp.colorSpace.blue, xy); @@ -243,8 +244,7 @@ ColorPoint LedDevicePhilipsHue::rgbToXYBrightness(float red, float green, float xy.x = closestPoint.x; xy.y = closestPoint.y; } - // Brightness is simply Y in the XYZ space. - xy.bri = Y; + return xy; } HueLamp::HueLamp(unsigned int id, QString originalState, QString modelId) : diff --git a/libsrc/leddevice/LedDevicePhilipsHue.h b/libsrc/leddevice/LedDevicePhilipsHue.h index ecbecf3a..c3503705 100755 --- a/libsrc/leddevice/LedDevicePhilipsHue.h +++ b/libsrc/leddevice/LedDevicePhilipsHue.h @@ -61,7 +61,7 @@ public: * Framegrabber must be limited to 10 Hz / numer of lights to avoid rate limitation by the hue bridge. * Create a new API user name "newdeveloper" on the bridge (http://developers.meethue.com/gettingstarted.html) * - * @author ntim (github) + * @author ntim (github), bimsarck (github) */ class LedDevicePhilipsHue: public QObject, public LedDevice { Q_OBJECT @@ -187,7 +187,6 @@ private: /// float crossProduct(ColorPoint p1, ColorPoint p2); - /// /// @param lamp the hue lamp instance /// @@ -197,7 +196,6 @@ private: /// bool isPointInLampsReach(HueLamp lamp, ColorPoint p); - /// /// @param a reference point one /// From f0d2c15aeb7d077ede26cf684128c38a88cf10cf Mon Sep 17 00:00:00 2001 From: Tim Niggemann Date: Wed, 16 Jul 2014 16:45:45 +0200 Subject: [PATCH 37/74] Fixed QString string formatting, added safety check in case the connection to the bridge might be lost. Former-commit-id: fb1f5fd21cd3873fc1b92d763c682e75f345ab42 --- libsrc/leddevice/LedDevicePhilipsHue.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/libsrc/leddevice/LedDevicePhilipsHue.cpp b/libsrc/leddevice/LedDevicePhilipsHue.cpp index 3e960509..df2385ef 100755 --- a/libsrc/leddevice/LedDevicePhilipsHue.cpp +++ b/libsrc/leddevice/LedDevicePhilipsHue.cpp @@ -32,6 +32,11 @@ int LedDevicePhilipsHue::write(const std::vector & ledValues) { saveStates((unsigned int) ledValues.size()); switchOn((unsigned int) ledValues.size()); } + // If there are less states saved than colors given, then maybe something went wrong before. + if (lamps.size() != ledValues.size()) { + restoreStates(); + return 0; + } // Iterate through colors and set light states. unsigned int idx = 0; for (const ColorRgb& color : ledValues) { @@ -51,7 +56,7 @@ int LedDevicePhilipsHue::write(const std::vector & ledValues) { } // Send adjust color and brightness command in JSON format. put(getStateRoute(lamp.id), - QString("{\"xy\": [%1, %2], \"bri\": %1}").arg(xy.x).arg(xy.y).arg(qRound(xy.bri * 255.0f))); + QString("{\"xy\": [%1, %2], \"bri\": %3}").arg(xy.x).arg(xy.y).arg(qRound(xy.bri * 255.0f))); // Remember written color. lamp.color = xy; } From dbd7a86665705276e91072e17297716b79ad0f47 Mon Sep 17 00:00:00 2001 From: Tim Niggemann Date: Wed, 16 Jul 2014 20:22:37 +0200 Subject: [PATCH 38/74] Moved color logic to lamp class. Former-commit-id: f450eebc8c9d0f29dc053f2115dac6576a5fa591 --- libsrc/leddevice/LedDeviceFactory.cpp | 2 +- libsrc/leddevice/LedDevicePhilipsHue.cpp | 278 ++++++++++++----------- libsrc/leddevice/LedDevicePhilipsHue.h | 129 ++++++----- 3 files changed, 208 insertions(+), 201 deletions(-) diff --git a/libsrc/leddevice/LedDeviceFactory.cpp b/libsrc/leddevice/LedDeviceFactory.cpp index 5a1bd123..34178421 100755 --- a/libsrc/leddevice/LedDeviceFactory.cpp +++ b/libsrc/leddevice/LedDeviceFactory.cpp @@ -164,7 +164,7 @@ LedDevice * LedDeviceFactory::construct(const Json::Value & deviceConfig) else if (type == "philipshue") { const std::string output = deviceConfig["output"].asString(); - const bool switchOffOnBlack = deviceConfig.get("switch_off_on_black", false).asBool(); + const bool switchOffOnBlack = deviceConfig.get("switchOffOnBlack", true).asBool(); device = new LedDevicePhilipsHue(output, switchOffOnBlack); } else if (type == "test") diff --git a/libsrc/leddevice/LedDevicePhilipsHue.cpp b/libsrc/leddevice/LedDevicePhilipsHue.cpp index df2385ef..0a47a24c 100755 --- a/libsrc/leddevice/LedDevicePhilipsHue.cpp +++ b/libsrc/leddevice/LedDevicePhilipsHue.cpp @@ -12,7 +12,125 @@ #include -const ColorPoint LedDevicePhilipsHue::BLACK = { 0.0f, 0.0f, 0.0f }; +bool operator ==(CiColor p1, CiColor p2) { + return (p1.x == p2.x) && (p1.y == p2.y) && (p1.bri == p2.bri); +} + +bool operator !=(CiColor p1, CiColor p2) { + return !(p1 == p2); +} + +PhilipsHueLamp::PhilipsHueLamp(unsigned int id, QString originalState, QString modelId) : + id(id), originalState(originalState) { + // Hue system model ids. + const std::set HUE_BULBS_MODEL_IDS = { "LCT001", "LCT002", "LCT003" }; + const std::set LIVING_COLORS_MODEL_IDS = { "LLC001", "LLC005", "LLC006", "LLC007", "LLC011", "LLC012", + "LLC013", "LST001" }; + // Find id in the sets and set the appropiate color space. + if (HUE_BULBS_MODEL_IDS.find(modelId) != HUE_BULBS_MODEL_IDS.end()) { + colorSpace.red = {0.675f, 0.322f}; + colorSpace.green = {0.4091f, 0.518f}; + colorSpace.blue = {0.167f, 0.04f}; + } else if (LIVING_COLORS_MODEL_IDS.find(modelId) != LIVING_COLORS_MODEL_IDS.end()) { + colorSpace.red = {0.703f, 0.296f}; + colorSpace.green = {0.214f, 0.709f}; + colorSpace.blue = {0.139f, 0.081f}; + } else { + colorSpace.red = {1.0f, 0.0f}; + colorSpace.green = {0.0f, 1.0f}; + colorSpace.blue = {0.0f, 0.0f}; + } + // Initialize black color. + black = rgbToCiColor(0.0f, 0.0f, 0.0f); + // Initialize color with black + color = {black.x, black.y, black.bri}; +} + +float PhilipsHueLamp::crossProduct(CiColor p1, CiColor p2) { + return p1.x * p2.y - p1.y * p2.x; +} + +bool PhilipsHueLamp::isPointInLampsReach(CiColor p) { + CiColor v1 = { colorSpace.green.x - colorSpace.red.x, colorSpace.green.y - colorSpace.red.y }; + CiColor v2 = { colorSpace.blue.x - colorSpace.red.x, colorSpace.blue.y - colorSpace.red.y }; + CiColor q = { p.x - colorSpace.red.x, p.y - colorSpace.red.y }; + float s = crossProduct(q, v2) / crossProduct(v1, v2); + float t = crossProduct(v1, q) / crossProduct(v1, v2); + if ((s >= 0.0f) && (t >= 0.0f) && (s + t <= 1.0f)) { + return true; + } + return false; +} + +CiColor PhilipsHueLamp::getClosestPointToPoint(CiColor a, CiColor b, CiColor p) { + CiColor AP = { p.x - a.x, p.y - a.y }; + CiColor AB = { b.x - a.x, b.y - a.y }; + float ab2 = AB.x * AB.x + AB.y * AB.y; + float ap_ab = AP.x * AB.x + AP.y * AB.y; + float t = ap_ab / ab2; + if (t < 0.0f) { + t = 0.0f; + } else if (t > 1.0f) { + t = 1.0f; + } + return {a.x + AB.x * t, a.y + AB.y * t}; +} + +float PhilipsHueLamp::getDistanceBetweenTwoPoints(CiColor p1, CiColor p2) { + // Horizontal difference. + float dx = p1.x - p2.x; + // Vertical difference. + float dy = p1.y - p2.y; + // Absolute value. + return sqrt(dx * dx + dy * dy); +} + +CiColor PhilipsHueLamp::rgbToCiColor(float red, float green, float blue) { + // Apply gamma correction. + float r = (red > 0.04045f) ? powf((red + 0.055f) / (1.0f + 0.055f), 2.4f) : (red / 12.92f); + float g = (green > 0.04045f) ? powf((green + 0.055f) / (1.0f + 0.055f), 2.4f) : (green / 12.92f); + float b = (blue > 0.04045f) ? powf((blue + 0.055f) / (1.0f + 0.055f), 2.4f) : (blue / 12.92f); + // Convert to XYZ space. + float X = r * 0.649926f + g * 0.103455f + b * 0.197109f; + float Y = r * 0.234327f + g * 0.743075f + b * 0.022598f; + float Z = r * 0.0000000f + g * 0.053077f + b * 1.035763f; + // Convert to x,y space. + float cx = X / (X + Y + Z); + float cy = Y / (X + Y + Z); + if (isnan(cx)) { + cx = 0.0f; + } + if (isnan(cy)) { + cy = 0.0f; + } + // Brightness is simply Y in the XYZ space. + CiColor xy = { cx, cy, Y }; + // Check if the given XY value is within the color reach of our lamps. + if (!isPointInLampsReach(xy)) { + // It seems the color is out of reach let's find the closes color we can produce with our lamp and send this XY value out. + CiColor pAB = getClosestPointToPoint(colorSpace.red, colorSpace.green, xy); + CiColor pAC = getClosestPointToPoint(colorSpace.blue, colorSpace.red, xy); + CiColor pBC = getClosestPointToPoint(colorSpace.green, colorSpace.blue, xy); + // Get the distances per point and see which point is closer to our Point. + float dAB = getDistanceBetweenTwoPoints(xy, pAB); + float dAC = getDistanceBetweenTwoPoints(xy, pAC); + float dBC = getDistanceBetweenTwoPoints(xy, pBC); + float lowest = dAB; + CiColor closestPoint = pAB; + if (dAC < lowest) { + lowest = dAC; + closestPoint = pAC; + } + if (dBC < lowest) { + lowest = dBC; + closestPoint = pBC; + } + // Change the xy value to a value which is within the reach of the lamp. + xy.x = closestPoint.x; + xy.y = closestPoint.y; + } + return xy; +} LedDevicePhilipsHue::LedDevicePhilipsHue(const std::string& output, bool switchOffOnBlack) : host(output.c_str()), username("newdeveloper"), switchOffOnBlack(switchOffOnBlack) { @@ -41,25 +159,35 @@ int LedDevicePhilipsHue::write(const std::vector & ledValues) { unsigned int idx = 0; for (const ColorRgb& color : ledValues) { // Get lamp. - HueLamp& lamp = lamps.at(idx); + PhilipsHueLamp& lamp = lamps.at(idx); // Scale colors from [0, 255] to [0, 1] and convert to xy space. - ColorPoint xy = rgbToXYBrightness(color.red / 255.0f, color.green / 255.0f, color.blue / 255.0f, lamp); - // Switch lamp off if switchOffOnBlack is enabled and the lamp is currently on. - if (switchOffOnBlack && xy == BLACK && lamp.color != BLACK) { - put(getStateRoute(lamp.id), QString("{\"on\": false}")); - } + CiColor xy = lamp.rgbToCiColor(color.red / 255.0f, color.green / 255.0f, color.blue / 255.0f); // Write color if color has been changed. - else if (xy != lamp.color) { + if (xy != lamp.color) { // Switch on if the lamp has been previously switched off. - if (switchOffOnBlack && lamp.color == BLACK) { - put(getStateRoute(lamp.id), QString("{\"on\": true}")); + if (switchOffOnBlack && lamp.color == lamp.black) { + } // Send adjust color and brightness command in JSON format. put(getStateRoute(lamp.id), QString("{\"xy\": [%1, %2], \"bri\": %3}").arg(xy.x).arg(xy.y).arg(qRound(xy.bri * 255.0f))); - // Remember written color. - lamp.color = xy; + } + // Switch lamp off if switchOffOnBlack is enabled and the lamp is currently on. + if (switchOffOnBlack) { + // From black to a color. + if (lamp.color == lamp.black && xy != lamp.black) { + put(getStateRoute(lamp.id), QString("{\"on\": true}")); + std::cout << "switchon" << std::endl; + } + // From a color to black. + else if (lamp.color != lamp.black && xy == lamp.black) { + put(getStateRoute(lamp.id), QString("{\"on\": false}")); + std::cout << "switchoff" << std::endl; + } + } + // Remember last color. + lamp.color = xy; // Next light id. idx++; } @@ -142,18 +270,18 @@ void LedDevicePhilipsHue::saveStates(unsigned int nLights) { QString modelId = QString(writer.write(json["modelid"]).c_str()).trimmed().replace("\"", ""); QString originalState = QString(writer.write(state).c_str()).trimmed(); // Save state object. - lamps.push_back(HueLamp(i + 1, originalState, modelId)); + lamps.push_back(PhilipsHueLamp(i + 1, originalState, modelId)); } } void LedDevicePhilipsHue::switchOn(unsigned int nLights) { - for (HueLamp lamp : lamps) { + for (PhilipsHueLamp lamp : lamps) { put(getStateRoute(lamp.id), "{\"on\": true}"); } } void LedDevicePhilipsHue::restoreStates() { - for (HueLamp lamp : lamps) { + for (PhilipsHueLamp lamp : lamps) { put(getStateRoute(lamp.id), lamp.originalState); } // Clear saved light states. @@ -163,123 +291,3 @@ void LedDevicePhilipsHue::restoreStates() { bool LedDevicePhilipsHue::areStatesSaved() { return !lamps.empty(); } - -float LedDevicePhilipsHue::crossProduct(ColorPoint p1, ColorPoint p2) { - return p1.x * p2.y - p1.y * p2.x; -} - -bool LedDevicePhilipsHue::isPointInLampsReach(HueLamp lamp, ColorPoint p) { - ColorTriangle& triangle = lamp.colorSpace; - ColorPoint v1 = { triangle.green.x - triangle.red.x, triangle.green.y - triangle.red.y }; - ColorPoint v2 = { triangle.blue.x - triangle.red.x, triangle.blue.y - triangle.red.y }; - ColorPoint q = { p.x - triangle.red.x, p.y - triangle.red.y }; - float s = crossProduct(q, v2) / crossProduct(v1, v2); - float t = crossProduct(v1, q) / crossProduct(v1, v2); - if ((s >= 0.0f) && (t >= 0.0f) && (s + t <= 1.0f)) { - return true; - } else { - return false; - } -} - -ColorPoint LedDevicePhilipsHue::getClosestPointToPoint(ColorPoint a, ColorPoint b, ColorPoint p) { - ColorPoint AP = { p.x - a.x, p.y - a.y }; - ColorPoint AB = { b.x - a.x, b.y - a.y }; - float ab2 = AB.x * AB.x + AB.y * AB.y; - float ap_ab = AP.x * AB.x + AP.y * AB.y; - float t = ap_ab / ab2; - if (t < 0.0f) { - t = 0.0f; - } else if (t > 1.0f) { - t = 1.0f; - } - return {a.x + AB.x * t, a.y + AB.y * t}; -} - -float LedDevicePhilipsHue::getDistanceBetweenTwoPoints(ColorPoint p1, ColorPoint p2) { - // Horizontal difference. - float dx = p1.x - p2.x; - // Vertical difference. - float dy = p1.y - p2.y; - // Absolute value. - return sqrt(dx * dx + dy * dy); -} - -ColorPoint LedDevicePhilipsHue::rgbToXYBrightness(float red, float green, float blue, HueLamp lamp) { - // Apply gamma correction. - float r = (red > 0.04045f) ? powf((red + 0.055f) / (1.0f + 0.055f), 2.4f) : (red / 12.92f); - float g = (green > 0.04045f) ? powf((green + 0.055f) / (1.0f + 0.055f), 2.4f) : (green / 12.92f); - float b = (blue > 0.04045f) ? powf((blue + 0.055f) / (1.0f + 0.055f), 2.4f) : (blue / 12.92f); - // Convert to XYZ space. - float X = r * 0.649926f + g * 0.103455f + b * 0.197109f; - float Y = r * 0.234327f + g * 0.743075f + b * 0.022598f; - float Z = r * 0.0000000f + g * 0.053077f + b * 1.035763f; - // Convert to x,y space. - float cx = X / (X + Y + Z); - float cy = Y / (X + Y + Z); - if (isnan(cx)) { - cx = 0.0f; - } - if (isnan(cy)) { - cy = 0.0f; - } - // Brightness is simply Y in the XYZ space. - ColorPoint xy = { cx, cy, Y }; - // Check if the given XY value is within the color reach of our lamps. - if (!isPointInLampsReach(lamp, xy)) { - // It seems the color is out of reach let's find the closes color we can produce with our lamp and send this XY value out. - ColorPoint pAB = getClosestPointToPoint(lamp.colorSpace.red, lamp.colorSpace.green, xy); - ColorPoint pAC = getClosestPointToPoint(lamp.colorSpace.blue, lamp.colorSpace.red, xy); - ColorPoint pBC = getClosestPointToPoint(lamp.colorSpace.green, lamp.colorSpace.blue, xy); - // Get the distances per point and see which point is closer to our Point. - float dAB = getDistanceBetweenTwoPoints(xy, pAB); - float dAC = getDistanceBetweenTwoPoints(xy, pAC); - float dBC = getDistanceBetweenTwoPoints(xy, pBC); - float lowest = dAB; - ColorPoint closestPoint = pAB; - if (dAC < lowest) { - lowest = dAC; - closestPoint = pAC; - } - if (dBC < lowest) { - lowest = dBC; - closestPoint = pBC; - } - // Change the xy value to a value which is within the reach of the lamp. - xy.x = closestPoint.x; - xy.y = closestPoint.y; - } - return xy; -} - -HueLamp::HueLamp(unsigned int id, QString originalState, QString modelId) : - id(id), originalState(originalState) { - // Hue system model ids. - const std::set HUE_BULBS_MODEL_IDS = { "LCT001", "LCT002", "LCT003" }; - const std::set LIVING_COLORS_MODEL_IDS = { "LLC001", "LLC005", "LLC006", "LLC007", "LLC011", "LLC012", - "LLC013", "LST001" }; - // Find id in the sets and set the appropiate color space. - if (HUE_BULBS_MODEL_IDS.find(modelId) != HUE_BULBS_MODEL_IDS.end()) { - colorSpace.red = {0.675f, 0.322f}; - colorSpace.green = {0.4091f, 0.518f}; - colorSpace.blue = {0.167f, 0.04f}; - } else if (LIVING_COLORS_MODEL_IDS.find(modelId) != LIVING_COLORS_MODEL_IDS.end()) { - colorSpace.red = {0.703f, 0.296f}; - colorSpace.green = {0.214f, 0.709f}; - colorSpace.blue = {0.139f, 0.081f}; - } else { - colorSpace.red = {1.0f, 0.0f}; - colorSpace.green = {0.0f, 1.0f}; - colorSpace.blue = {0.0f, 0.0f}; - } - // Initialize color with black - color = {0.0f, 0.0f, 0.0f}; -} - -bool operator ==(ColorPoint p1, ColorPoint p2) { - return (p1.x == p2.x) && (p1.y == p2.y) && (p1.bri == p2.bri); -} - -bool operator !=(ColorPoint p1, ColorPoint p2) { - return !(p1 == p2); -} diff --git a/libsrc/leddevice/LedDevicePhilipsHue.h b/libsrc/leddevice/LedDevicePhilipsHue.h index c3503705..085defb0 100755 --- a/libsrc/leddevice/LedDevicePhilipsHue.h +++ b/libsrc/leddevice/LedDevicePhilipsHue.h @@ -15,30 +15,34 @@ /** * A color point in the color space of the hue system. */ -struct ColorPoint { +struct CiColor { + /// X component. float x; + /// Y component. float y; + /// The brightness. float bri; }; -bool operator==(ColorPoint p1, ColorPoint p2); -bool operator!=(ColorPoint p1, ColorPoint p2); +bool operator==(CiColor p1, CiColor p2); +bool operator!=(CiColor p1, CiColor p2); /** * Color triangle to define an available color space for the hue lamps. */ -struct ColorTriangle { - ColorPoint red, green, blue; +struct CiColorTriangle { + CiColor red, green, blue; }; /** * Simple class to hold the id, the latest color, the color space and the original state. */ -class HueLamp { +class PhilipsHueLamp { public: unsigned int id; - ColorPoint color; - ColorTriangle colorSpace; + CiColor black; + CiColor color; + CiColorTriangle colorSpace; QString originalState; /// @@ -50,7 +54,57 @@ public: /// /// @param modelId the model id of the hue lamp which is used to determine the color space /// - HueLamp(unsigned int id, QString originalState, QString modelId); + PhilipsHueLamp(unsigned int id, QString originalState, QString modelId); + + /// + /// Converts an RGB color to the Hue xy color space and brightness. + /// https://github.com/PhilipsHue/PhilipsHueSDK-iOS-OSX/blob/master/ApplicationDesignNotes/RGB%20to%20xy%20Color%20conversion.md + /// + /// @param red the red component in [0, 1] + /// + /// @param green the green component in [0, 1] + /// + /// @param blue the blue component in [0, 1] + /// + /// @return color point + /// + CiColor rgbToCiColor(float red, float green, float blue); + + /// + /// @param p the color point to check + /// + /// @return true if the color point is covered by the lamp color space + /// + bool isPointInLampsReach(CiColor p); + + /// + /// @param p1 point one + /// + /// @param p2 point tow + /// + /// @return the cross product between p1 and p2 + /// + float crossProduct(CiColor p1, CiColor p2); + + /// + /// @param a reference point one + /// + /// @param b reference point two + /// + /// @param p the point to which the closest point is to be found + /// + /// @return the closest color point of p to a and b + /// + CiColor getClosestPointToPoint(CiColor a, CiColor b, CiColor p); + + /// + /// @param p1 point one + /// + /// @param p2 point tow + /// + /// @return the distance between the two points + /// + float getDistanceBetweenTwoPoints(CiColor p1, CiColor p2); }; /** @@ -97,9 +151,8 @@ private slots: void restoreStates(); private: - const static ColorPoint BLACK; /// Array to save the lamps. - std::vector lamps; + std::vector lamps; /// Ip address of the bridge QString host; /// User name for the API ("newdeveloper") @@ -162,58 +215,4 @@ private: /// bool areStatesSaved(); - /// - /// Converts an RGB color to the Hue xy color space and brightness. - /// https://github.com/PhilipsHue/PhilipsHueSDK-iOS-OSX/blob/master/ApplicationDesignNotes/RGB%20to%20xy%20Color%20conversion.md - /// - /// @param red the red component in [0, 1] - /// - /// @param green the green component in [0, 1] - /// - /// @param blue the blue component in [0, 1] - /// - /// @param lamp the hue lamp instance used for color space checks. - /// - /// @return color point - /// - ColorPoint rgbToXYBrightness(float red, float green, float blue, HueLamp lamp); - - /// - /// @param p1 point one - /// - /// @param p2 point tow - /// - /// @return the cross product between p1 and p2 - /// - float crossProduct(ColorPoint p1, ColorPoint p2); - - /// - /// @param lamp the hue lamp instance - /// - /// @param p the color point to check - /// - /// @return true if the color point is covered by the lamp color space - /// - bool isPointInLampsReach(HueLamp lamp, ColorPoint p); - - /// - /// @param a reference point one - /// - /// @param b reference point two - /// - /// @param p the point to which the closest point is to be found - /// - /// @return the closest color point of p to a and b - /// - ColorPoint getClosestPointToPoint(ColorPoint a, ColorPoint b, ColorPoint p); - - /// - /// @param p1 point one - /// - /// @param p2 point tow - /// - /// @return the distance between the two points - /// - float getDistanceBetweenTwoPoints(ColorPoint p1, ColorPoint p2); - }; From dc2e173f040e23d80529c11e743e43d20bd89b99 Mon Sep 17 00:00:00 2001 From: ntim Date: Wed, 16 Jul 2014 20:46:59 +0200 Subject: [PATCH 39/74] Remove couts. Former-commit-id: fdc93ea33644313277bd4b01c14e4a63396c077f --- libsrc/leddevice/LedDevicePhilipsHue.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/libsrc/leddevice/LedDevicePhilipsHue.cpp b/libsrc/leddevice/LedDevicePhilipsHue.cpp index 0a47a24c..35607871 100755 --- a/libsrc/leddevice/LedDevicePhilipsHue.cpp +++ b/libsrc/leddevice/LedDevicePhilipsHue.cpp @@ -178,12 +178,10 @@ int LedDevicePhilipsHue::write(const std::vector & ledValues) { // From black to a color. if (lamp.color == lamp.black && xy != lamp.black) { put(getStateRoute(lamp.id), QString("{\"on\": true}")); - std::cout << "switchon" << std::endl; } // From a color to black. else if (lamp.color != lamp.black && xy == lamp.black) { put(getStateRoute(lamp.id), QString("{\"on\": false}")); - std::cout << "switchoff" << std::endl; } } // Remember last color. From b6fa306df71da127323b7f9f6dc08cd78248355a Mon Sep 17 00:00:00 2001 From: Johan Date: Fri, 1 Aug 2014 10:13:40 +0200 Subject: [PATCH 40/74] Fix schema requirement for json transforms (use numbers instead of doubles to allow integer values for zero and one) Former-commit-id: f65ea81c217bbf621184152fd913d58247d9b4db --- libsrc/jsonserver/schema/schema-transform.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/libsrc/jsonserver/schema/schema-transform.json b/libsrc/jsonserver/schema/schema-transform.json index a135e12f..7b939839 100644 --- a/libsrc/jsonserver/schema/schema-transform.json +++ b/libsrc/jsonserver/schema/schema-transform.json @@ -16,12 +16,12 @@ "required" : false }, "saturationGain" : { - "type" : "double", + "type" : "number", "required" : false, "minimum" : 0.0 }, "valueGain" : { - "type" : "double", + "type" : "number", "required" : false, "minimum" : 0.0 }, @@ -29,7 +29,7 @@ "type": "array", "required": false, "items" : { - "type": "double", + "type": "number", "minimum": 0.0, "maximum": 1.0 }, @@ -40,7 +40,7 @@ "type": "array", "required": false, "items" : { - "type": "double", + "type": "number", "minimum": 0.0 }, "minItems": 3, @@ -50,7 +50,7 @@ "type": "array", "required": false, "items" : { - "type": "double" + "type": "number" }, "minItems": 3, "maxItems": 3 @@ -59,7 +59,7 @@ "type": "array", "required": false, "items" : { - "type": "double" + "type": "number" }, "minItems": 3, "maxItems": 3 From 04c54ee7eb375e8d94a62bfb0b9cea0d75a799c5 Mon Sep 17 00:00:00 2001 From: poljvd Date: Mon, 18 Aug 2014 13:50:19 +0200 Subject: [PATCH 41/74] Fix typo error Former-commit-id: 664fc81f7bcfab58ac543f08725992044e51d8db --- include/utils/VideoMode.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/utils/VideoMode.h b/include/utils/VideoMode.h index f5cae94f..ae727657 100644 --- a/include/utils/VideoMode.h +++ b/include/utils/VideoMode.h @@ -18,7 +18,7 @@ inline VideoMode parse3DMode(std::string videoMode) // convert to lower case std::transform(videoMode.begin(), videoMode.end(), videoMode.begin(), ::tolower); - if (videoMode == "23DTAB") + if (videoMode == "3DTAB") { return VIDEO_3DTAB; } From 089120fe9d8890b1ea61dd5037a23d12b6b0afe1 Mon Sep 17 00:00:00 2001 From: "T. van der Zwan" Date: Tue, 19 Aug 2014 10:54:15 +0200 Subject: [PATCH 42/74] Fixed error message for setImage Former-commit-id: db0c38d865463048c6bb039c173e02ea685ae36e --- libsrc/effectengine/Effect.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libsrc/effectengine/Effect.cpp b/libsrc/effectengine/Effect.cpp index e09f8979..1b3524bf 100644 --- a/libsrc/effectengine/Effect.cpp +++ b/libsrc/effectengine/Effect.cpp @@ -270,7 +270,7 @@ PyObject* Effect::wrapSetImage(PyObject *self, PyObject *args) } else { - PyErr_SetString(PyExc_RuntimeError, "Length of bytearray argument should be 3*ledCount"); + PyErr_SetString(PyExc_RuntimeError, "Length of bytearray argument should be 3*width*height"); return nullptr; } } From b6514b73ffa6c43693fcac9289ff97c985222e41 Mon Sep 17 00:00:00 2001 From: "T. van der Zwan" Date: Sun, 24 Aug 2014 21:00:03 +0200 Subject: [PATCH 43/74] Build release Former-commit-id: 00ccb7b1ea58bdcaec77246c5c1c424183f9adbe --- deploy/hyperion.tar.gz.REMOVED.git-id | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy/hyperion.tar.gz.REMOVED.git-id b/deploy/hyperion.tar.gz.REMOVED.git-id index 7729eb84..ab29ef88 100644 --- a/deploy/hyperion.tar.gz.REMOVED.git-id +++ b/deploy/hyperion.tar.gz.REMOVED.git-id @@ -1 +1 @@ -5e8d795d2aa82337e42924c1a5292203d7d4271a \ No newline at end of file +9546e335179f732ff68ea9bc47020a19e4b6f44c \ No newline at end of file From 265656cc358051e388594c0e2a0ed69102405215 Mon Sep 17 00:00:00 2001 From: Floris Bos Date: Mon, 8 Sep 2014 15:39:15 +0200 Subject: [PATCH 44/74] cmake: do not cache libusb library if libusb-1.0 headers not found Library found might be an older libusb version. Closes #158 Former-commit-id: 12783e1cfd3902954a9513ca1baf53433add742a --- cmake/Findlibusb-1.0.cmake | 1 + 1 file changed, 1 insertion(+) diff --git a/cmake/Findlibusb-1.0.cmake b/cmake/Findlibusb-1.0.cmake index 26a82c4f..77474e9a 100644 --- a/cmake/Findlibusb-1.0.cmake +++ b/cmake/Findlibusb-1.0.cmake @@ -87,6 +87,7 @@ else (LIBUSB_1_LIBRARIES AND LIBUSB_1_INCLUDE_DIRS) message(STATUS " - Libraries: ${LIBUSB_1_LIBRARIES}") endif (NOT libusb_1_FIND_QUIETLY) else (LIBUSB_1_FOUND) + unset(LIBUSB_1_LIBRARY CACHE) if (libusb_1_FIND_REQUIRED) message(FATAL_ERROR "Could not find libusb") endif (libusb_1_FIND_REQUIRED) From 3c65b82ac1ee3b6c104cb0483f014e785681709a Mon Sep 17 00:00:00 2001 From: Floris Bos Date: Mon, 8 Sep 2014 16:16:02 +0200 Subject: [PATCH 45/74] Allow disabling PROTOBUF support One dependency less for users that only use DISPMANX grabbing Former-commit-id: 24ea0480e3798bab692e75d82ddb9f5eccfa03c5 --- CMakeLists.txt | 21 ++++++++++----- HyperionConfig.h.in | 3 +++ libsrc/CMakeLists.txt | 34 ++++++++++++----------- src/hyperion-remote/CMakeLists.txt | 3 --- src/hyperiond/CMakeLists.txt | 43 ++++++++++++++++-------------- src/hyperiond/hyperiond.cpp | 6 +++++ 6 files changed, 66 insertions(+), 44 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4f4ff4f6..29c9476b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,6 +19,13 @@ message(STATUS "ENABLE_V4L2 = " ${ENABLE_V4L2}) option(ENABLE_TINKERFORGE "Enable the TINKERFORGE device" ON) message(STATUS "ENABLE_TINKERFORGE = " ${ENABLE_TINKERFORGE}) +option(ENABLE_PROTOBUF "Enable PROTOBUF server" ON) +message(STATUS "ENABLE_PROTOBUF = " ${ENABLE_PROTOBUF}) + +if (ENABLE_V4L2 AND NOT ENABLE_PROTOBUF) + message(FATAL_ERROR "V4L2 grabber requires PROTOBUF. Disable V4L2 or enable PROTOBUF") +endif (ENABLE_V4L2 AND NOT ENABLE_PROTOBUF) + # Createt the configuration file # configure a header file to pass some of the CMake settings # to the source code @@ -54,12 +61,14 @@ 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 (make sure to find the static version) -set(CMAKE_FIND_LIBRARY_SUFFIXES_OLD ${CMAKE_FIND_LIBRARY_SUFFIXES}) -set(CMAKE_FIND_LIBRARY_SUFFIXES ".a") -find_package(Protobuf REQUIRED) -set(CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES_OLD}) -set(CMAKE_FIND_LIBRARY_SUFFIXES_OLD) +if (ENABLE_PROTOBUF) + # add protocol buffers (make sure to find the static version) + set(CMAKE_FIND_LIBRARY_SUFFIXES_OLD ${CMAKE_FIND_LIBRARY_SUFFIXES}) + set(CMAKE_FIND_LIBRARY_SUFFIXES ".a") + find_package(Protobuf REQUIRED) + set(CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES_OLD}) + set(CMAKE_FIND_LIBRARY_SUFFIXES_OLD) +endif (ENABLE_PROTOBUF) #add libusb and pthreads find_package(libusb-1.0 REQUIRED) diff --git a/HyperionConfig.h.in b/HyperionConfig.h.in index 17f040c0..ff2f4f82 100644 --- a/HyperionConfig.h.in +++ b/HyperionConfig.h.in @@ -11,3 +11,6 @@ // Define to enable the spi-device #cmakedefine ENABLE_TINKERFORGE + +// Define to enable PROTOBUF server +#cmakedefine ENABLE_PROTOBUF diff --git a/libsrc/CMakeLists.txt b/libsrc/CMakeLists.txt index 62911dd7..680e7a53 100644 --- a/libsrc/CMakeLists.txt +++ b/libsrc/CMakeLists.txt @@ -1,15 +1,19 @@ - -# Define the current source locations -SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include) -SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc) - -add_subdirectory(hyperion) -add_subdirectory(blackborder) -add_subdirectory(jsonserver) -add_subdirectory(protoserver) -add_subdirectory(boblightserver) -add_subdirectory(leddevice) -add_subdirectory(utils) -add_subdirectory(xbmcvideochecker) -add_subdirectory(effectengine) -add_subdirectory(grabber) + +# Define the current source locations +SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include) +SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc) + +add_subdirectory(hyperion) +add_subdirectory(blackborder) +add_subdirectory(jsonserver) + +if (ENABLE_PROTOBUF) + add_subdirectory(protoserver) +endif (ENABLE_PROTOBUF) + +add_subdirectory(boblightserver) +add_subdirectory(leddevice) +add_subdirectory(utils) +add_subdirectory(xbmcvideochecker) +add_subdirectory(effectengine) +add_subdirectory(grabber) diff --git a/src/hyperion-remote/CMakeLists.txt b/src/hyperion-remote/CMakeLists.txt index 676dc7d3..c934be21 100644 --- a/src/hyperion-remote/CMakeLists.txt +++ b/src/hyperion-remote/CMakeLists.txt @@ -2,9 +2,6 @@ cmake_minimum_required(VERSION 2.8) project(hyperion-remote) -# add protocol buffers -find_package(Protobuf REQUIRED) - # find Qt4 find_package(Qt4 REQUIRED QtCore QtGui QtNetwork) diff --git a/src/hyperiond/CMakeLists.txt b/src/hyperiond/CMakeLists.txt index f843b15f..49df4bfe 100644 --- a/src/hyperiond/CMakeLists.txt +++ b/src/hyperiond/CMakeLists.txt @@ -1,20 +1,23 @@ - -add_executable(hyperiond - hyperiond.cpp) - -target_link_libraries(hyperiond - hyperion - xbmcvideochecker - effectengine - jsonserver - protoserver - 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) + +add_executable(hyperiond + hyperiond.cpp) + +target_link_libraries(hyperiond + hyperion + xbmcvideochecker + effectengine + jsonserver + 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) + +if (ENABLE_PROTOBUF) + target_link_libraries(hyperiond protoserver) +endif (ENABLE_PROTOBUF) diff --git a/src/hyperiond/hyperiond.cpp b/src/hyperiond/hyperiond.cpp index b6365019..e7591343 100644 --- a/src/hyperiond/hyperiond.cpp +++ b/src/hyperiond/hyperiond.cpp @@ -36,8 +36,10 @@ // JsonServer includes #include +#ifdef ENABLE_PROTOBUF // ProtoServer includes #include +#endif // BoblightServer includes #include @@ -233,6 +235,7 @@ int main(int argc, char** argv) std::cout << "Json server created and started on port " << jsonServer->getPort() << std::endl; } +#ifdef ENABLE_PROTOBUF // Create Proto server if configuration is present ProtoServer * protoServer = nullptr; if (config.isMember("protoServer")) @@ -241,6 +244,7 @@ int main(int argc, char** argv) protoServer = new ProtoServer(&hyperion, protoServerConfig["port"].asUInt()); std::cout << "Proto server created and started on port " << protoServer->getPort() << std::endl; } +#endif // Create Boblight server if configuration is present BoblightServer * boblightServer = nullptr; @@ -264,7 +268,9 @@ int main(int argc, char** argv) #endif delete xbmcVideoChecker; delete jsonServer; +#ifdef ENABLE_PROTOBUF delete protoServer; +#endif delete boblightServer; // leave application From 905f8565438480ea6676feba94cef1869e581d8a Mon Sep 17 00:00:00 2001 From: Floris Bos Date: Mon, 8 Sep 2014 18:59:29 +0200 Subject: [PATCH 46/74] Fix linking with static Qt Embedded builds Make sure pthreads and dl get linked when building Hyperion using a static Qt Embedded library instead of Qt X11 Former-commit-id: d1e57e9192dfb6c3c42261a9ee57ebdc85bf03cd --- cmake/qt4/Qt4ConfigDependentSettings.cmake | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/cmake/qt4/Qt4ConfigDependentSettings.cmake b/cmake/qt4/Qt4ConfigDependentSettings.cmake index 6db5da18..6df28f47 100644 --- a/cmake/qt4/Qt4ConfigDependentSettings.cmake +++ b/cmake/qt4/Qt4ConfigDependentSettings.cmake @@ -270,6 +270,16 @@ if(Q_WS_X11) endif() +if(Q_WS_QWS) + set(CMAKE_THREAD_PREFER_PTHREADS 1) + find_package(Threads) + if(CMAKE_USE_PTHREADS_INIT) + set(QT_QTCORE_LIB_DEPENDENCIES ${QT_QTCORE_LIB_DEPENDENCIES} ${CMAKE_THREAD_LIBS_INIT}) + endif() + + set (QT_QTCORE_LIB_DEPENDENCIES ${QT_QTCORE_LIB_DEPENDENCIES} ${CMAKE_DL_LIBS}) + +endif() if(Q_WS_WIN) set(QT_QTGUI_LIB_DEPENDENCIES ${QT_QTGUI_LIB_DEPENDENCIES} imm32 winmm) From 042d4b6e91f48bc4938168eb207599cdc9eadb8f Mon Sep 17 00:00:00 2001 From: David Brodski Date: Tue, 16 Sep 2014 00:17:23 +0200 Subject: [PATCH 47/74] Added LedDevice for "ws2812s" leds To activate: use led device "ws2812s" in the hyperion configuration Former-commit-id: 0b5ee38679fe353f43bb4a347882d056ca237128 --- libsrc/leddevice/CMakeLists.txt | 9 + libsrc/leddevice/LedDeviceFactory.cpp | 6 + libsrc/leddevice/LedDeviceWS2812s.cpp | 529 ++++++++++++++++++++++++++ libsrc/leddevice/LedDeviceWS2812s.h | 451 ++++++++++++++++++++++ 4 files changed, 995 insertions(+) create mode 100644 libsrc/leddevice/LedDeviceWS2812s.cpp create mode 100644 libsrc/leddevice/LedDeviceWS2812s.h diff --git a/libsrc/leddevice/CMakeLists.txt b/libsrc/leddevice/CMakeLists.txt index 441618d2..0efbd18c 100755 --- a/libsrc/leddevice/CMakeLists.txt +++ b/libsrc/leddevice/CMakeLists.txt @@ -68,6 +68,15 @@ if(ENABLE_SPIDEV) ) endif(ENABLE_SPIDEV) +SET(Leddevice_HEADERS + ${Leddevice_HEADERS} + ${CURRENT_SOURCE_DIR}/LedDeviceWS2812s.h + ) +SET(Leddevice_SOURCES + ${Leddevice_SOURCES} + ${CURRENT_SOURCE_DIR}/LedDeviceWS2812s.cpp +) + if(ENABLE_TINKERFORGE) SET(Leddevice_HEADERS ${Leddevice_HEADERS} diff --git a/libsrc/leddevice/LedDeviceFactory.cpp b/libsrc/leddevice/LedDeviceFactory.cpp index c7797c3d..abc0a2c7 100755 --- a/libsrc/leddevice/LedDeviceFactory.cpp +++ b/libsrc/leddevice/LedDeviceFactory.cpp @@ -31,6 +31,8 @@ #include "LedDevicePhilipsHue.h" #include "LedDeviceTpm2.h" +#include "LedDeviceWS2812s.h" + LedDevice * LedDeviceFactory::construct(const Json::Value & deviceConfig) { std::cout << "Device configuration: " << deviceConfig << std::endl; @@ -181,6 +183,10 @@ LedDevice * LedDeviceFactory::construct(const Json::Value & deviceConfig) LedDeviceTpm2* deviceTpm2 = new LedDeviceTpm2(output, rate); deviceTpm2->open(); device = deviceTpm2; + }else if (type == "ws2812s") + { + LedDeviceWS2812s * ledDeviceWS2812s = new LedDeviceWS2812s(); + device = ledDeviceWS2812s; } else { diff --git a/libsrc/leddevice/LedDeviceWS2812s.cpp b/libsrc/leddevice/LedDeviceWS2812s.cpp new file mode 100644 index 00000000..545519b0 --- /dev/null +++ b/libsrc/leddevice/LedDeviceWS2812s.cpp @@ -0,0 +1,529 @@ +// For license and other informations see LedDeviceWS2812s.h +// To activate: use led device "ws2812s" in the hyperion configuration + +// STL includes +#include +#include +#include +#include + +// Linux includes +#include +//#include + +// hyperion local includes +#include "LedDeviceWS2812s.h" + +LedDeviceWS2812s::LedDeviceWS2812s() : + LedDevice(), + mLedCount(0) +{ + // Init PWM generator and clear LED buffer + initHardware(); + //clearLEDBuffer(); +} + + +int LedDeviceWS2812s::write(const std::vector &ledValues) +{ + mLedCount = ledValues.size(); + //printf("Set leds, number: %d\n", mLedCount); + +// const unsigned dataLen = ledValues.size() * sizeof(ColorRgb); +// const uint8_t * dataPtr = reinterpret_cast(ledValues.data()); + + // Clear out the PWM buffer + // Disabled, because we will overwrite the buffer anyway. + + // Read data from LEDBuffer[], translate it into wire format, and write to PWMWaveform +// unsigned int LEDBuffeWordPos = 0; +// unsigned int PWMWaveformBitPos = 0; + unsigned int colorBits = 0; // Holds the GRB color before conversion to wire bit pattern + unsigned char colorBit = 0; // Holds current bit out of colorBits to be processed + unsigned int wireBit = 0; // Holds the current bit we will set in PWMWaveform +// Color_t color; + + for(size_t i=0; i=0; j--) { + colorBit = (colorBits & (1 << j)) ? 1 : 0; + switch(colorBit) { + case 1: + //wireBits = 0b110; // High, High, Low + setPWMBit(wireBit++, 1); + setPWMBit(wireBit++, 1); + setPWMBit(wireBit++, 0); + break; + case 0: + //wireBits = 0b100; // High, Low, Low + setPWMBit(wireBit++, 1); + setPWMBit(wireBit++, 0); + setPWMBit(wireBit++, 0); + break; + } + } + } + + // Copy PWM waveform to DMA's data buffer + //printf("Copying %d words to DMA data buffer\n", NUM_DATA_WORDS); + ctl = (struct control_data_s *)virtbase; + dma_cb_t *cbp = ctl->cb; + + // 72 bits per pixel / 32 bits per word = 2.25 words per pixel + // Add 1 to make sure the PWM FIFO gets the message: "we're sending zeroes" + // Times 4 because DMA works in bytes, not words + cbp->length = ((mLedCount * 2.25) + 1) * 4; + if(cbp->length > NUM_DATA_WORDS * 4) { + cbp->length = NUM_DATA_WORDS * 4; + } + + // This block is a major CPU hog when there are lots of pixels to be transmitted. + // It would go quicker with DMA. + for(unsigned int i = 0; i < (cbp->length / 4); i++) { + ctl->sample[i] = PWMWaveform[i]; + } + + + // Enable DMA and PWM engines, which should now send the data + startTransfer(); + + // Wait long enough for the DMA transfer to finish + // 3 RAM bits per wire bit, so 72 bits to send one color command. + //float bitTimeUSec = (float)(NUM_DATA_WORDS * 32) * 0.4; // Bits sent * time to transmit one bit, which is 0.4μSec + //printf("Delay for %d μSec\n", (int)bitTimeUSec); + //usleep((int)bitTimeUSec); + + return 0; +} + +int LedDeviceWS2812s::switchOff() +{ + return write(std::vector(mLedCount, ColorRgb{0,0,0})); +} + +LedDeviceWS2812s::~LedDeviceWS2812s() +{ + // Exit cleanly, freeing memory and stopping the DMA & PWM engines + // We trap all signals (including Ctrl+C), so even if you don't get here, it terminates correctly + terminate(0); +} + + +// ================================================================================================= +// ________ .__ +// / _____/ ____ ____ ________________ | | +// / \ ____/ __ \ / \_/ __ \_ __ \__ \ | | +// \ \_\ \ ___/| | \ ___/| | \// __ \| |__ +// \______ /\___ >___| /\___ >__| (____ /____/ +// \/ \/ \/ \/ \/ +// ================================================================================================= + +// Convenience functions +// -------------------------------------------------------------------------------------------------- +// Print some bits of a binary number (2nd arg is how many bits) +void LedDeviceWS2812s::printBinary(unsigned int i, unsigned int bits) { + int x; + for(x=bits-1; x>=0; x--) { + printf("%d", (i & (1 << x)) ? 1 : 0); + if(x % 16 == 0 && x > 0) { + printf(" "); + } else if(x % 4 == 0 && x > 0) { + printf(":"); + } + } +} + +// Reverse the bits in a word +unsigned int reverseWord(unsigned int word) { + unsigned int output = 0; + //unsigned char bit; + int i; + for(i=0; i<32; i++) { + //bit = word & (1 << i) ? 1 : 0; + output |= word & (1 << i) ? 1 : 0; + if(i<31) { + output <<= 1; + } + } + return output; +} + +// Not sure how this is better than usleep...? +/* +static void udelay(int us) { + struct timespec ts = { 0, us * 1000 }; + nanosleep(&ts, NULL); +} +*/ + + +// Shutdown functions +// -------------------------------------------------------------------------------------------------- +void LedDeviceWS2812s::terminate(int dummy) { + // Shut down the DMA controller + if(dma_reg) { + CLRBIT(dma_reg[DMA_CS], DMA_CS_ACTIVE); + usleep(100); + SETBIT(dma_reg[DMA_CS], DMA_CS_RESET); + usleep(100); + } + + // Shut down PWM + if(pwm_reg) { + CLRBIT(pwm_reg[PWM_CTL], PWM_CTL_PWEN1); + usleep(100); + pwm_reg[PWM_CTL] = (1 << PWM_CTL_CLRF1); + } + + // Free the allocated memory + if(page_map != 0) { + free(page_map); + } + + //exit(1); +} + +void LedDeviceWS2812s::fatal(char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + terminate(0); +} + + +// Memory management +// -------------------------------------------------------------------------------------------------- +// Translate from virtual address to physical +unsigned int LedDeviceWS2812s::mem_virt_to_phys(void *virt) { + unsigned int offset = (uint8_t *)virt - virtbase; + return page_map[offset >> PAGE_SHIFT].physaddr + (offset % PAGE_SIZE); +} + +// Translate from physical address to virtual +unsigned int LedDeviceWS2812s::mem_phys_to_virt(uint32_t phys) { + unsigned int pg_offset = phys & (PAGE_SIZE - 1); + unsigned int pg_addr = phys - pg_offset; + + for (unsigned int i = 0; i < NUM_PAGES; i++) { + if (page_map[i].physaddr == pg_addr) { + return (uint32_t)virtbase + i * PAGE_SIZE + pg_offset; + } + } + fatal("Failed to reverse map phys addr %08x\n", phys); + + return 0; +} + +// Map a peripheral's IO memory into our virtual memory, so we can read/write it directly +void * LedDeviceWS2812s::map_peripheral(uint32_t base, uint32_t len) { + int fd = open("/dev/mem", O_RDWR); + void * vaddr; + + if (fd < 0) + fatal("Failed to open /dev/mem: %m\n"); + vaddr = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_SHARED, fd, base); + if (vaddr == MAP_FAILED) + fatal("Failed to map peripheral at 0x%08x: %m\n", base); + close(fd); + + return vaddr; +} + +// Zero out the PWM waveform buffer +void LedDeviceWS2812s::clearPWMBuffer() { + memset(PWMWaveform, 0, NUM_DATA_WORDS * 4); // Times four because memset deals in bytes. +} + +// Set an individual bit in the PWM output array, accounting for word boundaries +// The (31 - bitIdx) is so that we write the data backwards, correcting its endianness +// This means getPWMBit will return something other than what was written, so it would be nice +// if the logic that calls this function would figure it out instead. (However, that's trickier) +void LedDeviceWS2812s::setPWMBit(unsigned int bitPos, unsigned char bit) { + + // Fetch word the bit is in + unsigned int wordOffset = (int)(bitPos / 32); + unsigned int bitIdx = bitPos - (wordOffset * 32); + + //printf("bitPos=%d wordOffset=%d bitIdx=%d value=%d\n", bitPos, wordOffset, bitIdx, bit); + + switch(bit) { + case 1: + PWMWaveform[wordOffset] |= (1 << (31 - bitIdx)); +// PWMWaveform[wordOffset] |= (1 << bitIdx); + break; + case 0: + PWMWaveform[wordOffset] &= ~(1 << (31 - bitIdx)); +// PWMWaveform[wordOffset] &= ~(1 << bitIdx); + break; + } +} + +// ================================================================================================= +// .___ .__ __ ___ ___ .___ +// | | ____ |__|/ |_ / | \_____ _______ __| _/_ _ _______ _______ ____ +// | |/ \| \ __\ / ~ \__ \\_ __ \/ __ |\ \/ \/ /\__ \\_ __ \_/ __ \ +// | | | \ || | \ Y // __ \| | \/ /_/ | \ / / __ \| | \/\ ___/ +// |___|___| /__||__| \___|_ /(____ /__| \____ | \/\_/ (____ /__| \___ > +// \/ \/ \/ \/ \/ \/ +// ================================================================================================= + +void LedDeviceWS2812s::initHardware() { + int pid; + int fd; + char pagemap_fn[64]; + + // Clear the PWM buffer + // --------------------------------------------------------------- + clearPWMBuffer(); + + // Set up peripheral access + // --------------------------------------------------------------- + dma_reg = (unsigned int *) map_peripheral(DMA_BASE, DMA_LEN); + dma_reg += 0x000; + pwm_reg = (unsigned int *) map_peripheral(PWM_BASE, PWM_LEN); + clk_reg = (unsigned int *) map_peripheral(CLK_BASE, CLK_LEN); + gpio_reg = (unsigned int *) map_peripheral(GPIO_BASE, GPIO_LEN); + + + // Set PWM alternate function for GPIO18 + // --------------------------------------------------------------- + //gpio_reg[1] &= ~(7 << 24); + //usleep(100); + //gpio_reg[1] |= (2 << 24); + //usleep(100); + SET_GPIO_ALT(18, 5); + + + // Allocate memory for the DMA control block & data to be sent + // --------------------------------------------------------------- + virtbase = (uint8_t *) mmap( + NULL, // Address + NUM_PAGES * PAGE_SIZE, // Length + PROT_READ | PROT_WRITE, // Protection + MAP_SHARED | // Shared + MAP_ANONYMOUS | // Not file-based, init contents to 0 + MAP_NORESERVE | // Don't reserve swap space + MAP_LOCKED, // Lock in RAM (don't swap) + -1, // File descriptor + 0); // Offset + + if (virtbase == MAP_FAILED) { + fatal("Failed to mmap physical pages: %m\n"); + } + + if ((unsigned long)virtbase & (PAGE_SIZE-1)) { + fatal("Virtual address is not page aligned\n"); + } + + //printf("virtbase mapped 0x%x bytes at 0x%x\n", NUM_PAGES * PAGE_SIZE, virtbase); + + // Allocate page map (pointers to the control block(s) and data for each CB + page_map = (page_map_t *) malloc(NUM_PAGES * sizeof(*page_map)); + if (page_map == 0) { + fatal("Failed to malloc page_map: %m\n"); + } else { + //printf("Allocated 0x%x bytes for page_map at 0x%x\n", NUM_PAGES * sizeof(*page_map), page_map); + } + + // Use /proc/self/pagemap to figure out the mapping between virtual and physical addresses + pid = getpid(); + sprintf(pagemap_fn, "/proc/%d/pagemap", pid); + fd = open(pagemap_fn, O_RDONLY); + + if (fd < 0) { + fatal("Failed to open %s: %m\n", pagemap_fn); + } + + if (lseek(fd, (unsigned long)virtbase >> 9, SEEK_SET) != (unsigned long)virtbase >> 9) { + fatal("Failed to seek on %s: %m\n", pagemap_fn); + } + + printf("Page map: %d pages\n", NUM_PAGES); + for (unsigned int i = 0; i < NUM_PAGES; i++) { + uint64_t pfn; + page_map[i].virtaddr = virtbase + i * PAGE_SIZE; + + // Following line forces page to be allocated + // (Note: Copied directly from Hirst's code... page_map[i].virtaddr[0] was just set...?) + page_map[i].virtaddr[0] = 0; + + if (read(fd, &pfn, sizeof(pfn)) != sizeof(pfn)) { + fatal("Failed to read %s: %m\n", pagemap_fn); + } + + if (((pfn >> 55) & 0xfbf) != 0x10c) { // pagemap bits: https://www.kernel.org/doc/Documentation/vm/pagemap.txt + fatal("Page %d not present (pfn 0x%016llx)\n", i, pfn); + } + + page_map[i].physaddr = (unsigned int)pfn << PAGE_SHIFT | 0x40000000; + //printf("Page map #%2d: virtual %8p ==> physical 0x%08x [0x%016llx]\n", i, page_map[i].virtaddr, page_map[i].physaddr, pfn); + } + + + // Set up control block + // --------------------------------------------------------------- + ctl = (struct control_data_s *)virtbase; + dma_cb_t *cbp = ctl->cb; + // FIXME: Change this to use DEFINEs + unsigned int phys_pwm_fifo_addr = 0x7e20c000 + 0x18; + + // No wide bursts, source increment, dest DREQ on line 5, wait for response, enable interrupt + cbp->info = DMA_TI_CONFIGWORD; + + // Source is our allocated memory + cbp->src = mem_virt_to_phys(ctl->sample); + + // Destination is the PWM controller + cbp->dst = phys_pwm_fifo_addr; + + // 72 bits per pixel / 32 bits per word = 2.25 words per pixel + // Add 1 to make sure the PWM FIFO gets the message: "we're sending zeroes" + // Times 4 because DMA works in bytes, not words + cbp->length = ((mLedCount * 2.25) + 1) * 4; + if(cbp->length > NUM_DATA_WORDS * 4) { + cbp->length = NUM_DATA_WORDS * 4; + } + + // We don't use striding + cbp->stride = 0; + + // These are reserved + cbp->pad[0] = 0; + cbp->pad[1] = 0; + + // Pointer to next block - 0 shuts down the DMA channel when transfer is complete + cbp->next = 0; + + // Testing + /* + ctl = (struct control_data_s *)virtbase; + ctl->sample[0] = 0x00000000; + ctl->sample[1] = 0x000000FA; + ctl->sample[2] = 0x0000FFFF; + ctl->sample[3] = 0xAAAAAAAA; + ctl->sample[4] = 0xF0F0F0F0; + ctl->sample[5] = 0x0A0A0A0A; + ctl->sample[6] = 0xF00F0000; + */ + + + // Stop any existing DMA transfers + // --------------------------------------------------------------- + dma_reg[DMA_CS] |= (1 << DMA_CS_ABORT); + usleep(100); + dma_reg[DMA_CS] = (1 << DMA_CS_RESET); + usleep(100); + + + // PWM Clock + // --------------------------------------------------------------- + // Kill the clock + // FIXME: Change this to use a DEFINE + clk_reg[PWM_CLK_CNTL] = 0x5A000000 | (1 << 5); + usleep(100); + + // Disable DMA requests + CLRBIT(pwm_reg[PWM_DMAC], PWM_DMAC_ENAB); + usleep(100); + + // The fractional part is quantized to a range of 0-1024, so multiply the decimal part by 1024. + // E.g., 0.25 * 1024 = 256. + // So, if you want a divisor of 400.5, set idiv to 400 and fdiv to 512. + unsigned int idiv = 400; + unsigned short fdiv = 0; // Should be 16 bits, but the value must be <= 1024 + clk_reg[PWM_CLK_DIV] = 0x5A000000 | (idiv << 12) | fdiv; // Set clock multiplier + usleep(100); + + // Enable the clock. Next-to-last digit means "enable clock". Last digit is 1 (oscillator), + // 4 (PLLA), 5 (PLLC), or 6 (PLLD) (according to the docs) although PLLA doesn't seem to work. + // FIXME: Change this to use a DEFINE + clk_reg[PWM_CLK_CNTL] = 0x5A000015; + usleep(100); + + + // PWM + // --------------------------------------------------------------- + // Clear any preexisting crap from the control & status register + pwm_reg[PWM_CTL] = 0; + + // Set transmission range (32 bytes, or 1 word) + // <32: Truncate. >32: Pad with SBIT1. As it happens, 32 is perfect. + pwm_reg[PWM_RNG1] = 32; + usleep(100); + + // Send DMA requests to fill the FIFO + pwm_reg[PWM_DMAC] = + (1 << PWM_DMAC_ENAB) | + (8 << PWM_DMAC_PANIC) | + (8 << PWM_DMAC_DREQ); + usleep(1000); + + // Clear the FIFO + SETBIT(pwm_reg[PWM_CTL], PWM_CTL_CLRF1); + usleep(100); + + // Don't repeat last FIFO contents if it runs dry + CLRBIT(pwm_reg[PWM_CTL], PWM_CTL_RPTL1); + usleep(100); + + // Silence (default) bit is 0 + CLRBIT(pwm_reg[PWM_CTL], PWM_CTL_SBIT1); + usleep(100); + + // Polarity = default (low = 0, high = 1) + CLRBIT(pwm_reg[PWM_CTL], PWM_CTL_POLA1); + usleep(100); + + // Enable serializer mode + SETBIT(pwm_reg[PWM_CTL], PWM_CTL_MODE1); + usleep(100); + + // Use FIFO rather than DAT1 + SETBIT(pwm_reg[PWM_CTL], PWM_CTL_USEF1); + usleep(100); + + // Disable MSEN1 + CLRBIT(pwm_reg[PWM_CTL], PWM_CTL_MSEN1); + usleep(100); + + + // DMA + // --------------------------------------------------------------- + // Raise an interrupt when transfer is complete, which will set the INT flag in the CS register + SETBIT(dma_reg[DMA_CS], DMA_CS_INT); + usleep(100); + + // Clear the END flag (by setting it - this is a "write 1 to clear", or W1C, bit) + SETBIT(dma_reg[DMA_CS], DMA_CS_END); + usleep(100); + + // Send the physical address of the control block into the DMA controller + dma_reg[DMA_CONBLK_AD] = mem_virt_to_phys(ctl->cb); + usleep(100); + + // Clear error flags, if any (these are also W1C bits) + // FIXME: Use a define instead of this + dma_reg[DMA_DEBUG] = 7; + usleep(100); +} + +// Begin the transfer +void LedDeviceWS2812s::startTransfer() { + // Enable DMA + dma_reg[DMA_CONBLK_AD] = mem_virt_to_phys(ctl->cb); + dma_reg[DMA_CS] = DMA_CS_CONFIGWORD | (1 << DMA_CS_ACTIVE); + usleep(100); + + // Enable PWM + SETBIT(pwm_reg[PWM_CTL], PWM_CTL_PWEN1); + +// dumpPWM(); +// dumpDMA(); +} diff --git a/libsrc/leddevice/LedDeviceWS2812s.h b/libsrc/leddevice/LedDeviceWS2812s.h new file mode 100644 index 00000000..2dca0fb0 --- /dev/null +++ b/libsrc/leddevice/LedDeviceWS2812s.h @@ -0,0 +1,451 @@ +#ifndef LEDDEVICEWS2812S_H_ +#define LEDDEVICEWS2812S_H_ + +#pragma once + + +// Set tabs to 4 spaces. + +// ================================================================================================= +// +// __ __ _________________ ______ ____________ ____________________.__ +// / \ / \/ _____/\_____ \ / __ \/_ \_____ \ \______ \______ \__| +// \ \/\/ /\_____ \ / ____/ > < | |/ ____/ | _/| ___/ | +// \ / / \/ \/ -- \| / \ | | \| | | | +// \__/\ / /_______ /\_______ \______ /|___\_______ \ |____|_ /|____| |__| +// \/ \/ \/ \/ \/ \/ +// +// WS2812 NeoPixel driver +// Based on code by Richard G. Hirst and others +// Adapted for the WS2812 by 626Pilot, April/May 2014 +// See: https://github.com/626Pilot/RaspberryPi-NeoPixel-WS2812 +// Version: https://github.com/626Pilot/RaspberryPi-NeoPixel-WS2812/blob/1d43407d9e6eba19bff24330bc09a27963b55751/ws2812-RPi.c +// Huge ASCII art section labels are from http://patorjk.com/software/taag/ +// +// LED driver adaptation by Kammerjaeger () +// mostly code removed that was not needed +// +// License: GPL +// +// You are using this at your OWN RISK. I believe this software is reasonably safe to use (aside +// from the intrinsic risk to those who are photosensitive - see below), although I can't be certain +// that it won't trash your hardware or cause property damage. +// +// Speaking of risk, WS2812 pixels are bright enough to cause eye pain and (for all I know) possibly +// retina damage when run at full strength. It's a good idea to set the brightness at 0.2 or so for +// direct viewing (whether you're looking directly at the pixels or not), or to put some diffuse +// material between you and the LEDs. +// +// PHOTOSENSITIVITY WARNING: +// Patterns of light and darkness (stationary or moving), flashing lights, patterns and backgrounds +// on screens, and the like, may cause epilleptic seizures in some people. This is a danger EVEN IF +// THE PERSON (WHICH MAY BE *YOU*) HAS NEVER KNOWINGLY HAD A PHOTOSENSITIVE EPISODE BEFORE. It's up +// to you to learn the warning signs, but symptoms may include dizziness, nausea, vision changes, +// convlusions, disorientation, involuntary movements, and eye twitching. (This list is not +// necessarily exhaustive.) +// +// NEOPIXEL BEST PRACTICES: https://learn.adafruit.com/adafruit-neopixel-uberguide/best-practices +// +// Connections: +// Positive to Raspberry Pi's 3.3v, for better separation connect only ground and data directly +// (5v can be used then without a problem, at least it worked for me, Kammerjaeger) +// Negative to Raspberry Pi's ground +// Data to GPIO18 (Pin 12) (through a resistor, which you should know from the Best +// Practices guide!) +// +// Buy WS2812-based stuff from: http://adafruit.com +// +// To activate: use led device "ws2812s" in the hyperion configuration +// (it needs to be root so it can map the peripherals' registers) +// +// ================================================================================================= + +// This is for the WS2812 LEDs. It won't work with the older WS2811s, although it could be modified +// for that without too much trouble. Preliminary driver used Frank Buss' servo driver, but I moved +// to Richard Hirst's memory mapping/access model because his code already works with DMA, and has +// what I think is a slightly cleaner way of accessing the registers: register[name] rather than +// *(register + name). + +// At the time of writing, there's a lot of confusing "PWM DMA" code revolving around simulating +// an FM signal. Usually this is done without properly initializing certain registers, which is +// OK for their purpose, but I needed to be able to transfer actual coherent data and have it wind +// up in a proper state once it was transferred. This has proven to be a somewhat painful task. +// The PWM controller likes to ignore the RPTL1 bit when the data is in a regular, repeating +// pattern. I'M NOT MAKING IT UP! It really does that. It's bizarre. There are lots of other +// strange irregularities as well, which had to be figured out through trial and error. It doesn't +// help that the BCM2835 ARM Peripherals manual contains outright errors and omissions! + +// Many examples of this kind of code have magic numbers in them. If you don't know, a magic number +// is one that either lacks an obvious structure (e.g. 0x2020C000) or purpose. Please don't use +// that stuff in any code you release! All magic numbers found in reference code have been changed +// to DEFINEs. That way, instead of seeing some inscrutable number, you see (e.g.) PWM_CTL. + +// References - BCM2835 ARM Peripherals: +// http://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Peripherals.pdf +// +// Raspberry Pi low-level peripherals: +// http://elinux.org/RPi_Low-level_peripherals +// +// Richard Hirst's nice, clean code: +// https://github.com/richardghirst/PiBits/blob/master/PiFmDma/PiFmDma.c +// +// PWM clock register: +// http://www.raspberrypi.org/forums/viewtopic.php?t=8467&p=124620 +// +// Simple (because it's in assembly) PWM+DMA setup: +// https://github.com/mikedurso/rpi-projects/blob/master/asm-nyancat/rpi-nyancat.s +// +// Adafruit's NeoPixel driver: +// https://github.com/adafruit/Adafruit_NeoPixel/blob/master/Adafruit_NeoPixel.cpp + + +// ================================================================================================= +// .___ .__ .___ +// | | ____ ____ | | __ __ __| _/____ ______ +// | |/ \_/ ___\| | | | \/ __ |/ __ \ / ___/ +// | | | \ \___| |_| | / /_/ \ ___/ \___ \ +// |___|___| /\___ >____/____/\____ |\___ >____ > +// \/ \/ \/ \/ \/ +// ================================================================================================= + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Hyperion includes +#include + + +// ================================================================================================= +// ________ _____.__ ____ ____ ____ +// \______ \ _____/ ____\__| ____ ____ ______ / _ \ \ \ / /____ _______ ______ +// | | \_/ __ \ __\| |/ \_/ __ \ / ___/ > _ __| |__|___| /\___ >____ > \_____\ \ \___/ (____ /__| /____ > +// \/ \/ \/ \/ \/ \/ \/ \/ +// ================================================================================================= + +// Base addresses for GPIO, PWM, PWM clock, and DMA controllers (physical, not bus!) +// These will be "memory mapped" into virtual RAM so that they can be written and read directly. +// ------------------------------------------------------------------------------------------------- +#define DMA_BASE 0x20007000 +#define DMA_LEN 0x24 +#define PWM_BASE 0x2020C000 +#define PWM_LEN 0x28 +#define CLK_BASE 0x20101000 +#define CLK_LEN 0xA8 +#define GPIO_BASE 0x20200000 +#define GPIO_LEN 0xB4 + +// GPIO +// ------------------------------------------------------------------------------------------------- +#define GPFSEL0 0x20200000 // GPIO function select, pins 0-9 (bits 30-31 reserved) +#define GPFSEL1 0x20200004 // Pins 10-19 +#define GPFSEL2 0x20200008 // Pins 20-29 +#define GPFSEL3 0x2020000C // Pins 30-39 +#define GPFSEL4 0x20200010 // Pins 40-49 +#define GPFSEL5 0x20200014 // Pins 50-53 +#define GPSET0 0x2020001C // Set (turn on) pin +#define GPCLR0 0x20200028 // Clear (turn off) pin +#define GPPUD 0x20200094 // Internal pullup/pulldown resistor control +#define GPPUDCLK0 0x20200098 // PUD clock for pins 0-31 +#define GPPUDCLK1 0x2020009C // PUD clock for pins 32-53 + +// Memory offsets for the PWM clock register, which is undocumented! Please fix that, Broadcom! +// ------------------------------------------------------------------------------------------------- +#define PWM_CLK_CNTL 40 // Control (on/off) +#define PWM_CLK_DIV 41 // Divisor (bits 11:0 are *quantized* floating part, 31:12 integer part) + +// PWM Register Addresses (page 141) +// These are divided by 4 because the register offsets in the guide are in bytes (8 bits) but +// the pointers we use in this program are in words (32 bits). Buss' original defines are in +// word offsets, e.g. PWM_RNG1 was 4 and PWM_DAT1 was 5. This is functionally the same, but it +// matches the numbers supplied in the guide. +// ------------------------------------------------------------------------------------------------- +#define PWM_CTL 0x00 // Control Register +#define PWM_STA (0x04 / 4) // Status Register +#define PWM_DMAC (0x08 / 4) // DMA Control Register +#define PWM_RNG1 (0x10 / 4) // Channel 1 Range +#define PWM_DAT1 (0x14 / 4) // Channel 1 Data +#define PWM_FIF1 (0x18 / 4) // FIFO (for both channels - bytes are interleaved if both active) +#define PWM_RNG2 (0x20 / 4) // Channel 2 Range +#define PWM_DAT2 (0x24 / 4) // Channel 2 Data + +// PWM_CTL register bit offsets +// Note: Don't use MSEN1/2 for this purpose. It will screw things up. +// ------------------------------------------------------------------------------------------------- +#define PWM_CTL_MSEN2 15 // Channel 2 - 0: Use PWM algorithm. 1: Use M/S (serial) algorithm. +#define PWM_CTL_USEF2 13 // Channel 2 - 0: Use PWM_DAT2. 1: Use FIFO. +#define PWM_CTL_POLA2 12 // Channel 2 - Invert output polarity (if set, 0=high and 1=low) +#define PWM_CTL_SBIT2 11 // Channel 2 - Silence bit (default line state when not transmitting) +#define PWM_CTL_RPTL2 10 // Channel 2 - Repeat last data in FIFO +#define PWM_CTL_MODE2 9 // Channel 2 - Mode. 0=PWM, 1=Serializer +#define PWM_CTL_PWEN2 8 // Channel 2 - Enable PWM +#define PWM_CTL_CLRF1 6 // Clear FIFO +#define PWM_CTL_MSEN1 7 // Channel 1 - 0: Use PWM algorithm. 1: Use M/S (serial) algorithm. +#define PWM_CTL_USEF1 5 // Channel 1 - 0: Use PWM_DAT1. 1: Use FIFO. +#define PWM_CTL_POLA1 4 // Channel 1 - Invert output polarity (if set, 0=high and 1=low) +#define PWM_CTL_SBIT1 3 // Channel 1 - Silence bit (default line state when not transmitting) +#define PWM_CTL_RPTL1 2 // Channel 1 - Repeat last data in FIFO +#define PWM_CTL_MODE1 1 // Channel 1 - Mode. 0=PWM, 1=Serializer +#define PWM_CTL_PWEN1 0 // Channel 1 - Enable PWM + +// PWM_STA register bit offsets +// ------------------------------------------------------------------------------------------------- +#define PWM_STA_STA4 12 // Channel 4 State +#define PWM_STA_STA3 11 // Channel 3 State +#define PWM_STA_STA2 10 // Channel 2 State +#define PWM_STA_STA1 9 // Channel 1 State +#define PWM_STA_BERR 8 // Bus Error +#define PWM_STA_GAPO4 7 // Gap Occurred on Channel 4 +#define PWM_STA_GAPO3 6 // Gap Occurred on Channel 3 +#define PWM_STA_GAPO2 5 // Gap Occurred on Channel 2 +#define PWM_STA_GAPO1 4 // Gap Occurred on Channel 1 +#define PWM_STA_RERR1 3 // FIFO Read Error +#define PWM_STA_WERR1 2 // FIFO Write Error +#define PWM_STA_EMPT1 1 // FIFO Empty +#define PWM_STA_FULL1 0 // FIFO Full + +// PWM_DMAC bit offsets +// ------------------------------------------------------------------------------------------------- +#define PWM_DMAC_ENAB 31 // 0: DMA Disabled. 1: DMA Enabled. +#define PWM_DMAC_PANIC 8 // Bits 15:8. Threshold for PANIC signal. Default 7. +#define PWM_DMAC_DREQ 0 // Bits 7:0. Threshold for DREQ signal. Default 7. + +// PWM_RNG1, PWM_RNG2 +// -------------------------------------------------------------------------------------------------- +// Defines the transmission range. In PWM mode, evenly spaced pulses are sent within a period +// of length defined in these registers. In serial mode, serialized data is sent within the +// same period. The value is normally 32. If less, data will be truncated. If more, data will +// be padded with zeros. + +// DAT1, DAT2 +// -------------------------------------------------------------------------------------------------- +// NOTE: These registers are not useful for our purposes - we will use the FIFO instead! +// Stores 32 bits of data to be sent when USEF1/USEF2 is 0. In PWM mode, defines how many +// pulses will be sent within the period specified in PWM_RNG1/PWM_RNG2. In serializer mode, +// defines a 32-bit word to be transmitted. + +// FIF1 +// -------------------------------------------------------------------------------------------------- +// 32-bit-wide register used to "stuff" the FIFO, which has 16 32-bit words. (So, if you write +// it 16 times, it will fill the FIFO.) +// See also: PWM_STA_EMPT1 (FIFO empty) +// PWM_STA_FULL1 (FIFO full) +// PWM_CTL_CLRF1 (Clear FIFO) + +// DMA +// -------------------------------------------------------------------------------------------------- +// DMA registers (divided by four to convert form word to byte offsets, as with the PWM registers) +#define DMA_CS (0x00 / 4) // Control & Status register +#define DMA_CONBLK_AD (0x04 / 4) // Address of Control Block (must be 256-BYTE ALIGNED!!!) +#define DMA_TI (0x08 / 4) // Transfer Information (populated from CB) +#define DMA_SOURCE_AD (0x0C / 4) // Source address, populated from CB. Physical address. +#define DMA_DEST_AD (0x10 / 4) // Destination address, populated from CB. Bus address. +#define DMA_TXFR_LEN (0x14 / 4) // Transfer length, populated from CB +#define DMA_STRIDE (0x18 / 4) // Stride, populated from CB +#define DMA_NEXTCONBK (0x1C / 4) // Next control block address, populated from CB +#define DMA_DEBUG (0x20 / 4) // Debug settings + +// DMA Control & Status register bit offsets +#define DMA_CS_RESET 31 // Reset the controller for this channel +#define DMA_CS_ABORT 30 // Set to abort transfer +#define DMA_CS_DISDEBUG 29 // Disable debug pause signal +#define DMA_CS_WAIT_FOR 28 // Wait for outstanding writes +#define DMA_CS_PANIC_PRI 20 // Panic priority (bits 23:20), default 7 +#define DMA_CS_PRIORITY 16 // AXI priority level (bits 19:16), default 7 +#define DMA_CS_ERROR 8 // Set when there's been an error +#define DMA_CS_WAITING_FOR 6 // Set when the channel's waiting for a write to be accepted +#define DMA_CS_DREQ_STOPS_DMA 5 // Set when the DMA is paused because DREQ is inactive +#define DMA_CS_PAUSED 4 // Set when the DMA is paused (active bit cleared, etc.) +#define DMA_CS_DREQ 3 // Set when DREQ line is high +#define DMA_CS_INT 2 // If INTEN is set, this will be set on CB transfer end +#define DMA_CS_END 1 // Set when the current control block is finished +#define DMA_CS_ACTIVE 0 // Enable DMA (CB_ADDR must not be 0) +// Default CS word +#define DMA_CS_CONFIGWORD (8 << DMA_CS_PANIC_PRI) | \ + (8 << DMA_CS_PRIORITY) | \ + (1 << DMA_CS_WAIT_FOR) + +// DREQ lines (page 61, most DREQs omitted) +#define DMA_DREQ_ALWAYS 0 +#define DMA_DREQ_PCM_TX 2 +#define DMA_DREQ_PCM_RX 3 +#define DMA_DREQ_PWM 5 +#define DMA_DREQ_SPI_TX 6 +#define DMA_DREQ_SPI_RX 7 +#define DMA_DREQ_BSC_TX 8 +#define DMA_DREQ_BSC_RX 9 + +// DMA Transfer Information register bit offsets +// We don't write DMA_TI directly. It's populated from the TI field in a control block. +#define DMA_TI_NO_WIDE_BURSTS 26 // Don't do wide writes in 2-beat bursts +#define DMA_TI_WAITS 21 // Wait this many cycles after end of each read/write +#define DMA_TI_PERMAP 16 // Peripheral # whose ready signal controls xfer rate (pwm=5) +#define DMA_TI_BURST_LENGTH 12 // Length of burst in words (bits 15:12) +#define DMA_TI_SRC_IGNORE 11 // Don't perform source reads (for fast cache fill) +#define DMA_TI_SRC_DREQ 10 // Peripheral in PERMAP gates source reads +#define DMA_TI_SRC_WIDTH 9 // Source transfer width - 0=32 bits, 1=128 bits +#define DMA_TI_SRC_INC 8 // Source address += SRC_WITH after each read +#define DMA_TI_DEST_IGNORE 7 // Don't perform destination writes +#define DMA_TI_DEST_DREQ 6 // Peripheral in PERMAP gates destination writes +#define DMA_TI_DEST_WIDTH 5 // Destination transfer width - 0=32 bits, 1=128 bits +#define DMA_TI_DEST_INC 4 // Dest address += DEST_WIDTH after each read +#define DMA_TI_WAIT_RESP 3 // Wait for write response +#define DMA_TI_TDMODE 1 // 2D striding mode +#define DMA_TI_INTEN 0 // Interrupt enable +// Default TI word +#define DMA_TI_CONFIGWORD (1 << DMA_TI_NO_WIDE_BURSTS) | \ + (1 << DMA_TI_SRC_INC) | \ + (1 << DMA_TI_DEST_DREQ) | \ + (1 << DMA_TI_WAIT_RESP) | \ + (1 << DMA_TI_INTEN) | \ + (DMA_DREQ_PWM << DMA_TI_PERMAP) + +// DMA Debug register bit offsets +#define DMA_DEBUG_LITE 28 // Whether the controller is "Lite" +#define DMA_DEBUG_VERSION 25 // DMA Version (bits 27:25) +#define DMA_DEBUG_DMA_STATE 16 // DMA State (bits 24:16) +#define DMA_DEBUG_DMA_ID 8 // DMA controller's AXI bus ID (bits 15:8) +#define DMA_DEBUG_OUTSTANDING_WRITES 4 // Outstanding writes (bits 7:4) +#define DMA_DEBUG_READ_ERROR 2 // Slave read response error (clear by setting) +#define DMA_DEBUG_FIFO_ERROR 1 // Operational read FIFO error (clear by setting) +#define DMA_DEBUG_READ_LAST_NOT_SET 0 // AXI bus read last signal not set (clear by setting) + +// Control Block (CB) - this tells the DMA controller what to do. +typedef struct { + unsigned int + info, // Transfer Information (TI) + src, // Source address (physical) + dst, // Destination address (bus) + length, // Length in bytes (not words!) + stride, // We don't care about this + next, // Pointer to next control block + pad[2]; // These are "reserved" (unused) +} dma_cb_t; + +// The page map contains pointers to memory that we will allocate below. It uses two pointers +// per address. This is because the software (this program) deals only in virtual addresses, +// whereas the DMA controller can only access RAM via physical address. (If that's not confusing +// enough, it writes to peripherals by their bus addresses.) +typedef struct { + uint8_t *virtaddr; + uint32_t physaddr; +} page_map_t; + + +#define PAGE_SIZE 4096 // Size of a RAM page to be allocated +#define PAGE_SHIFT 12 // This is used for address translation +#define NUM_PAGES ((sizeof(struct control_data_s) + PAGE_SIZE - 1) >> PAGE_SHIFT) + +#define SETBIT(word, bit) word |= 1< &ledValues); + + /// Switch the leds off + virtual int switchOff(); + +private: + + /// the number of leds (needed when switching off) + size_t mLedCount; + + page_map_t *page_map; // This will hold the page map, which we'll allocate below + uint8_t *virtbase; // Pointer to some virtual memory that will be allocated + + volatile unsigned int *pwm_reg; // PWM controller register set + volatile unsigned int *clk_reg; // PWM clock manager register set + volatile unsigned int *dma_reg; // DMA controller register set + volatile unsigned int *gpio_reg; // GPIO pin controller register set + + // Contains arrays of control blocks and their related samples. + // One pixel needs 72 bits (24 bits for the color * 3 to represent them on the wire). + // 768 words = 341.3 pixels + // 1024 words = 455.1 pixels + // The highest I can make this number is 1016. Any higher, and it will start copying garbage to the + // PWM controller. I think it might be because of the virtual->physical memory mapping not being + // contiguous, so *pointer+1016 isn't "next door" to *pointer+1017 for some weird reason. + // However, that's still enough for 451.5 color instructions! If someone has more pixels than that + // to control, they can figure it out. I tried Hirst's message of having one CB per word, which + // seems like it might fix that, but I couldn't figure it out. + #define NUM_DATA_WORDS 1016 + struct control_data_s { + dma_cb_t cb[1]; + uint32_t sample[NUM_DATA_WORDS]; + }; + + struct control_data_s *ctl; + + // PWM waveform buffer (in words), 16 32-bit words are enough to hold 170 wire bits. + // That's OK if we only transmit from the FIFO, but for DMA, we will use a much larger size. + // 1024 (4096 bytes) should be enough for over 400 elements. It can be bumped up if you need more! + unsigned int PWMWaveform[NUM_DATA_WORDS]; + + void initHardware(); + void startTransfer(); + + void clearPWMBuffer(); + void setPWMBit(unsigned int bitPos, unsigned char bit); + + unsigned int mem_phys_to_virt(uint32_t phys); + unsigned int mem_virt_to_phys(void *virt); + void terminate(int dummy); + void fatal(char *fmt, ...); + void * map_peripheral(uint32_t base, uint32_t len); + void printBinary(unsigned int i, unsigned int bits); +}; + + + + + + + + + + + + +#endif /* LEDDEVICEWS2812S_H_ */ From 61da05e1081777ae4708f593669968159b0fe8a5 Mon Sep 17 00:00:00 2001 From: David Brodski Date: Wed, 17 Sep 2014 21:12:46 +0200 Subject: [PATCH 48/74] Moved defines into cpp file make include of h file smaller removed not needed includes fixed warnings (removed some ascii art for that) Former-commit-id: 71b16cf7e73a9463462820238d12069e4d1e6d6e --- libsrc/leddevice/LedDeviceWS2812s.cpp | 227 +++++++++++++++++++++- libsrc/leddevice/LedDeviceWS2812s.h | 260 +------------------------- 2 files changed, 226 insertions(+), 261 deletions(-) diff --git a/libsrc/leddevice/LedDeviceWS2812s.cpp b/libsrc/leddevice/LedDeviceWS2812s.cpp index 545519b0..d7fd19b4 100644 --- a/libsrc/leddevice/LedDeviceWS2812s.cpp +++ b/libsrc/leddevice/LedDeviceWS2812s.cpp @@ -9,11 +9,224 @@ // Linux includes #include +#include +#include +//#include //#include // hyperion local includes #include "LedDeviceWS2812s.h" +// ==== Defines and Vars ==== + +// Base addresses for GPIO, PWM, PWM clock, and DMA controllers (physical, not bus!) +// These will be "memory mapped" into virtual RAM so that they can be written and read directly. +// ------------------------------------------------------------------------------------------------- +#define DMA_BASE 0x20007000 +#define DMA_LEN 0x24 +#define PWM_BASE 0x2020C000 +#define PWM_LEN 0x28 +#define CLK_BASE 0x20101000 +#define CLK_LEN 0xA8 +#define GPIO_BASE 0x20200000 +#define GPIO_LEN 0xB4 + +// GPIO +// ------------------------------------------------------------------------------------------------- +#define GPFSEL0 0x20200000 // GPIO function select, pins 0-9 (bits 30-31 reserved) +#define GPFSEL1 0x20200004 // Pins 10-19 +#define GPFSEL2 0x20200008 // Pins 20-29 +#define GPFSEL3 0x2020000C // Pins 30-39 +#define GPFSEL4 0x20200010 // Pins 40-49 +#define GPFSEL5 0x20200014 // Pins 50-53 +#define GPSET0 0x2020001C // Set (turn on) pin +#define GPCLR0 0x20200028 // Clear (turn off) pin +#define GPPUD 0x20200094 // Internal pullup/pulldown resistor control +#define GPPUDCLK0 0x20200098 // PUD clock for pins 0-31 +#define GPPUDCLK1 0x2020009C // PUD clock for pins 32-53 + +// Memory offsets for the PWM clock register, which is undocumented! Please fix that, Broadcom! +// ------------------------------------------------------------------------------------------------- +#define PWM_CLK_CNTL 40 // Control (on/off) +#define PWM_CLK_DIV 41 // Divisor (bits 11:0 are *quantized* floating part, 31:12 integer part) + +// PWM Register Addresses (page 141) +// These are divided by 4 because the register offsets in the guide are in bytes (8 bits) but +// the pointers we use in this program are in words (32 bits). Buss' original defines are in +// word offsets, e.g. PWM_RNG1 was 4 and PWM_DAT1 was 5. This is functionally the same, but it +// matches the numbers supplied in the guide. +// ------------------------------------------------------------------------------------------------- +#define PWM_CTL 0x00 // Control Register +#define PWM_STA (0x04 / 4) // Status Register +#define PWM_DMAC (0x08 / 4) // DMA Control Register +#define PWM_RNG1 (0x10 / 4) // Channel 1 Range +#define PWM_DAT1 (0x14 / 4) // Channel 1 Data +#define PWM_FIF1 (0x18 / 4) // FIFO (for both channels - bytes are interleaved if both active) +#define PWM_RNG2 (0x20 / 4) // Channel 2 Range +#define PWM_DAT2 (0x24 / 4) // Channel 2 Data + +// PWM_CTL register bit offsets +// Note: Don't use MSEN1/2 for this purpose. It will screw things up. +// ------------------------------------------------------------------------------------------------- +#define PWM_CTL_MSEN2 15 // Channel 2 - 0: Use PWM algorithm. 1: Use M/S (serial) algorithm. +#define PWM_CTL_USEF2 13 // Channel 2 - 0: Use PWM_DAT2. 1: Use FIFO. +#define PWM_CTL_POLA2 12 // Channel 2 - Invert output polarity (if set, 0=high and 1=low) +#define PWM_CTL_SBIT2 11 // Channel 2 - Silence bit (default line state when not transmitting) +#define PWM_CTL_RPTL2 10 // Channel 2 - Repeat last data in FIFO +#define PWM_CTL_MODE2 9 // Channel 2 - Mode. 0=PWM, 1=Serializer +#define PWM_CTL_PWEN2 8 // Channel 2 - Enable PWM +#define PWM_CTL_CLRF1 6 // Clear FIFO +#define PWM_CTL_MSEN1 7 // Channel 1 - 0: Use PWM algorithm. 1: Use M/S (serial) algorithm. +#define PWM_CTL_USEF1 5 // Channel 1 - 0: Use PWM_DAT1. 1: Use FIFO. +#define PWM_CTL_POLA1 4 // Channel 1 - Invert output polarity (if set, 0=high and 1=low) +#define PWM_CTL_SBIT1 3 // Channel 1 - Silence bit (default line state when not transmitting) +#define PWM_CTL_RPTL1 2 // Channel 1 - Repeat last data in FIFO +#define PWM_CTL_MODE1 1 // Channel 1 - Mode. 0=PWM, 1=Serializer +#define PWM_CTL_PWEN1 0 // Channel 1 - Enable PWM + +// PWM_STA register bit offsets +// ------------------------------------------------------------------------------------------------- +#define PWM_STA_STA4 12 // Channel 4 State +#define PWM_STA_STA3 11 // Channel 3 State +#define PWM_STA_STA2 10 // Channel 2 State +#define PWM_STA_STA1 9 // Channel 1 State +#define PWM_STA_BERR 8 // Bus Error +#define PWM_STA_GAPO4 7 // Gap Occurred on Channel 4 +#define PWM_STA_GAPO3 6 // Gap Occurred on Channel 3 +#define PWM_STA_GAPO2 5 // Gap Occurred on Channel 2 +#define PWM_STA_GAPO1 4 // Gap Occurred on Channel 1 +#define PWM_STA_RERR1 3 // FIFO Read Error +#define PWM_STA_WERR1 2 // FIFO Write Error +#define PWM_STA_EMPT1 1 // FIFO Empty +#define PWM_STA_FULL1 0 // FIFO Full + +// PWM_DMAC bit offsets +// ------------------------------------------------------------------------------------------------- +#define PWM_DMAC_ENAB 31 // 0: DMA Disabled. 1: DMA Enabled. +#define PWM_DMAC_PANIC 8 // Bits 15:8. Threshold for PANIC signal. Default 7. +#define PWM_DMAC_DREQ 0 // Bits 7:0. Threshold for DREQ signal. Default 7. + +// PWM_RNG1, PWM_RNG2 +// -------------------------------------------------------------------------------------------------- +// Defines the transmission range. In PWM mode, evenly spaced pulses are sent within a period +// of length defined in these registers. In serial mode, serialized data is sent within the +// same period. The value is normally 32. If less, data will be truncated. If more, data will +// be padded with zeros. + +// DAT1, DAT2 +// -------------------------------------------------------------------------------------------------- +// NOTE: These registers are not useful for our purposes - we will use the FIFO instead! +// Stores 32 bits of data to be sent when USEF1/USEF2 is 0. In PWM mode, defines how many +// pulses will be sent within the period specified in PWM_RNG1/PWM_RNG2. In serializer mode, +// defines a 32-bit word to be transmitted. + +// FIF1 +// -------------------------------------------------------------------------------------------------- +// 32-bit-wide register used to "stuff" the FIFO, which has 16 32-bit words. (So, if you write +// it 16 times, it will fill the FIFO.) +// See also: PWM_STA_EMPT1 (FIFO empty) +// PWM_STA_FULL1 (FIFO full) +// PWM_CTL_CLRF1 (Clear FIFO) + +// DMA +// -------------------------------------------------------------------------------------------------- +// DMA registers (divided by four to convert form word to byte offsets, as with the PWM registers) +#define DMA_CS (0x00 / 4) // Control & Status register +#define DMA_CONBLK_AD (0x04 / 4) // Address of Control Block (must be 256-BYTE ALIGNED!!!) +#define DMA_TI (0x08 / 4) // Transfer Information (populated from CB) +#define DMA_SOURCE_AD (0x0C / 4) // Source address, populated from CB. Physical address. +#define DMA_DEST_AD (0x10 / 4) // Destination address, populated from CB. Bus address. +#define DMA_TXFR_LEN (0x14 / 4) // Transfer length, populated from CB +#define DMA_STRIDE (0x18 / 4) // Stride, populated from CB +#define DMA_NEXTCONBK (0x1C / 4) // Next control block address, populated from CB +#define DMA_DEBUG (0x20 / 4) // Debug settings + +// DMA Control & Status register bit offsets +#define DMA_CS_RESET 31 // Reset the controller for this channel +#define DMA_CS_ABORT 30 // Set to abort transfer +#define DMA_CS_DISDEBUG 29 // Disable debug pause signal +#define DMA_CS_WAIT_FOR 28 // Wait for outstanding writes +#define DMA_CS_PANIC_PRI 20 // Panic priority (bits 23:20), default 7 +#define DMA_CS_PRIORITY 16 // AXI priority level (bits 19:16), default 7 +#define DMA_CS_ERROR 8 // Set when there's been an error +#define DMA_CS_WAITING_FOR 6 // Set when the channel's waiting for a write to be accepted +#define DMA_CS_DREQ_STOPS_DMA 5 // Set when the DMA is paused because DREQ is inactive +#define DMA_CS_PAUSED 4 // Set when the DMA is paused (active bit cleared, etc.) +#define DMA_CS_DREQ 3 // Set when DREQ line is high +#define DMA_CS_INT 2 // If INTEN is set, this will be set on CB transfer end +#define DMA_CS_END 1 // Set when the current control block is finished +#define DMA_CS_ACTIVE 0 // Enable DMA (CB_ADDR must not be 0) +// Default CS word +#define DMA_CS_CONFIGWORD (8 << DMA_CS_PANIC_PRI) | \ + (8 << DMA_CS_PRIORITY) | \ + (1 << DMA_CS_WAIT_FOR) + +// DREQ lines (page 61, most DREQs omitted) +#define DMA_DREQ_ALWAYS 0 +#define DMA_DREQ_PCM_TX 2 +#define DMA_DREQ_PCM_RX 3 +#define DMA_DREQ_PWM 5 +#define DMA_DREQ_SPI_TX 6 +#define DMA_DREQ_SPI_RX 7 +#define DMA_DREQ_BSC_TX 8 +#define DMA_DREQ_BSC_RX 9 + +// DMA Transfer Information register bit offsets +// We don't write DMA_TI directly. It's populated from the TI field in a control block. +#define DMA_TI_NO_WIDE_BURSTS 26 // Don't do wide writes in 2-beat bursts +#define DMA_TI_WAITS 21 // Wait this many cycles after end of each read/write +#define DMA_TI_PERMAP 16 // Peripheral # whose ready signal controls xfer rate (pwm=5) +#define DMA_TI_BURST_LENGTH 12 // Length of burst in words (bits 15:12) +#define DMA_TI_SRC_IGNORE 11 // Don't perform source reads (for fast cache fill) +#define DMA_TI_SRC_DREQ 10 // Peripheral in PERMAP gates source reads +#define DMA_TI_SRC_WIDTH 9 // Source transfer width - 0=32 bits, 1=128 bits +#define DMA_TI_SRC_INC 8 // Source address += SRC_WITH after each read +#define DMA_TI_DEST_IGNORE 7 // Don't perform destination writes +#define DMA_TI_DEST_DREQ 6 // Peripheral in PERMAP gates destination writes +#define DMA_TI_DEST_WIDTH 5 // Destination transfer width - 0=32 bits, 1=128 bits +#define DMA_TI_DEST_INC 4 // Dest address += DEST_WIDTH after each read +#define DMA_TI_WAIT_RESP 3 // Wait for write response +#define DMA_TI_TDMODE 1 // 2D striding mode +#define DMA_TI_INTEN 0 // Interrupt enable +// Default TI word +#define DMA_TI_CONFIGWORD (1 << DMA_TI_NO_WIDE_BURSTS) | \ + (1 << DMA_TI_SRC_INC) | \ + (1 << DMA_TI_DEST_DREQ) | \ + (1 << DMA_TI_WAIT_RESP) | \ + (1 << DMA_TI_INTEN) | \ + (DMA_DREQ_PWM << DMA_TI_PERMAP) + +// DMA Debug register bit offsets +#define DMA_DEBUG_LITE 28 // Whether the controller is "Lite" +#define DMA_DEBUG_VERSION 25 // DMA Version (bits 27:25) +#define DMA_DEBUG_DMA_STATE 16 // DMA State (bits 24:16) +#define DMA_DEBUG_DMA_ID 8 // DMA controller's AXI bus ID (bits 15:8) +#define DMA_DEBUG_OUTSTANDING_WRITES 4 // Outstanding writes (bits 7:4) +#define DMA_DEBUG_READ_ERROR 2 // Slave read response error (clear by setting) +#define DMA_DEBUG_FIFO_ERROR 1 // Operational read FIFO error (clear by setting) +#define DMA_DEBUG_READ_LAST_NOT_SET 0 // AXI bus read last signal not set (clear by setting) + + + +#define PAGE_SIZE 4096 // Size of a RAM page to be allocated +#define PAGE_SHIFT 12 // This is used for address translation +#define NUM_PAGES ((sizeof(struct control_data_s) + PAGE_SIZE - 1) >> PAGE_SHIFT) + +#define SETBIT(word, bit) word |= 1< -// \/ \/ \/ \/ \/ \/ -// ================================================================================================= +// ==== Init Hardware ==== void LedDeviceWS2812s::initHardware() { int pid; @@ -341,7 +547,8 @@ void LedDeviceWS2812s::initHardware() { fatal("Failed to open %s: %m\n", pagemap_fn); } - if (lseek(fd, (unsigned long)virtbase >> 9, SEEK_SET) != (unsigned long)virtbase >> 9) { + off_t newOffset = (unsigned long)virtbase >> 9; + if (lseek(fd, newOffset, SEEK_SET) != newOffset) { fatal("Failed to seek on %s: %m\n", pagemap_fn); } diff --git a/libsrc/leddevice/LedDeviceWS2812s.h b/libsrc/leddevice/LedDeviceWS2812s.h index 2dca0fb0..d92ef39f 100644 --- a/libsrc/leddevice/LedDeviceWS2812s.h +++ b/libsrc/leddevice/LedDeviceWS2812s.h @@ -98,233 +98,18 @@ // Adafruit's NeoPixel driver: // https://github.com/adafruit/Adafruit_NeoPixel/blob/master/Adafruit_NeoPixel.cpp - -// ================================================================================================= -// .___ .__ .___ -// | | ____ ____ | | __ __ __| _/____ ______ -// | |/ \_/ ___\| | | | \/ __ |/ __ \ / ___/ -// | | | \ \___| |_| | / /_/ \ ___/ \___ \ -// |___|___| /\___ >____/____/\____ |\___ >____ > -// \/ \/ \/ \/ \/ -// ================================================================================================= - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - // Hyperion includes #include -// ================================================================================================= -// ________ _____.__ ____ ____ ____ -// \______ \ _____/ ____\__| ____ ____ ______ / _ \ \ \ / /____ _______ ______ -// | | \_/ __ \ __\| |/ \_/ __ \ / ___/ > _ __| |__|___| /\___ >____ > \_____\ \ \___/ (____ /__| /____ > -// \/ \/ \/ \/ \/ \/ \/ \/ -// ================================================================================================= - -// Base addresses for GPIO, PWM, PWM clock, and DMA controllers (physical, not bus!) -// These will be "memory mapped" into virtual RAM so that they can be written and read directly. -// ------------------------------------------------------------------------------------------------- -#define DMA_BASE 0x20007000 -#define DMA_LEN 0x24 -#define PWM_BASE 0x2020C000 -#define PWM_LEN 0x28 -#define CLK_BASE 0x20101000 -#define CLK_LEN 0xA8 -#define GPIO_BASE 0x20200000 -#define GPIO_LEN 0xB4 - -// GPIO -// ------------------------------------------------------------------------------------------------- -#define GPFSEL0 0x20200000 // GPIO function select, pins 0-9 (bits 30-31 reserved) -#define GPFSEL1 0x20200004 // Pins 10-19 -#define GPFSEL2 0x20200008 // Pins 20-29 -#define GPFSEL3 0x2020000C // Pins 30-39 -#define GPFSEL4 0x20200010 // Pins 40-49 -#define GPFSEL5 0x20200014 // Pins 50-53 -#define GPSET0 0x2020001C // Set (turn on) pin -#define GPCLR0 0x20200028 // Clear (turn off) pin -#define GPPUD 0x20200094 // Internal pullup/pulldown resistor control -#define GPPUDCLK0 0x20200098 // PUD clock for pins 0-31 -#define GPPUDCLK1 0x2020009C // PUD clock for pins 32-53 - -// Memory offsets for the PWM clock register, which is undocumented! Please fix that, Broadcom! -// ------------------------------------------------------------------------------------------------- -#define PWM_CLK_CNTL 40 // Control (on/off) -#define PWM_CLK_DIV 41 // Divisor (bits 11:0 are *quantized* floating part, 31:12 integer part) - -// PWM Register Addresses (page 141) -// These are divided by 4 because the register offsets in the guide are in bytes (8 bits) but -// the pointers we use in this program are in words (32 bits). Buss' original defines are in -// word offsets, e.g. PWM_RNG1 was 4 and PWM_DAT1 was 5. This is functionally the same, but it -// matches the numbers supplied in the guide. -// ------------------------------------------------------------------------------------------------- -#define PWM_CTL 0x00 // Control Register -#define PWM_STA (0x04 / 4) // Status Register -#define PWM_DMAC (0x08 / 4) // DMA Control Register -#define PWM_RNG1 (0x10 / 4) // Channel 1 Range -#define PWM_DAT1 (0x14 / 4) // Channel 1 Data -#define PWM_FIF1 (0x18 / 4) // FIFO (for both channels - bytes are interleaved if both active) -#define PWM_RNG2 (0x20 / 4) // Channel 2 Range -#define PWM_DAT2 (0x24 / 4) // Channel 2 Data - -// PWM_CTL register bit offsets -// Note: Don't use MSEN1/2 for this purpose. It will screw things up. -// ------------------------------------------------------------------------------------------------- -#define PWM_CTL_MSEN2 15 // Channel 2 - 0: Use PWM algorithm. 1: Use M/S (serial) algorithm. -#define PWM_CTL_USEF2 13 // Channel 2 - 0: Use PWM_DAT2. 1: Use FIFO. -#define PWM_CTL_POLA2 12 // Channel 2 - Invert output polarity (if set, 0=high and 1=low) -#define PWM_CTL_SBIT2 11 // Channel 2 - Silence bit (default line state when not transmitting) -#define PWM_CTL_RPTL2 10 // Channel 2 - Repeat last data in FIFO -#define PWM_CTL_MODE2 9 // Channel 2 - Mode. 0=PWM, 1=Serializer -#define PWM_CTL_PWEN2 8 // Channel 2 - Enable PWM -#define PWM_CTL_CLRF1 6 // Clear FIFO -#define PWM_CTL_MSEN1 7 // Channel 1 - 0: Use PWM algorithm. 1: Use M/S (serial) algorithm. -#define PWM_CTL_USEF1 5 // Channel 1 - 0: Use PWM_DAT1. 1: Use FIFO. -#define PWM_CTL_POLA1 4 // Channel 1 - Invert output polarity (if set, 0=high and 1=low) -#define PWM_CTL_SBIT1 3 // Channel 1 - Silence bit (default line state when not transmitting) -#define PWM_CTL_RPTL1 2 // Channel 1 - Repeat last data in FIFO -#define PWM_CTL_MODE1 1 // Channel 1 - Mode. 0=PWM, 1=Serializer -#define PWM_CTL_PWEN1 0 // Channel 1 - Enable PWM - -// PWM_STA register bit offsets -// ------------------------------------------------------------------------------------------------- -#define PWM_STA_STA4 12 // Channel 4 State -#define PWM_STA_STA3 11 // Channel 3 State -#define PWM_STA_STA2 10 // Channel 2 State -#define PWM_STA_STA1 9 // Channel 1 State -#define PWM_STA_BERR 8 // Bus Error -#define PWM_STA_GAPO4 7 // Gap Occurred on Channel 4 -#define PWM_STA_GAPO3 6 // Gap Occurred on Channel 3 -#define PWM_STA_GAPO2 5 // Gap Occurred on Channel 2 -#define PWM_STA_GAPO1 4 // Gap Occurred on Channel 1 -#define PWM_STA_RERR1 3 // FIFO Read Error -#define PWM_STA_WERR1 2 // FIFO Write Error -#define PWM_STA_EMPT1 1 // FIFO Empty -#define PWM_STA_FULL1 0 // FIFO Full - -// PWM_DMAC bit offsets -// ------------------------------------------------------------------------------------------------- -#define PWM_DMAC_ENAB 31 // 0: DMA Disabled. 1: DMA Enabled. -#define PWM_DMAC_PANIC 8 // Bits 15:8. Threshold for PANIC signal. Default 7. -#define PWM_DMAC_DREQ 0 // Bits 7:0. Threshold for DREQ signal. Default 7. - -// PWM_RNG1, PWM_RNG2 -// -------------------------------------------------------------------------------------------------- -// Defines the transmission range. In PWM mode, evenly spaced pulses are sent within a period -// of length defined in these registers. In serial mode, serialized data is sent within the -// same period. The value is normally 32. If less, data will be truncated. If more, data will -// be padded with zeros. - -// DAT1, DAT2 -// -------------------------------------------------------------------------------------------------- -// NOTE: These registers are not useful for our purposes - we will use the FIFO instead! -// Stores 32 bits of data to be sent when USEF1/USEF2 is 0. In PWM mode, defines how many -// pulses will be sent within the period specified in PWM_RNG1/PWM_RNG2. In serializer mode, -// defines a 32-bit word to be transmitted. - -// FIF1 -// -------------------------------------------------------------------------------------------------- -// 32-bit-wide register used to "stuff" the FIFO, which has 16 32-bit words. (So, if you write -// it 16 times, it will fill the FIFO.) -// See also: PWM_STA_EMPT1 (FIFO empty) -// PWM_STA_FULL1 (FIFO full) -// PWM_CTL_CLRF1 (Clear FIFO) - -// DMA -// -------------------------------------------------------------------------------------------------- -// DMA registers (divided by four to convert form word to byte offsets, as with the PWM registers) -#define DMA_CS (0x00 / 4) // Control & Status register -#define DMA_CONBLK_AD (0x04 / 4) // Address of Control Block (must be 256-BYTE ALIGNED!!!) -#define DMA_TI (0x08 / 4) // Transfer Information (populated from CB) -#define DMA_SOURCE_AD (0x0C / 4) // Source address, populated from CB. Physical address. -#define DMA_DEST_AD (0x10 / 4) // Destination address, populated from CB. Bus address. -#define DMA_TXFR_LEN (0x14 / 4) // Transfer length, populated from CB -#define DMA_STRIDE (0x18 / 4) // Stride, populated from CB -#define DMA_NEXTCONBK (0x1C / 4) // Next control block address, populated from CB -#define DMA_DEBUG (0x20 / 4) // Debug settings - -// DMA Control & Status register bit offsets -#define DMA_CS_RESET 31 // Reset the controller for this channel -#define DMA_CS_ABORT 30 // Set to abort transfer -#define DMA_CS_DISDEBUG 29 // Disable debug pause signal -#define DMA_CS_WAIT_FOR 28 // Wait for outstanding writes -#define DMA_CS_PANIC_PRI 20 // Panic priority (bits 23:20), default 7 -#define DMA_CS_PRIORITY 16 // AXI priority level (bits 19:16), default 7 -#define DMA_CS_ERROR 8 // Set when there's been an error -#define DMA_CS_WAITING_FOR 6 // Set when the channel's waiting for a write to be accepted -#define DMA_CS_DREQ_STOPS_DMA 5 // Set when the DMA is paused because DREQ is inactive -#define DMA_CS_PAUSED 4 // Set when the DMA is paused (active bit cleared, etc.) -#define DMA_CS_DREQ 3 // Set when DREQ line is high -#define DMA_CS_INT 2 // If INTEN is set, this will be set on CB transfer end -#define DMA_CS_END 1 // Set when the current control block is finished -#define DMA_CS_ACTIVE 0 // Enable DMA (CB_ADDR must not be 0) -// Default CS word -#define DMA_CS_CONFIGWORD (8 << DMA_CS_PANIC_PRI) | \ - (8 << DMA_CS_PRIORITY) | \ - (1 << DMA_CS_WAIT_FOR) - -// DREQ lines (page 61, most DREQs omitted) -#define DMA_DREQ_ALWAYS 0 -#define DMA_DREQ_PCM_TX 2 -#define DMA_DREQ_PCM_RX 3 -#define DMA_DREQ_PWM 5 -#define DMA_DREQ_SPI_TX 6 -#define DMA_DREQ_SPI_RX 7 -#define DMA_DREQ_BSC_TX 8 -#define DMA_DREQ_BSC_RX 9 - -// DMA Transfer Information register bit offsets -// We don't write DMA_TI directly. It's populated from the TI field in a control block. -#define DMA_TI_NO_WIDE_BURSTS 26 // Don't do wide writes in 2-beat bursts -#define DMA_TI_WAITS 21 // Wait this many cycles after end of each read/write -#define DMA_TI_PERMAP 16 // Peripheral # whose ready signal controls xfer rate (pwm=5) -#define DMA_TI_BURST_LENGTH 12 // Length of burst in words (bits 15:12) -#define DMA_TI_SRC_IGNORE 11 // Don't perform source reads (for fast cache fill) -#define DMA_TI_SRC_DREQ 10 // Peripheral in PERMAP gates source reads -#define DMA_TI_SRC_WIDTH 9 // Source transfer width - 0=32 bits, 1=128 bits -#define DMA_TI_SRC_INC 8 // Source address += SRC_WITH after each read -#define DMA_TI_DEST_IGNORE 7 // Don't perform destination writes -#define DMA_TI_DEST_DREQ 6 // Peripheral in PERMAP gates destination writes -#define DMA_TI_DEST_WIDTH 5 // Destination transfer width - 0=32 bits, 1=128 bits -#define DMA_TI_DEST_INC 4 // Dest address += DEST_WIDTH after each read -#define DMA_TI_WAIT_RESP 3 // Wait for write response -#define DMA_TI_TDMODE 1 // 2D striding mode -#define DMA_TI_INTEN 0 // Interrupt enable -// Default TI word -#define DMA_TI_CONFIGWORD (1 << DMA_TI_NO_WIDE_BURSTS) | \ - (1 << DMA_TI_SRC_INC) | \ - (1 << DMA_TI_DEST_DREQ) | \ - (1 << DMA_TI_WAIT_RESP) | \ - (1 << DMA_TI_INTEN) | \ - (DMA_DREQ_PWM << DMA_TI_PERMAP) - -// DMA Debug register bit offsets -#define DMA_DEBUG_LITE 28 // Whether the controller is "Lite" -#define DMA_DEBUG_VERSION 25 // DMA Version (bits 27:25) -#define DMA_DEBUG_DMA_STATE 16 // DMA State (bits 24:16) -#define DMA_DEBUG_DMA_ID 8 // DMA controller's AXI bus ID (bits 15:8) -#define DMA_DEBUG_OUTSTANDING_WRITES 4 // Outstanding writes (bits 7:4) -#define DMA_DEBUG_READ_ERROR 2 // Slave read response error (clear by setting) -#define DMA_DEBUG_FIFO_ERROR 1 // Operational read FIFO error (clear by setting) -#define DMA_DEBUG_READ_LAST_NOT_SET 0 // AXI bus read last signal not set (clear by setting) +// The page map contains pointers to memory that we will allocate below. It uses two pointers +// per address. This is because the software (this program) deals only in virtual addresses, +// whereas the DMA controller can only access RAM via physical address. (If that's not confusing +// enough, it writes to peripherals by their bus addresses.) +typedef struct { + uint8_t *virtaddr; + uint32_t physaddr; +} page_map_t; // Control Block (CB) - this tells the DMA controller what to do. typedef struct { @@ -338,33 +123,6 @@ typedef struct { pad[2]; // These are "reserved" (unused) } dma_cb_t; -// The page map contains pointers to memory that we will allocate below. It uses two pointers -// per address. This is because the software (this program) deals only in virtual addresses, -// whereas the DMA controller can only access RAM via physical address. (If that's not confusing -// enough, it writes to peripherals by their bus addresses.) -typedef struct { - uint8_t *virtaddr; - uint32_t physaddr; -} page_map_t; - - -#define PAGE_SIZE 4096 // Size of a RAM page to be allocated -#define PAGE_SHIFT 12 // This is used for address translation -#define NUM_PAGES ((sizeof(struct control_data_s) + PAGE_SIZE - 1) >> PAGE_SHIFT) - -#define SETBIT(word, bit) word |= 1< Date: Wed, 17 Sep 2014 21:20:36 +0200 Subject: [PATCH 49/74] Rename WS2812s to WS2812b (spelling error) Former-commit-id: 83c92c9ef99d45b8683860f199a14952c05d0f5d --- libsrc/leddevice/CMakeLists.txt | 4 +-- libsrc/leddevice/LedDeviceFactory.cpp | 8 ++--- ...DeviceWS2812s.cpp => LedDeviceWS2812b.cpp} | 32 +++++++++---------- ...{LedDeviceWS2812s.h => LedDeviceWS2812b.h} | 12 +++---- 4 files changed, 28 insertions(+), 28 deletions(-) rename libsrc/leddevice/{LedDeviceWS2812s.cpp => LedDeviceWS2812b.cpp} (97%) rename libsrc/leddevice/{LedDeviceWS2812s.h => LedDeviceWS2812b.h} (98%) diff --git a/libsrc/leddevice/CMakeLists.txt b/libsrc/leddevice/CMakeLists.txt index 0efbd18c..abe28bf5 100755 --- a/libsrc/leddevice/CMakeLists.txt +++ b/libsrc/leddevice/CMakeLists.txt @@ -70,11 +70,11 @@ endif(ENABLE_SPIDEV) SET(Leddevice_HEADERS ${Leddevice_HEADERS} - ${CURRENT_SOURCE_DIR}/LedDeviceWS2812s.h + ${CURRENT_SOURCE_DIR}/LedDeviceWS2812b.h ) SET(Leddevice_SOURCES ${Leddevice_SOURCES} - ${CURRENT_SOURCE_DIR}/LedDeviceWS2812s.cpp + ${CURRENT_SOURCE_DIR}/LedDeviceWS2812b.cpp ) if(ENABLE_TINKERFORGE) diff --git a/libsrc/leddevice/LedDeviceFactory.cpp b/libsrc/leddevice/LedDeviceFactory.cpp index abc0a2c7..0915b4fe 100755 --- a/libsrc/leddevice/LedDeviceFactory.cpp +++ b/libsrc/leddevice/LedDeviceFactory.cpp @@ -31,7 +31,7 @@ #include "LedDevicePhilipsHue.h" #include "LedDeviceTpm2.h" -#include "LedDeviceWS2812s.h" +#include "LedDeviceWS2812b.h" LedDevice * LedDeviceFactory::construct(const Json::Value & deviceConfig) { @@ -183,10 +183,10 @@ LedDevice * LedDeviceFactory::construct(const Json::Value & deviceConfig) LedDeviceTpm2* deviceTpm2 = new LedDeviceTpm2(output, rate); deviceTpm2->open(); device = deviceTpm2; - }else if (type == "ws2812s") + }else if (type == "ws2812b") { - LedDeviceWS2812s * ledDeviceWS2812s = new LedDeviceWS2812s(); - device = ledDeviceWS2812s; + LedDeviceWS2812b * ledDeviceWS2812b = new LedDeviceWS2812b(); + device = ledDeviceWS2812b; } else { diff --git a/libsrc/leddevice/LedDeviceWS2812s.cpp b/libsrc/leddevice/LedDeviceWS2812b.cpp similarity index 97% rename from libsrc/leddevice/LedDeviceWS2812s.cpp rename to libsrc/leddevice/LedDeviceWS2812b.cpp index d7fd19b4..2abbb1b9 100644 --- a/libsrc/leddevice/LedDeviceWS2812s.cpp +++ b/libsrc/leddevice/LedDeviceWS2812b.cpp @@ -1,4 +1,4 @@ -// For license and other informations see LedDeviceWS2812s.h +// For license and other informations see LedDeviceWS2812b.h // To activate: use led device "ws2812s" in the hyperion configuration // STL includes @@ -15,7 +15,7 @@ //#include // hyperion local includes -#include "LedDeviceWS2812s.h" +#include "LedDeviceWS2812b.h" // ==== Defines and Vars ==== @@ -227,7 +227,7 @@ -LedDeviceWS2812s::LedDeviceWS2812s() : +LedDeviceWS2812b::LedDeviceWS2812b() : LedDevice(), mLedCount(0) { @@ -237,7 +237,7 @@ LedDeviceWS2812s::LedDeviceWS2812s() : } -int LedDeviceWS2812s::write(const std::vector &ledValues) +int LedDeviceWS2812b::write(const std::vector &ledValues) { mLedCount = ledValues.size(); //printf("Set leds, number: %d\n", mLedCount); @@ -315,12 +315,12 @@ int LedDeviceWS2812s::write(const std::vector &ledValues) return 0; } -int LedDeviceWS2812s::switchOff() +int LedDeviceWS2812b::switchOff() { return write(std::vector(mLedCount, ColorRgb{0,0,0})); } -LedDeviceWS2812s::~LedDeviceWS2812s() +LedDeviceWS2812b::~LedDeviceWS2812b() { // Exit cleanly, freeing memory and stopping the DMA & PWM engines // We trap all signals (including Ctrl+C), so even if you don't get here, it terminates correctly @@ -340,7 +340,7 @@ LedDeviceWS2812s::~LedDeviceWS2812s() // Convenience functions // -------------------------------------------------------------------------------------------------- // Print some bits of a binary number (2nd arg is how many bits) -void LedDeviceWS2812s::printBinary(unsigned int i, unsigned int bits) { +void LedDeviceWS2812b::printBinary(unsigned int i, unsigned int bits) { int x; for(x=bits-1; x>=0; x--) { printf("%d", (i & (1 << x)) ? 1 : 0); @@ -378,7 +378,7 @@ static void udelay(int us) { // Shutdown functions // -------------------------------------------------------------------------------------------------- -void LedDeviceWS2812s::terminate(int dummy) { +void LedDeviceWS2812b::terminate(int dummy) { // Shut down the DMA controller if(dma_reg) { CLRBIT(dma_reg[DMA_CS], DMA_CS_ACTIVE); @@ -402,7 +402,7 @@ void LedDeviceWS2812s::terminate(int dummy) { //exit(1); } -void LedDeviceWS2812s::fatal(const char *fmt, ...) { +void LedDeviceWS2812b::fatal(const char *fmt, ...) { va_list ap; va_start(ap, fmt); vfprintf(stderr, fmt, ap); @@ -414,13 +414,13 @@ void LedDeviceWS2812s::fatal(const char *fmt, ...) { // Memory management // -------------------------------------------------------------------------------------------------- // Translate from virtual address to physical -unsigned int LedDeviceWS2812s::mem_virt_to_phys(void *virt) { +unsigned int LedDeviceWS2812b::mem_virt_to_phys(void *virt) { unsigned int offset = (uint8_t *)virt - virtbase; return page_map[offset >> PAGE_SHIFT].physaddr + (offset % PAGE_SIZE); } // Translate from physical address to virtual -unsigned int LedDeviceWS2812s::mem_phys_to_virt(uint32_t phys) { +unsigned int LedDeviceWS2812b::mem_phys_to_virt(uint32_t phys) { unsigned int pg_offset = phys & (PAGE_SIZE - 1); unsigned int pg_addr = phys - pg_offset; @@ -435,7 +435,7 @@ unsigned int LedDeviceWS2812s::mem_phys_to_virt(uint32_t phys) { } // Map a peripheral's IO memory into our virtual memory, so we can read/write it directly -void * LedDeviceWS2812s::map_peripheral(uint32_t base, uint32_t len) { +void * LedDeviceWS2812b::map_peripheral(uint32_t base, uint32_t len) { int fd = open("/dev/mem", O_RDWR); void * vaddr; @@ -450,7 +450,7 @@ void * LedDeviceWS2812s::map_peripheral(uint32_t base, uint32_t len) { } // Zero out the PWM waveform buffer -void LedDeviceWS2812s::clearPWMBuffer() { +void LedDeviceWS2812b::clearPWMBuffer() { memset(PWMWaveform, 0, NUM_DATA_WORDS * 4); // Times four because memset deals in bytes. } @@ -458,7 +458,7 @@ void LedDeviceWS2812s::clearPWMBuffer() { // The (31 - bitIdx) is so that we write the data backwards, correcting its endianness // This means getPWMBit will return something other than what was written, so it would be nice // if the logic that calls this function would figure it out instead. (However, that's trickier) -void LedDeviceWS2812s::setPWMBit(unsigned int bitPos, unsigned char bit) { +void LedDeviceWS2812b::setPWMBit(unsigned int bitPos, unsigned char bit) { // Fetch word the bit is in unsigned int wordOffset = (int)(bitPos / 32); @@ -480,7 +480,7 @@ void LedDeviceWS2812s::setPWMBit(unsigned int bitPos, unsigned char bit) { // ==== Init Hardware ==== -void LedDeviceWS2812s::initHardware() { +void LedDeviceWS2812b::initHardware() { int pid; int fd; char pagemap_fn[64]; @@ -722,7 +722,7 @@ void LedDeviceWS2812s::initHardware() { } // Begin the transfer -void LedDeviceWS2812s::startTransfer() { +void LedDeviceWS2812b::startTransfer() { // Enable DMA dma_reg[DMA_CONBLK_AD] = mem_virt_to_phys(ctl->cb); dma_reg[DMA_CS] = DMA_CS_CONFIGWORD | (1 << DMA_CS_ACTIVE); diff --git a/libsrc/leddevice/LedDeviceWS2812s.h b/libsrc/leddevice/LedDeviceWS2812b.h similarity index 98% rename from libsrc/leddevice/LedDeviceWS2812s.h rename to libsrc/leddevice/LedDeviceWS2812b.h index d92ef39f..65351056 100644 --- a/libsrc/leddevice/LedDeviceWS2812s.h +++ b/libsrc/leddevice/LedDeviceWS2812b.h @@ -1,5 +1,5 @@ -#ifndef LEDDEVICEWS2812S_H_ -#define LEDDEVICEWS2812S_H_ +#ifndef LEDDEVICEWS2812B_H_ +#define LEDDEVICEWS2812B_H_ #pragma once @@ -126,14 +126,14 @@ typedef struct { /// /// Implementation of the LedDevice interface for writing to Ws2801 led device. /// -class LedDeviceWS2812s : public LedDevice +class LedDeviceWS2812b : public LedDevice { public: /// /// Constructs the LedDevice for a string containing leds of the type WS2812 - LedDeviceWS2812s(); + LedDeviceWS2812b(); - ~LedDeviceWS2812s(); + ~LedDeviceWS2812b(); /// /// Writes the led color values to the led-device /// @@ -206,4 +206,4 @@ private: -#endif /* LEDDEVICEWS2812S_H_ */ +#endif /* LEDDEVICEWS2812B_H_ */ From a92967fa7c345d4e9b0ed7207194fe884c3b31fd Mon Sep 17 00:00:00 2001 From: David Brodski Date: Thu, 18 Sep 2014 10:28:23 +0200 Subject: [PATCH 50/74] Added benchmark define some code cleanup and speedups Former-commit-id: 8254c34e1d10c598e127f46635ae6bafcb97087a --- libsrc/leddevice/LedDeviceWS2812b.cpp | 68 ++++++++++++++++++++------- libsrc/leddevice/LedDeviceWS2812b.h | 11 ++++- 2 files changed, 61 insertions(+), 18 deletions(-) diff --git a/libsrc/leddevice/LedDeviceWS2812b.cpp b/libsrc/leddevice/LedDeviceWS2812b.cpp index 2abbb1b9..bcd9f48d 100644 --- a/libsrc/leddevice/LedDeviceWS2812b.cpp +++ b/libsrc/leddevice/LedDeviceWS2812b.cpp @@ -14,6 +14,10 @@ //#include //#include +#ifdef BENCHMARK + #include +#endif + // hyperion local includes #include "LedDeviceWS2812b.h" @@ -230,31 +234,40 @@ LedDeviceWS2812b::LedDeviceWS2812b() : LedDevice(), mLedCount(0) + +#ifdef BENCHMARK + , + runCount(0), + combinedNseconds(0), + shortestNseconds(2147483647) +#endif + { + //shortestNseconds = 2147483647; // Init PWM generator and clear LED buffer initHardware(); //clearLEDBuffer(); + printf("WS2812b init finished \n"); } int LedDeviceWS2812b::write(const std::vector &ledValues) { +#ifdef BENCHMARK + timespec timeStart; + timespec timeEnd; + clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &timeStart); +#endif + mLedCount = ledValues.size(); //printf("Set leds, number: %d\n", mLedCount); -// const unsigned dataLen = ledValues.size() * sizeof(ColorRgb); -// const uint8_t * dataPtr = reinterpret_cast(ledValues.data()); - // Clear out the PWM buffer // Disabled, because we will overwrite the buffer anyway. // Read data from LEDBuffer[], translate it into wire format, and write to PWMWaveform -// unsigned int LEDBuffeWordPos = 0; -// unsigned int PWMWaveformBitPos = 0; unsigned int colorBits = 0; // Holds the GRB color before conversion to wire bit pattern - unsigned char colorBit = 0; // Holds current bit out of colorBits to be processed unsigned int wireBit = 0; // Holds the current bit we will set in PWMWaveform -// Color_t color; for(size_t i=0; i &ledValues) // Iterate through color bits to get wire bits for(int j=23; j>=0; j--) { - colorBit = (colorBits & (1 << j)) ? 1 : 0; + unsigned char colorBit = (colorBits & (1 << j)) ? 1 : 0; // Holds current bit out of colorBits to be processed + + setPWMBit(wireBit++, 1); + setPWMBit(wireBit++, colorBit); + setPWMBit(wireBit++, 0); + /* old code for better understanding switch(colorBit) { case 1: //wireBits = 0b110; // High, High, Low @@ -279,13 +297,13 @@ int LedDeviceWS2812b::write(const std::vector &ledValues) setPWMBit(wireBit++, 0); setPWMBit(wireBit++, 0); break; - } + }*/ } } // Copy PWM waveform to DMA's data buffer //printf("Copying %d words to DMA data buffer\n", NUM_DATA_WORDS); - ctl = (struct control_data_s *)virtbase; + struct control_data_s *ctl = (struct control_data_s *)virtbase; dma_cb_t *cbp = ctl->cb; // 72 bits per pixel / 32 bits per word = 2.25 words per pixel @@ -298,10 +316,10 @@ int LedDeviceWS2812b::write(const std::vector &ledValues) // This block is a major CPU hog when there are lots of pixels to be transmitted. // It would go quicker with DMA. - for(unsigned int i = 0; i < (cbp->length / 4); i++) { - ctl->sample[i] = PWMWaveform[i]; - } - +// for(unsigned int i = 0; i < (cbp->length / 4); i++) { +// ctl->sample[i] = PWMWaveform[i]; +// } + memcpy ( ctl->sample, PWMWaveform, cbp->length ); // memcpy does the same and is potentially faster // Enable DMA and PWM engines, which should now send the data startTransfer(); @@ -312,6 +330,20 @@ int LedDeviceWS2812b::write(const std::vector &ledValues) //printf("Delay for %d μSec\n", (int)bitTimeUSec); //usleep((int)bitTimeUSec); +#ifdef BENCHMARK + clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &timeEnd); + timespec result; + + result.tv_sec = timeEnd.tv_sec - timeStart.tv_sec; + result.tv_nsec = timeEnd.tv_nsec - timeStart.tv_nsec; + if (result.tv_nsec < 0) { + result.tv_nsec = 1e9 - result.tv_nsec; + result.tv_sec -= 1; + } + runCount ++; + combinedNseconds += result.tv_nsec; + shortestNseconds = result.tv_nsec < shortestNseconds ? result.tv_nsec : shortestNseconds; +#endif return 0; } @@ -325,6 +357,10 @@ LedDeviceWS2812b::~LedDeviceWS2812b() // Exit cleanly, freeing memory and stopping the DMA & PWM engines // We trap all signals (including Ctrl+C), so even if you don't get here, it terminates correctly terminate(0); +#ifdef BENCHMARK + printf("WS2812b Benchmark results: Runs %d - Avarage %lu (n) - Minimum %ld (n)\n", + runCount, (runCount > 0 ? combinedNseconds / runCount : 0), shortestNseconds); +#endif } @@ -576,7 +612,7 @@ void LedDeviceWS2812b::initHardware() { // Set up control block // --------------------------------------------------------------- - ctl = (struct control_data_s *)virtbase; + struct control_data_s *ctl = (struct control_data_s *)virtbase; dma_cb_t *cbp = ctl->cb; // FIXME: Change this to use DEFINEs unsigned int phys_pwm_fifo_addr = 0x7e20c000 + 0x18; @@ -724,7 +760,7 @@ void LedDeviceWS2812b::initHardware() { // Begin the transfer void LedDeviceWS2812b::startTransfer() { // Enable DMA - dma_reg[DMA_CONBLK_AD] = mem_virt_to_phys(ctl->cb); + dma_reg[DMA_CONBLK_AD] = mem_virt_to_phys(((struct control_data_s *) virtbase)->cb); dma_reg[DMA_CS] = DMA_CS_CONFIGWORD | (1 << DMA_CS_ACTIVE); usleep(100); diff --git a/libsrc/leddevice/LedDeviceWS2812b.h b/libsrc/leddevice/LedDeviceWS2812b.h index 65351056..79216b9a 100644 --- a/libsrc/leddevice/LedDeviceWS2812b.h +++ b/libsrc/leddevice/LedDeviceWS2812b.h @@ -101,6 +101,7 @@ // Hyperion includes #include +//#define BENCHMARK // The page map contains pointers to memory that we will allocate below. It uses two pointers // per address. This is because the software (this program) deals only in virtual addresses, @@ -150,7 +151,7 @@ private: /// the number of leds (needed when switching off) size_t mLedCount; - page_map_t *page_map; // This will hold the page map, which we'll allocate below + page_map_t *page_map; // This will hold the page map, which we'll allocate uint8_t *virtbase; // Pointer to some virtual memory that will be allocated volatile unsigned int *pwm_reg; // PWM controller register set @@ -174,7 +175,7 @@ private: uint32_t sample[NUM_DATA_WORDS]; }; - struct control_data_s *ctl; + //struct control_data_s *ctl; // PWM waveform buffer (in words), 16 32-bit words are enough to hold 170 wire bits. // That's OK if we only transmit from the FIFO, but for DMA, we will use a much larger size. @@ -193,6 +194,12 @@ private: void fatal(const char *fmt, ...); void * map_peripheral(uint32_t base, uint32_t len); void printBinary(unsigned int i, unsigned int bits); + +#ifdef BENCHMARK + unsigned int runCount; + long combinedNseconds; + long shortestNseconds; +#endif }; From 5ff05d58fdfe86760acf1da764dcada75bc80601 Mon Sep 17 00:00:00 2001 From: David Brodski Date: Thu, 18 Sep 2014 20:56:32 +0200 Subject: [PATCH 51/74] fixed potential buffer overflow Former-commit-id: 2c9ea902fd563b909e6d0457c4957f80be86f93f --- libsrc/leddevice/LedDeviceWS2812b.cpp | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/libsrc/leddevice/LedDeviceWS2812b.cpp b/libsrc/leddevice/LedDeviceWS2812b.cpp index bcd9f48d..10df666a 100644 --- a/libsrc/leddevice/LedDeviceWS2812b.cpp +++ b/libsrc/leddevice/LedDeviceWS2812b.cpp @@ -269,6 +269,20 @@ int LedDeviceWS2812b::write(const std::vector &ledValues) unsigned int colorBits = 0; // Holds the GRB color before conversion to wire bit pattern unsigned int wireBit = 0; // Holds the current bit we will set in PWMWaveform + // Copy PWM waveform to DMA's data buffer + //printf("Copying %d words to DMA data buffer\n", NUM_DATA_WORDS); + struct control_data_s *ctl = (struct control_data_s *)virtbase; + dma_cb_t *cbp = ctl->cb; + + // 72 bits per pixel / 32 bits per word = 2.25 words per pixel + // Add 1 to make sure the PWM FIFO gets the message: "we're sending zeroes" + // Times 4 because DMA works in bytes, not words + cbp->length = ((mLedCount * 2.25) + 1) * 4; + if(cbp->length > NUM_DATA_WORDS * 4) { + cbp->length = NUM_DATA_WORDS * 4; + mLedCount = (NUM_DATA_WORDS - 1) / 2.25; + } + for(size_t i=0; i &ledValues) } } - // Copy PWM waveform to DMA's data buffer - //printf("Copying %d words to DMA data buffer\n", NUM_DATA_WORDS); - struct control_data_s *ctl = (struct control_data_s *)virtbase; - dma_cb_t *cbp = ctl->cb; - - // 72 bits per pixel / 32 bits per word = 2.25 words per pixel - // Add 1 to make sure the PWM FIFO gets the message: "we're sending zeroes" - // Times 4 because DMA works in bytes, not words - cbp->length = ((mLedCount * 2.25) + 1) * 4; - if(cbp->length > NUM_DATA_WORDS * 4) { - cbp->length = NUM_DATA_WORDS * 4; - } - // This block is a major CPU hog when there are lots of pixels to be transmitted. // It would go quicker with DMA. // for(unsigned int i = 0; i < (cbp->length / 4); i++) { From d41857e626053de54f4295ab987106ac6fec0fa0 Mon Sep 17 00:00:00 2001 From: David Brodski Date: Thu, 18 Sep 2014 23:41:46 +0200 Subject: [PATCH 52/74] Pre initialized bit pattern to speed things up Former-commit-id: 08dc5ee53854997060af0257b5cff324d29f87b5 --- libsrc/leddevice/LedDeviceWS2812b.cpp | 54 ++++++++++++++++++++++++--- libsrc/leddevice/LedDeviceWS2812b.h | 2 +- 2 files changed, 49 insertions(+), 7 deletions(-) diff --git a/libsrc/leddevice/LedDeviceWS2812b.cpp b/libsrc/leddevice/LedDeviceWS2812b.cpp index 10df666a..827a7f3d 100644 --- a/libsrc/leddevice/LedDeviceWS2812b.cpp +++ b/libsrc/leddevice/LedDeviceWS2812b.cpp @@ -247,6 +247,16 @@ LedDeviceWS2812b::LedDeviceWS2812b() : // Init PWM generator and clear LED buffer initHardware(); //clearLEDBuffer(); + + // init bit pattern, it is always 1X0 + unsigned int wireBit = 0; + + while ((wireBit + 3) < ((NUM_DATA_WORDS) * 4 * 8)){ + setPWMBit(wireBit++, 1); + setPWMBit(wireBit++, 0); // just init it with 0 + setPWMBit(wireBit++, 0); + } + printf("WS2812b init finished \n"); } @@ -267,7 +277,7 @@ int LedDeviceWS2812b::write(const std::vector &ledValues) // Read data from LEDBuffer[], translate it into wire format, and write to PWMWaveform unsigned int colorBits = 0; // Holds the GRB color before conversion to wire bit pattern - unsigned int wireBit = 0; // Holds the current bit we will set in PWMWaveform + unsigned int wireBit = 1; // Holds the current bit we will set in PWMWaveform, start with 1 and skip the other two for speed // Copy PWM waveform to DMA's data buffer //printf("Copying %d words to DMA data buffer\n", NUM_DATA_WORDS); @@ -277,12 +287,16 @@ int LedDeviceWS2812b::write(const std::vector &ledValues) // 72 bits per pixel / 32 bits per word = 2.25 words per pixel // Add 1 to make sure the PWM FIFO gets the message: "we're sending zeroes" // Times 4 because DMA works in bytes, not words - cbp->length = ((mLedCount * 2.25) + 1) * 4; + cbp->length = (mLedCount * 2.25) * 4; + //cbp->length = ((mLedCount * 2.25) + 1) * 4; if(cbp->length > NUM_DATA_WORDS * 4) { cbp->length = NUM_DATA_WORDS * 4; - mLedCount = (NUM_DATA_WORDS - 1) / 2.25; + mLedCount = NUM_DATA_WORDS / 2.25; + //mLedCount = (NUM_DATA_WORDS - 1) / 2.25; } + + for(size_t i=0; i &ledValues) for(int j=23; j>=0; j--) { unsigned char colorBit = (colorBits & (1 << j)) ? 1 : 0; // Holds current bit out of colorBits to be processed - setPWMBit(wireBit++, 1); - setPWMBit(wireBit++, colorBit); - setPWMBit(wireBit++, 0); + setPWMBit(wireBit, colorBit); + wireBit +=3; /* old code for better understanding switch(colorBit) { case 1: @@ -315,6 +328,25 @@ int LedDeviceWS2812b::write(const std::vector &ledValues) } } + //remove one to undo optimization + wireBit --; + + // fill up the bytes + int rest = 32 - wireBit % 32; + unsigned int oldwireBitValue = wireBit; + +// printBinary(PWMWaveform[(int)(oldwireBitValue / 32)], 32); +// printf(" pre\n"); + + // zero rest of the 4 bytes / int so that output is 0 (no data is send) + for (int i = 0; i < rest; i += 3){ + setPWMBit(wireBit, 0); + wireBit += 3; + } + +// printBinary(PWMWaveform[(int)(oldwireBitValue / 32)], 32); +// printf(" post\n"); + // This block is a major CPU hog when there are lots of pixels to be transmitted. // It would go quicker with DMA. // for(unsigned int i = 0; i < (cbp->length / 4); i++) { @@ -325,6 +357,16 @@ int LedDeviceWS2812b::write(const std::vector &ledValues) // Enable DMA and PWM engines, which should now send the data startTransfer(); + // restore bit pattern + wireBit = oldwireBitValue; + for (int i = 0; i < rest; i += 3){ + setPWMBit(wireBit, 1); + wireBit += 3; + } + +// printBinary(PWMWaveform[(int)(oldwireBitValue / 32)], 32); +// printf(" restored\n"); + // Wait long enough for the DMA transfer to finish // 3 RAM bits per wire bit, so 72 bits to send one color command. //float bitTimeUSec = (float)(NUM_DATA_WORDS * 32) * 0.4; // Bits sent * time to transmit one bit, which is 0.4μSec diff --git a/libsrc/leddevice/LedDeviceWS2812b.h b/libsrc/leddevice/LedDeviceWS2812b.h index 79216b9a..55ce250a 100644 --- a/libsrc/leddevice/LedDeviceWS2812b.h +++ b/libsrc/leddevice/LedDeviceWS2812b.h @@ -101,7 +101,7 @@ // Hyperion includes #include -//#define BENCHMARK +#define BENCHMARK // The page map contains pointers to memory that we will allocate below. It uses two pointers // per address. This is because the software (this program) deals only in virtual addresses, From 961bef22f598c5c2bba00540ac793739470443e5 Mon Sep 17 00:00:00 2001 From: David Brodski Date: Fri, 19 Sep 2014 02:00:32 +0200 Subject: [PATCH 53/74] Assembler version 1: use roll and bit clear instructions Former-commit-id: 4f27d34dd63c635a65ee33f2c368978d5b162974 --- libsrc/leddevice/LedDeviceWS2812b.cpp | 53 ++++++++++++++++++++++++++- libsrc/leddevice/LedDeviceWS2812b.h | 1 + 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/libsrc/leddevice/LedDeviceWS2812b.cpp b/libsrc/leddevice/LedDeviceWS2812b.cpp index 827a7f3d..84b7ac97 100644 --- a/libsrc/leddevice/LedDeviceWS2812b.cpp +++ b/libsrc/leddevice/LedDeviceWS2812b.cpp @@ -260,6 +260,31 @@ LedDeviceWS2812b::LedDeviceWS2812b() : printf("WS2812b init finished \n"); } +#ifdef WS2812_ASM_OPTI + +// rotate register, used to move the 1 around :-) +static inline __attribute__((always_inline)) +uint32_t arm_ror_imm(uint32_t v, uint32_t sh) { + uint32_t d; + asm ("ROR %[Rd], %[Rm], %[Is]" : [Rd] "=r" (d) : [Rm] "r" (v), [Is] "i" (sh)); + return d; +} + +static inline __attribute__((always_inline)) +uint32_t arm_ror(uint32_t v, uint32_t sh) { + uint32_t d; + asm ("ROR %[Rd], %[Rm], %[Rs]" : [Rd] "=r" (d) : [Rm] "r" (v), [Rs] "r" (sh)); + return d; +} + + +static inline __attribute__((always_inline)) +uint32_t arm_Bit_Clear_imm(uint32_t v, uint32_t v2) { + uint32_t d; + asm ("BIC %[Rd], %[Rm], %[Rs]" : [Rd] "=r" (d) : [Rm] "r" (v), [Rs] "r" (v2)); + return d; +} +#endif int LedDeviceWS2812b::write(const std::vector &ledValues) { @@ -295,6 +320,9 @@ int LedDeviceWS2812b::write(const std::vector &ledValues) //mLedCount = (NUM_DATA_WORDS - 1) / 2.25; } +#ifdef WS2812_ASM_OPTI + unsigned int startbitPattern = 0x40000000; // = 0100 0000 0000 0000 0000 0000 0000 0000 pattern +#endif for(size_t i=0; i &ledValues) // Iterate through color bits to get wire bits for(int j=23; j>=0; j--) { - unsigned char colorBit = (colorBits & (1 << j)) ? 1 : 0; // Holds current bit out of colorBits to be processed +#ifdef WS2812_ASM_OPTI + // Fetch word the bit is in + unsigned int wordOffset = (int)(wireBit / 32); + wireBit +=3; +// printBinary(startbitPattern, 32); +// printf(" %d\n", j); + if (colorBits & (1 << j)) { + PWMWaveform[wordOffset] |= startbitPattern; + } else { + PWMWaveform[wordOffset] = arm_Bit_Clear_imm(PWMWaveform[wordOffset], startbitPattern); + } + + startbitPattern = arm_ror_imm(startbitPattern, 3); + +#else + unsigned char colorBit = (colorBits & (1 << j)) ? 1 : 0; // Holds current bit out of colorBits to be processed setPWMBit(wireBit, colorBit); wireBit +=3; +#endif /* old code for better understanding switch(colorBit) { case 1: @@ -328,6 +372,11 @@ int LedDeviceWS2812b::write(const std::vector &ledValues) } } +#ifdef WS2812_ASM_OPTI + // calculate the bits manually since it is not needed with asm + //wireBit += mLedCount * 24 *3; + //printf(" %d\n", wireBit); +#endif //remove one to undo optimization wireBit --; @@ -344,6 +393,8 @@ int LedDeviceWS2812b::write(const std::vector &ledValues) wireBit += 3; } +// printBinary(PWMWaveform[(int)(oldwireBitValue / 32) -1 ], 32); +// printf(" post\n"); // printBinary(PWMWaveform[(int)(oldwireBitValue / 32)], 32); // printf(" post\n"); diff --git a/libsrc/leddevice/LedDeviceWS2812b.h b/libsrc/leddevice/LedDeviceWS2812b.h index 55ce250a..d95b7912 100644 --- a/libsrc/leddevice/LedDeviceWS2812b.h +++ b/libsrc/leddevice/LedDeviceWS2812b.h @@ -102,6 +102,7 @@ #include #define BENCHMARK +#define WS2812_ASM_OPTI // The page map contains pointers to memory that we will allocate below. It uses two pointers // per address. This is because the software (this program) deals only in virtual addresses, From 0e55169f42c5efc8ef3000bf245d3489c5a99c46 Mon Sep 17 00:00:00 2001 From: David Brodski Date: Fri, 19 Sep 2014 03:16:15 +0200 Subject: [PATCH 54/74] fixed strange bug with last led and pwm needs one more int Former-commit-id: c5e20fed3d84c19032afb64e66a5934fc3db701c --- libsrc/leddevice/LedDeviceWS2812b.cpp | 61 ++++++++++++++++++++++----- 1 file changed, 51 insertions(+), 10 deletions(-) diff --git a/libsrc/leddevice/LedDeviceWS2812b.cpp b/libsrc/leddevice/LedDeviceWS2812b.cpp index 84b7ac97..8a084b9c 100644 --- a/libsrc/leddevice/LedDeviceWS2812b.cpp +++ b/libsrc/leddevice/LedDeviceWS2812b.cpp @@ -270,6 +270,16 @@ uint32_t arm_ror_imm(uint32_t v, uint32_t sh) { return d; } +// rotate register, used to move the 1 around, add 1 to int counter on carry +static inline __attribute__((always_inline)) +uint32_t arm_ror_imm_add_on_carry(uint32_t v, uint32_t sh, uint32_t inc) { + uint32_t d; + asm ("RORS %[Rd], %[Rm], %[Is]\n\t" + "ADDCS %[Rd1], %[Rd1], #1" + : [Rd] "=r" (d), [Rd1] "+r" (inc): [Rm] "r" (v), [Is] "i" (sh)); + return d; +} + static inline __attribute__((always_inline)) uint32_t arm_ror(uint32_t v, uint32_t sh) { uint32_t d; @@ -312,12 +322,12 @@ int LedDeviceWS2812b::write(const std::vector &ledValues) // 72 bits per pixel / 32 bits per word = 2.25 words per pixel // Add 1 to make sure the PWM FIFO gets the message: "we're sending zeroes" // Times 4 because DMA works in bytes, not words - cbp->length = (mLedCount * 2.25) * 4; - //cbp->length = ((mLedCount * 2.25) + 1) * 4; +// cbp->length = (mLedCount * 2.25) * 4; + cbp->length = ((mLedCount * 2.25) + 1) * 4; if(cbp->length > NUM_DATA_WORDS * 4) { cbp->length = NUM_DATA_WORDS * 4; - mLedCount = NUM_DATA_WORDS / 2.25; - //mLedCount = (NUM_DATA_WORDS - 1) / 2.25; +// mLedCount = NUM_DATA_WORDS / 2.25; + mLedCount = (NUM_DATA_WORDS - 1) / 2.25; } #ifdef WS2812_ASM_OPTI @@ -380,19 +390,39 @@ int LedDeviceWS2812b::write(const std::vector &ledValues) //remove one to undo optimization wireBit --; - // fill up the bytes - int rest = 32 - wireBit % 32; - unsigned int oldwireBitValue = wireBit; - -// printBinary(PWMWaveform[(int)(oldwireBitValue / 32)], 32); +// printBinary(PWMWaveform[(int)(wireBit / 32)], 32); // printf(" pre\n"); +#ifdef WS2812_ASM_OPTI + int rest = 32 - wireBit % 32; // 64: 32 - used Bits + startbitPattern = (1 << (rest-1)); // set new bitpattern to start at the benigining of one bit (3 bit in wave form) + rest += 32; // add one int extra for pwm + +// printBinary(startbitPattern, 32); +// printf(" startbit\n"); + + unsigned int oldwireBitValue = wireBit; + unsigned int oldbitPattern = startbitPattern; + + // zero rest of the 4 bytes / int so that output is 0 (no data is send) + for (int i = 0; i < rest; i += 3){ + unsigned int wordOffset = (int)(wireBit / 32); + wireBit += 3; + PWMWaveform[wordOffset] = arm_Bit_Clear_imm(PWMWaveform[wordOffset], startbitPattern); + startbitPattern = arm_ror_imm(startbitPattern, 3); + } + +#else + // fill up the bytes + int rest = 32 - wireBit % 32 + 32; // 64: 32 - used Bits + 32 (one int extra for pwm) + unsigned int oldwireBitValue = wireBit; + // zero rest of the 4 bytes / int so that output is 0 (no data is send) for (int i = 0; i < rest; i += 3){ setPWMBit(wireBit, 0); wireBit += 3; } - +#endif // printBinary(PWMWaveform[(int)(oldwireBitValue / 32) -1 ], 32); // printf(" post\n"); // printBinary(PWMWaveform[(int)(oldwireBitValue / 32)], 32); @@ -410,10 +440,21 @@ int LedDeviceWS2812b::write(const std::vector &ledValues) // restore bit pattern wireBit = oldwireBitValue; + +#ifdef WS2812_ASM_OPTI + startbitPattern = oldbitPattern; + for (int i = 0; i < rest; i += 3){ + unsigned int wordOffset = (int)(wireBit / 32); + wireBit += 3; + PWMWaveform[wordOffset] |= startbitPattern; + startbitPattern = arm_ror_imm(startbitPattern, 3); + } +#else for (int i = 0; i < rest; i += 3){ setPWMBit(wireBit, 1); wireBit += 3; } +#endif // printBinary(PWMWaveform[(int)(oldwireBitValue / 32)], 32); // printf(" restored\n"); From f41a0fc977ecf3275de48ab62fb0149d03258ea7 Mon Sep 17 00:00:00 2001 From: David Brodski Date: Fri, 19 Sep 2014 03:18:26 +0200 Subject: [PATCH 55/74] removed benchmark Former-commit-id: e716fa337c4995a258c34200e14fd56ee8dae05c --- libsrc/leddevice/LedDeviceWS2812b.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libsrc/leddevice/LedDeviceWS2812b.h b/libsrc/leddevice/LedDeviceWS2812b.h index d95b7912..dd92065c 100644 --- a/libsrc/leddevice/LedDeviceWS2812b.h +++ b/libsrc/leddevice/LedDeviceWS2812b.h @@ -101,7 +101,7 @@ // Hyperion includes #include -#define BENCHMARK +//#define BENCHMARK #define WS2812_ASM_OPTI // The page map contains pointers to memory that we will allocate below. It uses two pointers From c0f0837b925b95c0b1aa7fb2409a27976714372f Mon Sep 17 00:00:00 2001 From: "T. van der Zwan" Date: Fri, 19 Sep 2014 16:20:01 +0200 Subject: [PATCH 56/74] Added missing include Former-commit-id: 1cf2f19a0a7e3fc9bef55979ce7e3221f3e66fec --- libsrc/leddevice/LedDeviceWS2812b.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libsrc/leddevice/LedDeviceWS2812b.cpp b/libsrc/leddevice/LedDeviceWS2812b.cpp index 8a084b9c..70f7e708 100644 --- a/libsrc/leddevice/LedDeviceWS2812b.cpp +++ b/libsrc/leddevice/LedDeviceWS2812b.cpp @@ -11,6 +11,7 @@ #include #include #include +#include //#include //#include From 8ad0e88e2f6b843a550a055f2996f50853e50439 Mon Sep 17 00:00:00 2001 From: "T. van der Zwan" Date: Fri, 19 Sep 2014 16:37:58 +0200 Subject: [PATCH 57/74] Minor reformat of code to match hyperion-style. Added ws2812b as option to disable for devices without dma-pwm Former-commit-id: 579f4426285fb537706a22af446f5280748f2ab7 --- CMakeLists.txt | 7 +- HyperionConfig.h.in | 3 + libsrc/leddevice/CMakeLists.txt | 2 + libsrc/leddevice/LedDeviceFactory.cpp | 11 +- libsrc/leddevice/LedDeviceWS2812b.cpp | 249 ++++++++++++-------------- libsrc/leddevice/LedDeviceWS2812b.h | 25 +-- 6 files changed, 143 insertions(+), 154 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 29c9476b..64cf1050 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,6 +13,9 @@ message(STATUS "ENABLE_DISPMANX = " ${ENABLE_DISPMANX}) option(ENABLE_SPIDEV "Enable the SPIDEV device" ON) message(STATUS "ENABLE_SPIDEV = " ${ENABLE_SPIDEV}) +option(ENABLE_WS2812BPWM "Enable the WS2812b-PWM device" ON) +message(STATUS "ENABLE_WS2812BPWM = " ${ENABLE_WS2812BPWM}) + option(ENABLE_V4L2 "Enable the V4L2 grabber" ON) message(STATUS "ENABLE_V4L2 = " ${ENABLE_V4L2}) @@ -22,9 +25,9 @@ message(STATUS "ENABLE_TINKERFORGE = " ${ENABLE_TINKERFORGE}) option(ENABLE_PROTOBUF "Enable PROTOBUF server" ON) message(STATUS "ENABLE_PROTOBUF = " ${ENABLE_PROTOBUF}) -if (ENABLE_V4L2 AND NOT ENABLE_PROTOBUF) +if(ENABLE_V4L2 AND NOT ENABLE_PROTOBUF) message(FATAL_ERROR "V4L2 grabber requires PROTOBUF. Disable V4L2 or enable PROTOBUF") -endif (ENABLE_V4L2 AND NOT ENABLE_PROTOBUF) +endif(ENABLE_V4L2 AND NOT ENABLE_PROTOBUF) # Createt the configuration file # configure a header file to pass some of the CMake settings diff --git a/HyperionConfig.h.in b/HyperionConfig.h.in index ff2f4f82..3739bba8 100644 --- a/HyperionConfig.h.in +++ b/HyperionConfig.h.in @@ -9,6 +9,9 @@ // Define to enable the spi-device #cmakedefine ENABLE_SPIDEV +// Define to enable the ws2812b-pwm-device +#cmakedefine ENABLE_WS2812BPWM + // Define to enable the spi-device #cmakedefine ENABLE_TINKERFORGE diff --git a/libsrc/leddevice/CMakeLists.txt b/libsrc/leddevice/CMakeLists.txt index abe28bf5..4304c874 100755 --- a/libsrc/leddevice/CMakeLists.txt +++ b/libsrc/leddevice/CMakeLists.txt @@ -68,6 +68,7 @@ if(ENABLE_SPIDEV) ) endif(ENABLE_SPIDEV) +if(ENABLE_WS2812BPWM) SET(Leddevice_HEADERS ${Leddevice_HEADERS} ${CURRENT_SOURCE_DIR}/LedDeviceWS2812b.h @@ -76,6 +77,7 @@ SET(Leddevice_SOURCES ${Leddevice_SOURCES} ${CURRENT_SOURCE_DIR}/LedDeviceWS2812b.cpp ) +endif(ENABLE_WS2812BPWM) if(ENABLE_TINKERFORGE) SET(Leddevice_HEADERS diff --git a/libsrc/leddevice/LedDeviceFactory.cpp b/libsrc/leddevice/LedDeviceFactory.cpp index 0915b4fe..fb602b02 100755 --- a/libsrc/leddevice/LedDeviceFactory.cpp +++ b/libsrc/leddevice/LedDeviceFactory.cpp @@ -31,7 +31,9 @@ #include "LedDevicePhilipsHue.h" #include "LedDeviceTpm2.h" -#include "LedDeviceWS2812b.h" +#ifdef ENABLE_WS2812BPWM + #include "LedDeviceWS2812b.h" +#endif LedDevice * LedDeviceFactory::construct(const Json::Value & deviceConfig) { @@ -180,14 +182,17 @@ LedDevice * LedDeviceFactory::construct(const Json::Value & deviceConfig) const std::string output = deviceConfig["output"].asString(); const unsigned rate = deviceConfig["rate"].asInt(); - LedDeviceTpm2* deviceTpm2 = new LedDeviceTpm2(output, rate); + LedDeviceTpm2 * deviceTpm2 = new LedDeviceTpm2(output, rate); deviceTpm2->open(); device = deviceTpm2; - }else if (type == "ws2812b") + } +#ifdef ENABLE_WS2812BPWM + else if (type == "ws2812b") { LedDeviceWS2812b * ledDeviceWS2812b = new LedDeviceWS2812b(); device = ledDeviceWS2812b; } +#endif else { std::cout << "Unable to create device " << type << std::endl; diff --git a/libsrc/leddevice/LedDeviceWS2812b.cpp b/libsrc/leddevice/LedDeviceWS2812b.cpp index 70f7e708..dc6bbe70 100644 --- a/libsrc/leddevice/LedDeviceWS2812b.cpp +++ b/libsrc/leddevice/LedDeviceWS2812b.cpp @@ -252,7 +252,8 @@ LedDeviceWS2812b::LedDeviceWS2812b() : // init bit pattern, it is always 1X0 unsigned int wireBit = 0; - while ((wireBit + 3) < ((NUM_DATA_WORDS) * 4 * 8)){ + while ((wireBit + 3) < ((NUM_DATA_WORDS) * 4 * 8)) + { setPWMBit(wireBit++, 1); setPWMBit(wireBit++, 0); // just init it with 0 setPWMBit(wireBit++, 0); @@ -264,36 +265,36 @@ LedDeviceWS2812b::LedDeviceWS2812b() : #ifdef WS2812_ASM_OPTI // rotate register, used to move the 1 around :-) -static inline __attribute__((always_inline)) -uint32_t arm_ror_imm(uint32_t v, uint32_t sh) { - uint32_t d; - asm ("ROR %[Rd], %[Rm], %[Is]" : [Rd] "=r" (d) : [Rm] "r" (v), [Is] "i" (sh)); - return d; +static inline __attribute__((always_inline)) uint32_t arm_ror_imm(uint32_t v, uint32_t sh) +{ + uint32_t d; + asm ("ROR %[Rd], %[Rm], %[Is]" : [Rd] "=r" (d) : [Rm] "r" (v), [Is] "i" (sh)); + return d; } // rotate register, used to move the 1 around, add 1 to int counter on carry -static inline __attribute__((always_inline)) -uint32_t arm_ror_imm_add_on_carry(uint32_t v, uint32_t sh, uint32_t inc) { - uint32_t d; - asm ("RORS %[Rd], %[Rm], %[Is]\n\t" - "ADDCS %[Rd1], %[Rd1], #1" - : [Rd] "=r" (d), [Rd1] "+r" (inc): [Rm] "r" (v), [Is] "i" (sh)); - return d; +static inline __attribute__((always_inline)) uint32_t arm_ror_imm_add_on_carry(uint32_t v, uint32_t sh, uint32_t inc) +{ + uint32_t d; + asm ("RORS %[Rd], %[Rm], %[Is]\n\t" + "ADDCS %[Rd1], %[Rd1], #1" + : [Rd] "=r" (d), [Rd1] "+r" (inc): [Rm] "r" (v), [Is] "i" (sh)); + return d; } -static inline __attribute__((always_inline)) -uint32_t arm_ror(uint32_t v, uint32_t sh) { - uint32_t d; - asm ("ROR %[Rd], %[Rm], %[Rs]" : [Rd] "=r" (d) : [Rm] "r" (v), [Rs] "r" (sh)); - return d; +static inline __attribute__((always_inline)) uint32_t arm_ror(uint32_t v, uint32_t sh) +{ + uint32_t d; + asm ("ROR %[Rd], %[Rm], %[Rs]" : [Rd] "=r" (d) : [Rm] "r" (v), [Rs] "r" (sh)); + return d; } -static inline __attribute__((always_inline)) -uint32_t arm_Bit_Clear_imm(uint32_t v, uint32_t v2) { - uint32_t d; - asm ("BIC %[Rd], %[Rm], %[Rs]" : [Rd] "=r" (d) : [Rm] "r" (v), [Rs] "r" (v2)); - return d; +static inline __attribute__((always_inline)) uint32_t arm_Bit_Clear_imm(uint32_t v, uint32_t v2) +{ + uint32_t d; + asm ("BIC %[Rd], %[Rm], %[Rs]" : [Rd] "=r" (d) : [Rm] "r" (v), [Rs] "r" (v2)); + return d; } #endif @@ -306,10 +307,6 @@ int LedDeviceWS2812b::write(const std::vector &ledValues) #endif mLedCount = ledValues.size(); - //printf("Set leds, number: %d\n", mLedCount); - - // Clear out the PWM buffer - // Disabled, because we will overwrite the buffer anyway. // Read data from LEDBuffer[], translate it into wire format, and write to PWMWaveform unsigned int colorBits = 0; // Holds the GRB color before conversion to wire bit pattern @@ -323,11 +320,10 @@ int LedDeviceWS2812b::write(const std::vector &ledValues) // 72 bits per pixel / 32 bits per word = 2.25 words per pixel // Add 1 to make sure the PWM FIFO gets the message: "we're sending zeroes" // Times 4 because DMA works in bytes, not words -// cbp->length = (mLedCount * 2.25) * 4; cbp->length = ((mLedCount * 2.25) + 1) * 4; - if(cbp->length > NUM_DATA_WORDS * 4) { + if(cbp->length > NUM_DATA_WORDS * 4) + { cbp->length = NUM_DATA_WORDS * 4; -// mLedCount = NUM_DATA_WORDS / 2.25; mLedCount = (NUM_DATA_WORDS - 1) / 2.25; } @@ -336,7 +332,8 @@ int LedDeviceWS2812b::write(const std::vector &ledValues) #endif - for(size_t i=0; i &ledValues) for(int j=23; j>=0; j--) { #ifdef WS2812_ASM_OPTI // Fetch word the bit is in - unsigned int wordOffset = (int)(wireBit / 32); - wireBit +=3; + unsigned int wordOffset = (int)(wireBit / 32); + wireBit +=3; -// printBinary(startbitPattern, 32); -// printf(" %d\n", j); - if (colorBits & (1 << j)) { - PWMWaveform[wordOffset] |= startbitPattern; - } else { - PWMWaveform[wordOffset] = arm_Bit_Clear_imm(PWMWaveform[wordOffset], startbitPattern); - } - - startbitPattern = arm_ror_imm(startbitPattern, 3); + if (colorBits & (1 << j)) { + PWMWaveform[wordOffset] |= startbitPattern; + } else { + PWMWaveform[wordOffset] = arm_Bit_Clear_imm(PWMWaveform[wordOffset], startbitPattern); + } + startbitPattern = arm_ror_imm(startbitPattern, 3); #else unsigned char colorBit = (colorBits & (1 << j)) ? 1 : 0; // Holds current bit out of colorBits to be processed setPWMBit(wireBit, colorBit); @@ -391,9 +385,6 @@ int LedDeviceWS2812b::write(const std::vector &ledValues) //remove one to undo optimization wireBit --; -// printBinary(PWMWaveform[(int)(wireBit / 32)], 32); -// printf(" pre\n"); - #ifdef WS2812_ASM_OPTI int rest = 32 - wireBit % 32; // 64: 32 - used Bits startbitPattern = (1 << (rest-1)); // set new bitpattern to start at the benigining of one bit (3 bit in wave form) @@ -406,7 +397,8 @@ int LedDeviceWS2812b::write(const std::vector &ledValues) unsigned int oldbitPattern = startbitPattern; // zero rest of the 4 bytes / int so that output is 0 (no data is send) - for (int i = 0; i < rest; i += 3){ + for (int i = 0; i < rest; i += 3) + { unsigned int wordOffset = (int)(wireBit / 32); wireBit += 3; PWMWaveform[wordOffset] = arm_Bit_Clear_imm(PWMWaveform[wordOffset], startbitPattern); @@ -419,22 +411,14 @@ int LedDeviceWS2812b::write(const std::vector &ledValues) unsigned int oldwireBitValue = wireBit; // zero rest of the 4 bytes / int so that output is 0 (no data is send) - for (int i = 0; i < rest; i += 3){ + for (int i = 0; i < rest; i += 3) + { setPWMBit(wireBit, 0); wireBit += 3; } #endif -// printBinary(PWMWaveform[(int)(oldwireBitValue / 32) -1 ], 32); -// printf(" post\n"); -// printBinary(PWMWaveform[(int)(oldwireBitValue / 32)], 32); -// printf(" post\n"); - // This block is a major CPU hog when there are lots of pixels to be transmitted. - // It would go quicker with DMA. -// for(unsigned int i = 0; i < (cbp->length / 4); i++) { -// ctl->sample[i] = PWMWaveform[i]; -// } - memcpy ( ctl->sample, PWMWaveform, cbp->length ); // memcpy does the same and is potentially faster + memcpy ( ctl->sample, PWMWaveform, cbp->length ); // Enable DMA and PWM engines, which should now send the data startTransfer(); @@ -444,35 +428,29 @@ int LedDeviceWS2812b::write(const std::vector &ledValues) #ifdef WS2812_ASM_OPTI startbitPattern = oldbitPattern; - for (int i = 0; i < rest; i += 3){ + for (int i = 0; i < rest; i += 3) + { unsigned int wordOffset = (int)(wireBit / 32); wireBit += 3; PWMWaveform[wordOffset] |= startbitPattern; startbitPattern = arm_ror_imm(startbitPattern, 3); } #else - for (int i = 0; i < rest; i += 3){ + for (int i = 0; i < rest; i += 3) + { setPWMBit(wireBit, 1); wireBit += 3; } #endif -// printBinary(PWMWaveform[(int)(oldwireBitValue / 32)], 32); -// printf(" restored\n"); - - // Wait long enough for the DMA transfer to finish - // 3 RAM bits per wire bit, so 72 bits to send one color command. - //float bitTimeUSec = (float)(NUM_DATA_WORDS * 32) * 0.4; // Bits sent * time to transmit one bit, which is 0.4μSec - //printf("Delay for %d μSec\n", (int)bitTimeUSec); - //usleep((int)bitTimeUSec); - #ifdef BENCHMARK clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &timeEnd); timespec result; result.tv_sec = timeEnd.tv_sec - timeStart.tv_sec; result.tv_nsec = timeEnd.tv_nsec - timeStart.tv_nsec; - if (result.tv_nsec < 0) { + if (result.tv_nsec < 0) + { result.tv_nsec = 1e9 - result.tv_nsec; result.tv_sec -= 1; } @@ -491,8 +469,7 @@ int LedDeviceWS2812b::switchOff() LedDeviceWS2812b::~LedDeviceWS2812b() { // Exit cleanly, freeing memory and stopping the DMA & PWM engines - // We trap all signals (including Ctrl+C), so even if you don't get here, it terminates correctly - terminate(0); + terminate(0); #ifdef BENCHMARK printf("WS2812b Benchmark results: Runs %d - Avarage %lu (n) - Minimum %ld (n)\n", runCount, (runCount > 0 ? combinedNseconds / runCount : 0), shortestNseconds); @@ -512,47 +489,46 @@ LedDeviceWS2812b::~LedDeviceWS2812b() // Convenience functions // -------------------------------------------------------------------------------------------------- // Print some bits of a binary number (2nd arg is how many bits) -void LedDeviceWS2812b::printBinary(unsigned int i, unsigned int bits) { +void LedDeviceWS2812b::printBinary(unsigned int i, unsigned int bits) +{ int x; - for(x=bits-1; x>=0; x--) { + for(x=bits-1; x>=0; x--) + { printf("%d", (i & (1 << x)) ? 1 : 0); - if(x % 16 == 0 && x > 0) { + if(x % 16 == 0 && x > 0) + { printf(" "); - } else if(x % 4 == 0 && x > 0) { + } + else if(x % 4 == 0 && x > 0) + { printf(":"); } } } // Reverse the bits in a word -unsigned int reverseWord(unsigned int word) { +unsigned int reverseWord(unsigned int word) +{ unsigned int output = 0; //unsigned char bit; int i; - for(i=0; i<32; i++) { - //bit = word & (1 << i) ? 1 : 0; + for(i=0; i<32; i++) + { output |= word & (1 << i) ? 1 : 0; - if(i<31) { + if(i<31) + { output <<= 1; } } return output; } -// Not sure how this is better than usleep...? -/* -static void udelay(int us) { - struct timespec ts = { 0, us * 1000 }; - nanosleep(&ts, NULL); -} -*/ - - // Shutdown functions // -------------------------------------------------------------------------------------------------- void LedDeviceWS2812b::terminate(int dummy) { // Shut down the DMA controller - if(dma_reg) { + if(dma_reg) + { CLRBIT(dma_reg[DMA_CS], DMA_CS_ACTIVE); usleep(100); SETBIT(dma_reg[DMA_CS], DMA_CS_RESET); @@ -560,21 +536,22 @@ void LedDeviceWS2812b::terminate(int dummy) { } // Shut down PWM - if(pwm_reg) { + if(pwm_reg) + { CLRBIT(pwm_reg[PWM_CTL], PWM_CTL_PWEN1); usleep(100); pwm_reg[PWM_CTL] = (1 << PWM_CTL_CLRF1); } // Free the allocated memory - if(page_map != 0) { + if(page_map != 0) + { free(page_map); } - - //exit(1); } -void LedDeviceWS2812b::fatal(const char *fmt, ...) { +void LedDeviceWS2812b::fatal(const char *fmt, ...) +{ va_list ap; va_start(ap, fmt); vfprintf(stderr, fmt, ap); @@ -586,18 +563,22 @@ void LedDeviceWS2812b::fatal(const char *fmt, ...) { // Memory management // -------------------------------------------------------------------------------------------------- // Translate from virtual address to physical -unsigned int LedDeviceWS2812b::mem_virt_to_phys(void *virt) { +unsigned int LedDeviceWS2812b::mem_virt_to_phys(void *virt) +{ unsigned int offset = (uint8_t *)virt - virtbase; return page_map[offset >> PAGE_SHIFT].physaddr + (offset % PAGE_SIZE); } // Translate from physical address to virtual -unsigned int LedDeviceWS2812b::mem_phys_to_virt(uint32_t phys) { +unsigned int LedDeviceWS2812b::mem_phys_to_virt(uint32_t phys) +{ unsigned int pg_offset = phys & (PAGE_SIZE - 1); unsigned int pg_addr = phys - pg_offset; - for (unsigned int i = 0; i < NUM_PAGES; i++) { - if (page_map[i].physaddr == pg_addr) { + for (unsigned int i = 0; i < NUM_PAGES; i++) + { + if (page_map[i].physaddr == pg_addr) + { return (uint32_t)virtbase + i * PAGE_SIZE + pg_offset; } } @@ -607,22 +588,28 @@ unsigned int LedDeviceWS2812b::mem_phys_to_virt(uint32_t phys) { } // Map a peripheral's IO memory into our virtual memory, so we can read/write it directly -void * LedDeviceWS2812b::map_peripheral(uint32_t base, uint32_t len) { +void * LedDeviceWS2812b::map_peripheral(uint32_t base, uint32_t len) +{ int fd = open("/dev/mem", O_RDWR); void * vaddr; if (fd < 0) + { fatal("Failed to open /dev/mem: %m\n"); + } vaddr = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_SHARED, fd, base); if (vaddr == MAP_FAILED) + { fatal("Failed to map peripheral at 0x%08x: %m\n", base); + } close(fd); return vaddr; } // Zero out the PWM waveform buffer -void LedDeviceWS2812b::clearPWMBuffer() { +void LedDeviceWS2812b::clearPWMBuffer() +{ memset(PWMWaveform, 0, NUM_DATA_WORDS * 4); // Times four because memset deals in bytes. } @@ -630,29 +617,26 @@ void LedDeviceWS2812b::clearPWMBuffer() { // The (31 - bitIdx) is so that we write the data backwards, correcting its endianness // This means getPWMBit will return something other than what was written, so it would be nice // if the logic that calls this function would figure it out instead. (However, that's trickier) -void LedDeviceWS2812b::setPWMBit(unsigned int bitPos, unsigned char bit) { - +void LedDeviceWS2812b::setPWMBit(unsigned int bitPos, unsigned char bit) +{ // Fetch word the bit is in unsigned int wordOffset = (int)(bitPos / 32); unsigned int bitIdx = bitPos - (wordOffset * 32); - //printf("bitPos=%d wordOffset=%d bitIdx=%d value=%d\n", bitPos, wordOffset, bitIdx, bit); - - switch(bit) { + switch(bit) + { case 1: PWMWaveform[wordOffset] |= (1 << (31 - bitIdx)); -// PWMWaveform[wordOffset] |= (1 << bitIdx); break; case 0: PWMWaveform[wordOffset] &= ~(1 << (31 - bitIdx)); -// PWMWaveform[wordOffset] &= ~(1 << bitIdx); break; } } // ==== Init Hardware ==== - -void LedDeviceWS2812b::initHardware() { +void LedDeviceWS2812b::initHardware() +{ int pid; int fd; char pagemap_fn[64]; @@ -692,22 +676,24 @@ void LedDeviceWS2812b::initHardware() { -1, // File descriptor 0); // Offset - if (virtbase == MAP_FAILED) { + if (virtbase == MAP_FAILED) + { fatal("Failed to mmap physical pages: %m\n"); + return; } - if ((unsigned long)virtbase & (PAGE_SIZE-1)) { + if ((unsigned long)virtbase & (PAGE_SIZE-1)) + { fatal("Virtual address is not page aligned\n"); + return; } - //printf("virtbase mapped 0x%x bytes at 0x%x\n", NUM_PAGES * PAGE_SIZE, virtbase); - // Allocate page map (pointers to the control block(s) and data for each CB page_map = (page_map_t *) malloc(NUM_PAGES * sizeof(*page_map)); - if (page_map == 0) { + if (page_map == 0) + { fatal("Failed to malloc page_map: %m\n"); - } else { - //printf("Allocated 0x%x bytes for page_map at 0x%x\n", NUM_PAGES * sizeof(*page_map), page_map); + return; } // Use /proc/self/pagemap to figure out the mapping between virtual and physical addresses @@ -715,17 +701,20 @@ void LedDeviceWS2812b::initHardware() { sprintf(pagemap_fn, "/proc/%d/pagemap", pid); fd = open(pagemap_fn, O_RDONLY); - if (fd < 0) { + if (fd < 0) + { fatal("Failed to open %s: %m\n", pagemap_fn); } off_t newOffset = (unsigned long)virtbase >> 9; - if (lseek(fd, newOffset, SEEK_SET) != newOffset) { + if (lseek(fd, newOffset, SEEK_SET) != newOffset) + { fatal("Failed to seek on %s: %m\n", pagemap_fn); } printf("Page map: %d pages\n", NUM_PAGES); - for (unsigned int i = 0; i < NUM_PAGES; i++) { + for (unsigned int i = 0; i < NUM_PAGES; i++) + { uint64_t pfn; page_map[i].virtaddr = virtbase + i * PAGE_SIZE; @@ -766,7 +755,8 @@ void LedDeviceWS2812b::initHardware() { // Add 1 to make sure the PWM FIFO gets the message: "we're sending zeroes" // Times 4 because DMA works in bytes, not words cbp->length = ((mLedCount * 2.25) + 1) * 4; - if(cbp->length > NUM_DATA_WORDS * 4) { + if(cbp->length > NUM_DATA_WORDS * 4) + { cbp->length = NUM_DATA_WORDS * 4; } @@ -780,19 +770,6 @@ void LedDeviceWS2812b::initHardware() { // Pointer to next block - 0 shuts down the DMA channel when transfer is complete cbp->next = 0; - // Testing - /* - ctl = (struct control_data_s *)virtbase; - ctl->sample[0] = 0x00000000; - ctl->sample[1] = 0x000000FA; - ctl->sample[2] = 0x0000FFFF; - ctl->sample[3] = 0xAAAAAAAA; - ctl->sample[4] = 0xF0F0F0F0; - ctl->sample[5] = 0x0A0A0A0A; - ctl->sample[6] = 0xF00F0000; - */ - - // Stop any existing DMA transfers // --------------------------------------------------------------- dma_reg[DMA_CS] |= (1 << DMA_CS_ABORT); @@ -894,7 +871,8 @@ void LedDeviceWS2812b::initHardware() { } // Begin the transfer -void LedDeviceWS2812b::startTransfer() { +void LedDeviceWS2812b::startTransfer() +{ // Enable DMA dma_reg[DMA_CONBLK_AD] = mem_virt_to_phys(((struct control_data_s *) virtbase)->cb); dma_reg[DMA_CS] = DMA_CS_CONFIGWORD | (1 << DMA_CS_ACTIVE); @@ -902,7 +880,4 @@ void LedDeviceWS2812b::startTransfer() { // Enable PWM SETBIT(pwm_reg[PWM_CTL], PWM_CTL_PWEN1); - -// dumpPWM(); -// dumpDMA(); } diff --git a/libsrc/leddevice/LedDeviceWS2812b.h b/libsrc/leddevice/LedDeviceWS2812b.h index dd92065c..d945e20d 100644 --- a/libsrc/leddevice/LedDeviceWS2812b.h +++ b/libsrc/leddevice/LedDeviceWS2812b.h @@ -108,22 +108,23 @@ // per address. This is because the software (this program) deals only in virtual addresses, // whereas the DMA controller can only access RAM via physical address. (If that's not confusing // enough, it writes to peripherals by their bus addresses.) -typedef struct { +struct page_map_t +{ uint8_t *virtaddr; uint32_t physaddr; -} page_map_t; +}; // Control Block (CB) - this tells the DMA controller what to do. -typedef struct { - unsigned int - info, // Transfer Information (TI) - src, // Source address (physical) - dst, // Destination address (bus) - length, // Length in bytes (not words!) - stride, // We don't care about this - next, // Pointer to next control block - pad[2]; // These are "reserved" (unused) -} dma_cb_t; +struct dma_cb_t +{ + unsigned info; // Transfer Information (TI) + unsigned src; // Source address (physical) + unsigned dst; // Destination address (bus) + unsigned length; // Length in bytes (not words!) + unsigned stride; // We don't care about this + unsigned next; // Pointer to next control block + unsigned pad[2]; // These are "reserved" (unused) +}; /// /// Implementation of the LedDevice interface for writing to Ws2801 led device. From c05c32b67f48dfe0b6baedfa4d228d528b7c5f17 Mon Sep 17 00:00:00 2001 From: "T. van der Zwan" Date: Fri, 19 Sep 2014 16:40:06 +0200 Subject: [PATCH 58/74] Updated binary-release Former-commit-id: 34dbccafe66ba60435c1a88c8ddde98f2fcccf17 --- deploy/hyperion.tar.gz.REMOVED.git-id | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy/hyperion.tar.gz.REMOVED.git-id b/deploy/hyperion.tar.gz.REMOVED.git-id index ab29ef88..88c050e0 100644 --- a/deploy/hyperion.tar.gz.REMOVED.git-id +++ b/deploy/hyperion.tar.gz.REMOVED.git-id @@ -1 +1 @@ -9546e335179f732ff68ea9bc47020a19e4b6f44c \ No newline at end of file +7db53c6c615dd3570277a253f1daff28e97e7abb \ No newline at end of file From b4d7410520a28c671d48ad5a9e4e2620c0e80776 Mon Sep 17 00:00:00 2001 From: "T. van der Zwan" Date: Mon, 22 Sep 2014 20:19:58 +0200 Subject: [PATCH 59/74] Started on adding a configurable delay in case leds are ahead of picture Former-commit-id: 9eedf27c9cb51d05fca2ec2f0f9edae4726ac54d --- config/hyperion.config.json | 3 ++- libsrc/hyperion/Hyperion.cpp | 1 + src/hyperiond/hyperiond.cpp | 16 +++++++--------- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/config/hyperion.config.json b/config/hyperion.config.json index fa08d80d..61fb7865 100644 --- a/config/hyperion.config.json +++ b/config/hyperion.config.json @@ -80,7 +80,8 @@ { "type" : "none", "time_ms" : 200, - "updateFrequency" : 20.0000 + "updateFrequency" : 20.0000, + "framesDelay" : 0 } }, diff --git a/libsrc/hyperion/Hyperion.cpp b/libsrc/hyperion/Hyperion.cpp index a81e4f3c..9b5eb9b9 100644 --- a/libsrc/hyperion/Hyperion.cpp +++ b/libsrc/hyperion/Hyperion.cpp @@ -243,6 +243,7 @@ LedDevice * Hyperion::createColorSmoothing(const Json::Value & smoothingConfig, } else { +// const unsigned framesDelay = smoothingConfig.get("framesDelay", Json::Value(0u)).asUInt(); std::cout << "Creating linear smoothing" << std::endl; return new LinearColorSmoothing(ledDevice, smoothingConfig["updateFrequency"].asDouble(), smoothingConfig["time_ms"].asInt()); } diff --git a/src/hyperiond/hyperiond.cpp b/src/hyperiond/hyperiond.cpp index e7591343..38d2aa42 100644 --- a/src/hyperiond/hyperiond.cpp +++ b/src/hyperiond/hyperiond.cpp @@ -119,18 +119,16 @@ int main(int argc, char** argv) { const Json::Value effectConfigArgs = effectConfig["args"]; if (hyperion.setEffect(effectName, effectConfigArgs, priority, duration_ms) == 0) - { - std::cout << "Boot sequence(" << effectName << ") with user-defined arguments created and started" << std::endl; - } - else - { - std::cout << "Failed to start boot sequence: " << effectName << " with user-defined arguments" << std::endl; - } - + { + std::cout << "Boot sequence(" << effectName << ") with user-defined arguments created and started" << std::endl; + } + else + { + std::cout << "Failed to start boot sequence: " << effectName << " with user-defined arguments" << std::endl; + } } else { - if (hyperion.setEffect(effectName, priority, duration_ms) == 0) { std::cout << "Boot sequence(" << effectName << ") created and started" << std::endl; From b92ea3a5d62fa45a469b23944ceb76f6e963489f Mon Sep 17 00:00:00 2001 From: "T. van der Zwan" Date: Mon, 22 Sep 2014 20:21:17 +0200 Subject: [PATCH 60/74] Set the DMA ws2812b to default OFF in build Former-commit-id: 1e8775cf07bd33b61429e65ce309f42a8f464828 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 64cf1050..6f0c0ed5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,7 +13,7 @@ message(STATUS "ENABLE_DISPMANX = " ${ENABLE_DISPMANX}) option(ENABLE_SPIDEV "Enable the SPIDEV device" ON) message(STATUS "ENABLE_SPIDEV = " ${ENABLE_SPIDEV}) -option(ENABLE_WS2812BPWM "Enable the WS2812b-PWM device" ON) +option(ENABLE_WS2812BPWM "Enable the WS2812b-PWM device" OFF) message(STATUS "ENABLE_WS2812BPWM = " ${ENABLE_WS2812BPWM}) option(ENABLE_V4L2 "Enable the V4L2 grabber" ON) From a86e451db9d21a048c99c1af3a22b75085abf21b Mon Sep 17 00:00:00 2001 From: "T. van der Zwan" Date: Mon, 22 Sep 2014 20:24:33 +0200 Subject: [PATCH 61/74] Added black color before boot-sequence, to make sure that the boot-sequence is the first thing to start Former-commit-id: 575f1dfa4d2047c109d179c209c683c37028e721 --- src/hyperiond/hyperiond.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/hyperiond/hyperiond.cpp b/src/hyperiond/hyperiond.cpp index 38d2aa42..4c21b39b 100644 --- a/src/hyperiond/hyperiond.cpp +++ b/src/hyperiond/hyperiond.cpp @@ -115,6 +115,8 @@ int main(int argc, char** argv) const unsigned duration_ms = effectConfig["duration_ms"].asUInt(); const int priority = 0; + hyperion.setColor(priority+1, ColorRgb::BLACK, duration_ms, false); + if (effectConfig.isMember("args")) { const Json::Value effectConfigArgs = effectConfig["args"]; From 068782d419966c5b0a6dd030d538262e40c098fd Mon Sep 17 00:00:00 2001 From: "T. van der Zwan" Date: Mon, 22 Sep 2014 20:25:06 +0200 Subject: [PATCH 62/74] Updated RPi release Former-commit-id: 248e4c3d5dd5c2f8dc1d795ccf0a86bda4bc316b --- deploy/hyperion.tar.gz.REMOVED.git-id | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy/hyperion.tar.gz.REMOVED.git-id b/deploy/hyperion.tar.gz.REMOVED.git-id index 88c050e0..f71fa321 100644 --- a/deploy/hyperion.tar.gz.REMOVED.git-id +++ b/deploy/hyperion.tar.gz.REMOVED.git-id @@ -1 +1 @@ -7db53c6c615dd3570277a253f1daff28e97e7abb \ No newline at end of file +7dbfa7fe80db4f8e65357ff45ff92a1321dfde49 \ No newline at end of file From 30cc14d9e8983707f9ca7cc229dfdf897821f992 Mon Sep 17 00:00:00 2001 From: "T. van der Zwan" Date: Mon, 22 Sep 2014 21:28:38 +0200 Subject: [PATCH 63/74] Added delay to smoothing and changed the switch off to continue sending black Former-commit-id: fa5a7f14b0fdf3a0a74169cfefbf5b625330b753 --- config/hyperion.config.json | 2 +- libsrc/hyperion/Hyperion.cpp | 8 +++- libsrc/hyperion/LinearColorSmoothing.cpp | 52 +++++++++++++++++------- libsrc/hyperion/LinearColorSmoothing.h | 16 +++++++- 4 files changed, 60 insertions(+), 18 deletions(-) diff --git a/config/hyperion.config.json b/config/hyperion.config.json index 61fb7865..8e361ed0 100644 --- a/config/hyperion.config.json +++ b/config/hyperion.config.json @@ -81,7 +81,7 @@ "type" : "none", "time_ms" : 200, "updateFrequency" : 20.0000, - "framesDelay" : 0 + "updateDelay" : 0 } }, diff --git a/libsrc/hyperion/Hyperion.cpp b/libsrc/hyperion/Hyperion.cpp index 9b5eb9b9..5e76d8e8 100644 --- a/libsrc/hyperion/Hyperion.cpp +++ b/libsrc/hyperion/Hyperion.cpp @@ -243,9 +243,13 @@ LedDevice * Hyperion::createColorSmoothing(const Json::Value & smoothingConfig, } else { -// const unsigned framesDelay = smoothingConfig.get("framesDelay", Json::Value(0u)).asUInt(); + const unsigned updateDelay = smoothingConfig.get("updateDelay", Json::Value(0u)).asUInt(); std::cout << "Creating linear smoothing" << std::endl; - return new LinearColorSmoothing(ledDevice, smoothingConfig["updateFrequency"].asDouble(), smoothingConfig["time_ms"].asInt()); + return new LinearColorSmoothing( + ledDevice, + smoothingConfig["updateFrequency"].asDouble(), + smoothingConfig["time_ms"].asInt(), + updateDelay); } } else diff --git a/libsrc/hyperion/LinearColorSmoothing.cpp b/libsrc/hyperion/LinearColorSmoothing.cpp index 0fe3e208..47974c90 100644 --- a/libsrc/hyperion/LinearColorSmoothing.cpp +++ b/libsrc/hyperion/LinearColorSmoothing.cpp @@ -3,13 +3,18 @@ #include "LinearColorSmoothing.h" -LinearColorSmoothing::LinearColorSmoothing(LedDevice *ledDevice, double ledUpdateFrequency_hz, int settlingTime_ms) : +LinearColorSmoothing::LinearColorSmoothing( + LedDevice *ledDevice, + double ledUpdateFrequency_hz, + int settlingTime_ms, + unsigned updateDelay) : QObject(), LedDevice(), _ledDevice(ledDevice), _updateInterval(1000 / ledUpdateFrequency_hz), _settlingTime(settlingTime_ms), - _timer() + _timer(), + _outputDelay(updateDelay) { _timer.setSingleShot(false); _timer.setInterval(_updateInterval); @@ -25,7 +30,7 @@ LinearColorSmoothing::~LinearColorSmoothing() int LinearColorSmoothing::write(const std::vector &ledValues) { // received a new target color - if (_previousValues.size() == 0) + if (_previousValues.empty()) { // not initialized yet _targetTime = QDateTime::currentMSecsSinceEpoch() + _settlingTime; @@ -46,17 +51,19 @@ int LinearColorSmoothing::write(const std::vector &ledValues) int LinearColorSmoothing::switchOff() { - // stop smoothing filter - _timer.stop(); - - // return to uninitialized state - _previousValues.clear(); - _previousTime = 0; - _targetValues.clear(); + // Clear the smoothing parameters + std::fill(_targetValues.begin(), _targetValues.end(), ColorRgb::BLACK); _targetTime = 0; - // finally switch off all leds - return _ledDevice->switchOff(); + // Erase the output-queue + for (unsigned i=0; i<_outputQueue.size(); ++i) + { + _outputQueue.push_back(_targetValues); + _outputQueue.pop_front(); + } + + + return 0; } void LinearColorSmoothing::updateLeds() @@ -69,7 +76,7 @@ void LinearColorSmoothing::updateLeds() memcpy(_previousValues.data(), _targetValues.data(), _targetValues.size() * sizeof(ColorRgb)); _previousTime = now; - _ledDevice->write(_previousValues); + queueColors(_previousValues); } else { @@ -86,6 +93,23 @@ void LinearColorSmoothing::updateLeds() } _previousTime = now; - _ledDevice->write(_previousValues); + queueColors(_previousValues); + } +} + +void LinearColorSmoothing::queueColors(const std::vector & ledColors) +{ + if (_outputDelay == 0) + { + _ledDevice->write(ledColors); + } + else + { + _outputQueue.push_back(ledColors); + if (_outputQueue.size() > _outputDelay) + { + _ledDevice->write(_outputQueue.front()); + _outputQueue.pop_front(); + } } } diff --git a/libsrc/hyperion/LinearColorSmoothing.h b/libsrc/hyperion/LinearColorSmoothing.h index 5c27862d..2b75059e 100644 --- a/libsrc/hyperion/LinearColorSmoothing.h +++ b/libsrc/hyperion/LinearColorSmoothing.h @@ -23,7 +23,8 @@ public: /// @param LedDevice the led device /// @param LedUpdatFrequency The frequency at which the leds will be updated (Hz) /// @param settingTime The time after which the updated led values have been fully applied (sec) - LinearColorSmoothing(LedDevice *ledDevice, double ledUpdateFrequency, int settlingTime); + /// @param updateDelay The number of frames to delay outgoing led updates + LinearColorSmoothing(LedDevice *ledDevice, double ledUpdateFrequency, int settlingTime, unsigned updateDelay); /// Destructor virtual ~LinearColorSmoothing(); @@ -43,6 +44,13 @@ private slots: void updateLeds(); private: + /** + * Pushes the colors into the output queue and popping the head to the led-device + * + * @param ledColors The colors to queue + */ + void queueColors(const std::vector & ledColors); + /// The led device LedDevice * _ledDevice; @@ -66,4 +74,10 @@ private: /// The previously written led data std::vector _previousValues; + + /** The number of updates to keep in the output queue (delayed) before being output */ + const unsigned _outputDelay; + /** The output queue */ + std::list > _outputQueue; + }; From fbe8e0ad4e29ea4e278ac2831b259cc3f368e8aa Mon Sep 17 00:00:00 2001 From: "T. van der Zwan" Date: Thu, 25 Sep 2014 22:05:01 +0200 Subject: [PATCH 64/74] Added some code comments Former-commit-id: 58c1ec1fd2dcc210b6d162d1e8b6c40a502f30fa --- libsrc/hyperion/LinearColorSmoothing.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/libsrc/hyperion/LinearColorSmoothing.cpp b/libsrc/hyperion/LinearColorSmoothing.cpp index 47974c90..888b1c2f 100644 --- a/libsrc/hyperion/LinearColorSmoothing.cpp +++ b/libsrc/hyperion/LinearColorSmoothing.cpp @@ -4,7 +4,7 @@ #include "LinearColorSmoothing.h" LinearColorSmoothing::LinearColorSmoothing( - LedDevice *ledDevice, + LedDevice * ledDevice, double ledUpdateFrequency_hz, int settlingTime_ms, unsigned updateDelay) : @@ -51,6 +51,8 @@ int LinearColorSmoothing::write(const std::vector &ledValues) int LinearColorSmoothing::switchOff() { + // We will keep updating the leds (but with pure-black) + // Clear the smoothing parameters std::fill(_targetValues.begin(), _targetValues.end(), ColorRgb::BLACK); _targetTime = 0; @@ -62,7 +64,6 @@ int LinearColorSmoothing::switchOff() _outputQueue.pop_front(); } - return 0; } @@ -101,11 +102,14 @@ void LinearColorSmoothing::queueColors(const std::vector & ledColors) { if (_outputDelay == 0) { + // No output delay => immediate write _ledDevice->write(ledColors); } else { + // Push the new colors in the delay-buffer _outputQueue.push_back(ledColors); + // If the delay-buffer is filled pop the front and write to device if (_outputQueue.size() > _outputDelay) { _ledDevice->write(_outputQueue.front()); From 11d29d02c565151821fa045cc67522b06cc94c75 Mon Sep 17 00:00:00 2001 From: "T. van der Zwan" Date: Thu, 25 Sep 2014 22:31:20 +0200 Subject: [PATCH 65/74] Added debug statement to standard out Former-commit-id: daaeb2a07931942bbeaf272775da81902169ad04 --- libsrc/hyperion/LinearColorSmoothing.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libsrc/hyperion/LinearColorSmoothing.cpp b/libsrc/hyperion/LinearColorSmoothing.cpp index 888b1c2f..da932e9e 100644 --- a/libsrc/hyperion/LinearColorSmoothing.cpp +++ b/libsrc/hyperion/LinearColorSmoothing.cpp @@ -20,6 +20,8 @@ LinearColorSmoothing::LinearColorSmoothing( _timer.setInterval(_updateInterval); connect(&_timer, SIGNAL(timeout()), this, SLOT(updateLeds())); + + std::cout << "Created linear-smoothing(interval_ms=" << _updateInterval << ";settlingTime_ms=" << settlingTime_ms << ";updateDelay=" << _outputDelay << std::endl; } LinearColorSmoothing::~LinearColorSmoothing() From 7c1811091aba5b16b65d94d51d183570835ba4b1 Mon Sep 17 00:00:00 2001 From: "T. van der Zwan" Date: Thu, 25 Sep 2014 22:33:03 +0200 Subject: [PATCH 66/74] Updated RPi release Former-commit-id: 971008c4e31aaf67e7cfe28d163e3305f426a8e6 --- deploy/hyperion.tar.gz.REMOVED.git-id | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy/hyperion.tar.gz.REMOVED.git-id b/deploy/hyperion.tar.gz.REMOVED.git-id index f71fa321..687dfb80 100644 --- a/deploy/hyperion.tar.gz.REMOVED.git-id +++ b/deploy/hyperion.tar.gz.REMOVED.git-id @@ -1 +1 @@ -7dbfa7fe80db4f8e65357ff45ff92a1321dfde49 \ No newline at end of file +f8d592fd55a3ca744f582c960dcde3aaa0afe5c0 \ No newline at end of file From 27a8e8bca4a63d156ea95a0d9093312dd6a59566 Mon Sep 17 00:00:00 2001 From: "T.van der Zwan" Date: Fri, 26 Sep 2014 18:24:38 +0000 Subject: [PATCH 67/74] Created a preliminary binary release for the HummingBoard (or cubox) Former-commit-id: e572c1f0d610e4509f01f6523e95852bd06b7975 --- deploy/hummingboard_prerelease.tar.gz.REMOVED.git-id | 1 + 1 file changed, 1 insertion(+) create mode 100644 deploy/hummingboard_prerelease.tar.gz.REMOVED.git-id diff --git a/deploy/hummingboard_prerelease.tar.gz.REMOVED.git-id b/deploy/hummingboard_prerelease.tar.gz.REMOVED.git-id new file mode 100644 index 00000000..6069fe6c --- /dev/null +++ b/deploy/hummingboard_prerelease.tar.gz.REMOVED.git-id @@ -0,0 +1 @@ +fb7e80dc922d44ce6d88cfcc2bea3e634edb3d07 \ No newline at end of file From c9d3a91d3607fd084f09f32035344cb6a97787d4 Mon Sep 17 00:00:00 2001 From: "T.van der Zwan" Date: Fri, 26 Sep 2014 18:26:46 +0000 Subject: [PATCH 68/74] Updated the HummingBoard release Former-commit-id: bc32fd4c66e721930c183cafd5a2bbc765c55cfc --- deploy/hummingboard_prerelease.tar.gz.REMOVED.git-id | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy/hummingboard_prerelease.tar.gz.REMOVED.git-id b/deploy/hummingboard_prerelease.tar.gz.REMOVED.git-id index 6069fe6c..25e27e13 100644 --- a/deploy/hummingboard_prerelease.tar.gz.REMOVED.git-id +++ b/deploy/hummingboard_prerelease.tar.gz.REMOVED.git-id @@ -1 +1 @@ -fb7e80dc922d44ce6d88cfcc2bea3e634edb3d07 \ No newline at end of file +63b2cc4bf190a0c0c996be6db30fcf03109c9625 \ No newline at end of file From 654f6416ea04f401eca501cdeb2bf9f28b623c80 Mon Sep 17 00:00:00 2001 From: "T.van der Zwan" Date: Fri, 26 Sep 2014 18:27:30 +0000 Subject: [PATCH 69/74] Updated proto-definition due to changes in newest protobuf library Former-commit-id: 2e5a2de1a05bdd5c5ccc8668d6a6d050b5fbb1de --- libsrc/protoserver/message.proto | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libsrc/protoserver/message.proto b/libsrc/protoserver/message.proto index ebc7cba2..33c995b3 100644 --- a/libsrc/protoserver/message.proto +++ b/libsrc/protoserver/message.proto @@ -17,7 +17,7 @@ message HyperionRequest { message ColorRequest { extend HyperionRequest { - required ColorRequest colorRequest = 10; + optional ColorRequest colorRequest = 10; } // priority to use when setting the color @@ -32,7 +32,7 @@ message ColorRequest { message ImageRequest { extend HyperionRequest { - required ImageRequest imageRequest = 11; + optional ImageRequest imageRequest = 11; } // priority to use when setting the image @@ -53,7 +53,7 @@ message ImageRequest { message ClearRequest { extend HyperionRequest { - required ClearRequest clearRequest = 12; + optional ClearRequest clearRequest = 12; } // priority which need to be cleared From d1f043f23328bcb924b428753a80d8bf3d529840 Mon Sep 17 00:00:00 2001 From: "T. van der Zwan" Date: Tue, 30 Sep 2014 21:00:42 +0200 Subject: [PATCH 70/74] Fixed switch-off on shutdown Former-commit-id: 25438c11a3fdb14c89f946d02893a257aada67ea --- deploy/hyperion.tar.gz.REMOVED.git-id | 2 +- libsrc/hyperion/LinearColorSmoothing.cpp | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/deploy/hyperion.tar.gz.REMOVED.git-id b/deploy/hyperion.tar.gz.REMOVED.git-id index 687dfb80..e7c9bf76 100644 --- a/deploy/hyperion.tar.gz.REMOVED.git-id +++ b/deploy/hyperion.tar.gz.REMOVED.git-id @@ -1 +1 @@ -f8d592fd55a3ca744f582c960dcde3aaa0afe5c0 \ No newline at end of file +e1ca70a63f1ce9b6975aab98b8eb6ae1a353df14 \ No newline at end of file diff --git a/libsrc/hyperion/LinearColorSmoothing.cpp b/libsrc/hyperion/LinearColorSmoothing.cpp index da932e9e..eef5569d 100644 --- a/libsrc/hyperion/LinearColorSmoothing.cpp +++ b/libsrc/hyperion/LinearColorSmoothing.cpp @@ -26,6 +26,8 @@ LinearColorSmoothing::LinearColorSmoothing( LinearColorSmoothing::~LinearColorSmoothing() { + // Make sure to switch off the underlying led-device (because switchOff is no longer forwarded) + _ledDevice->switchOff(); delete _ledDevice; } From bbc6fe389d07309057827125c9068903183f3a52 Mon Sep 17 00:00:00 2001 From: Gamadril Date: Sat, 25 Oct 2014 22:35:53 +0200 Subject: [PATCH 71/74] Added very basic WebSocket support to json server Former-commit-id: 5d62331eecdbd10287ba0520bbb06814bca0bca7 --- libsrc/jsonserver/JsonClientConnection.cpp | 150 ++++++++++++++++++++- libsrc/jsonserver/JsonClientConnection.h | 3 + 2 files changed, 152 insertions(+), 1 deletion(-) diff --git a/libsrc/jsonserver/JsonClientConnection.cpp b/libsrc/jsonserver/JsonClientConnection.cpp index d478a4d2..b82b51d7 100644 --- a/libsrc/jsonserver/JsonClientConnection.cpp +++ b/libsrc/jsonserver/JsonClientConnection.cpp @@ -10,6 +10,8 @@ // Qt includes #include #include +#include +#include // hyperion util includes #include @@ -27,6 +29,7 @@ JsonClientConnection::JsonClientConnection(QTcpSocket *socket, Hyperion * hyperi _hyperion(hyperion), _receiveBuffer() { + _webSocketHandshakeDone = false; // connect internal signals and slots connect(_socket, SIGNAL(disconnected()), this, SLOT(socketClosed())); connect(_socket, SIGNAL(readyRead()), this, SLOT(readData())); @@ -41,7 +44,126 @@ JsonClientConnection::~JsonClientConnection() void JsonClientConnection::readData() { _receiveBuffer += _socket->readAll(); + + if (_webSocketHandshakeDone) { // websocket mode, data frame + quint8 opCode = 0; + quint64 payloadLength = 0; + bool isMasked = false; + quint32 index = 0; + quint8 maskKey[4]; + + if ((_receiveBuffer.at(0) & 0x80) == 0x80) { // final bit + opCode = _receiveBuffer.at(0) & 0x0F; + isMasked = (_receiveBuffer.at(1) & 0x80) == 0x80; + payloadLength = _receiveBuffer.at(1) & 0x7F; + index = 2; + + switch (payloadLength) { + case 126: + payloadLength = ((_receiveBuffer.at(2) << 8) & 0xFF00) | (_receiveBuffer.at(3) & 0xFF); + index += 2; + break; + case 127: { + payloadLength = 0; + for (int i=0; i < 8; i++) { + payloadLength |= ((quint64)(_receiveBuffer.at(index+i) & 0xFF)) << (8*(7-i)); + } + index += 8; + } + break; + default: + break; + } + + if (isMasked) { // if the data is masked we need to get the key for unmasking + for (int i=0; i < 4; i++) { + maskKey[i] = _receiveBuffer.at(index + i); + } + index += 4; + } + + // check the type of data frame + switch (opCode) { + case 0x01: { // text + QByteArray result = _receiveBuffer.mid(index, payloadLength); + _receiveBuffer.clear(); + + // unmask data if necessary + if (isMasked) { + for (uint i=0 ; i < payloadLength; i++) { + result[i] = (result[i] ^ maskKey[i % 4]); + } + } + + handleMessage(QString(result).toStdString()); + } + break; + case 0x08: { // close + quint8 close[]={0x88, 0}; + _socket->write((const char*)close, 2); + _socket->flush(); + _socket->close(); + } + break; + case 0x09: { // ping, send pong + quint8 close[]={0x0A, 0}; + _socket->write((const char*)close, 2); + _socket->flush(); + } + break; + } + } else { + std::cout << "Someone is sending very big messages over several frames... it's not supported yet" << std::endl; + quint8 close[]={0x88, 0}; + _socket->write((const char*)close, 2); + _socket->flush(); + _socket->close(); + } + } else { // might be a handshake request or raw socket data + if(_receiveBuffer.contains("Upgrade: websocket")){ // http header, might not be a very reliable check... + std::cout << "Websocket handshake" << std::endl; + + // get the key to tprepare an answer + int start = _receiveBuffer.indexOf("Sec-WebSocket-Key") + 19; + std::string value(_receiveBuffer.mid(start, _receiveBuffer.indexOf("\r\n", start) - start).data()); + _receiveBuffer.clear(); + // must be always appended + value += "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; + + // generate sha1 hash + QByteArray hash = QCryptographicHash::hash(value.c_str(), QCryptographicHash::Sha1); + + // prepare an answer + std::ostringstream h; + h << "HTTP/1.1 101 Switching Protocols\r\n" << + "Upgrade: websocket\r\n" << + "Connection: Upgrade\r\n" << + "Sec-WebSocket-Accept: " << QString(hash.toBase64()).toStdString() << "\r\n\r\n"; + + _socket->write(h.str().c_str()); + _socket->flush(); + _webSocketHandshakeDone = true; // we are in WebSocket mode, data frames should follow next + } else { // raw socket data, handling as usual + int bytes = _receiveBuffer.indexOf('\n') + 1; + while(bytes > 0) + { + // create message string + std::string message(_receiveBuffer.data(), bytes); + + // remove message data from buffer + _receiveBuffer = _receiveBuffer.mid(bytes); + + // handle message + handleMessage(message); + + // try too look up '\n' again + bytes = _receiveBuffer.indexOf('\n') + 1; + } + } + } + + /* int bytes = _receiveBuffer.indexOf('\n') + 1; while(bytes > 0) { @@ -57,10 +179,12 @@ void JsonClientConnection::readData() // try too look up '\n' again bytes = _receiveBuffer.indexOf('\n') + 1; } + */ } void JsonClientConnection::socketClosed() { + _webSocketHandshakeDone = false; emit connectionClosed(this); } @@ -205,6 +329,9 @@ void JsonClientConnection::handleServerInfoCommand(const Json::Value &) Json::Value result; result["success"] = true; Json::Value & info = result["info"]; + + // add host name for remote clients + info["hostname"] = QHostInfo::localHostName().toStdString(); // collect priority information Json::Value & priorities = info["priorities"] = Json::Value(Json::arrayValue); @@ -362,7 +489,28 @@ void JsonClientConnection::sendMessage(const Json::Value &message) { Json::FastWriter writer; std::string serializedReply = writer.write(message); - _socket->write(serializedReply.data(), serializedReply.length()); + + if (!_webSocketHandshakeDone) { // raw tcp socket mode + _socket->write(serializedReply.data(), serializedReply.length()); + } else { // websocket mode + quint32 size = serializedReply.length(); + + // prepare data frame + QByteArray response; + response.append(0x81); + if (size > 125) { + response.append(0x7E); + response.append((size >> 8) & 0xFF); + response.append(size & 0xFF); + } else { + response.append(size); + } + + QByteArray data(serializedReply.c_str(), serializedReply.length()); + response.append(data); + + _socket->write(response.data(), response.length()); + } } void JsonClientConnection::sendSuccessReply() diff --git a/libsrc/jsonserver/JsonClientConnection.h b/libsrc/jsonserver/JsonClientConnection.h index dc80deba..b036e638 100644 --- a/libsrc/jsonserver/JsonClientConnection.h +++ b/libsrc/jsonserver/JsonClientConnection.h @@ -161,4 +161,7 @@ private: /// The buffer used for reading data from the socket QByteArray _receiveBuffer; + + /// used for WebSocket detection and connection handling + bool _webSocketHandshakeDone; }; From 6a9e75243d9bfa383381227549cbe37dea064abe Mon Sep 17 00:00:00 2001 From: Gamadril Date: Sun, 26 Oct 2014 10:55:40 +0100 Subject: [PATCH 72/74] Update JsonClientConnection.cpp small code cleanup Former-commit-id: 82029cd23f77b393b2e6688ed6e65424cb676452 --- libsrc/jsonserver/JsonClientConnection.cpp | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/libsrc/jsonserver/JsonClientConnection.cpp b/libsrc/jsonserver/JsonClientConnection.cpp index b82b51d7..d50ed951 100644 --- a/libsrc/jsonserver/JsonClientConnection.cpp +++ b/libsrc/jsonserver/JsonClientConnection.cpp @@ -65,7 +65,7 @@ void JsonClientConnection::readData() break; case 127: { payloadLength = 0; - for (int i=0; i < 8; i++) { + for (uint i=0; i < 8; i++) { payloadLength |= ((quint64)(_receiveBuffer.at(index+i) & 0xFF)) << (8*(7-i)); } index += 8; @@ -76,7 +76,7 @@ void JsonClientConnection::readData() } if (isMasked) { // if the data is masked we need to get the key for unmasking - for (int i=0; i < 4; i++) { + for (uint i=0; i < 4; i++) { maskKey[i] = _receiveBuffer.at(index + i); } index += 4; @@ -162,24 +162,6 @@ void JsonClientConnection::readData() } } } - - /* - int bytes = _receiveBuffer.indexOf('\n') + 1; - while(bytes > 0) - { - // create message string - std::string message(_receiveBuffer.data(), bytes); - - // remove message data from buffer - _receiveBuffer = _receiveBuffer.mid(bytes); - - // handle message - handleMessage(message); - - // try too look up '\n' again - bytes = _receiveBuffer.indexOf('\n') + 1; - } - */ } void JsonClientConnection::socketClosed() From 9168ee5098ccd5498181f5a500e8ca8020afb805 Mon Sep 17 00:00:00 2001 From: Gamadril Date: Sat, 8 Nov 2014 21:01:46 +0100 Subject: [PATCH 73/74] code refactoring Former-commit-id: 157f424e83c58fca4ed1e3283899cbbdfadcffe1 --- libsrc/jsonserver/JsonClientConnection.cpp | 253 ++++++++++++--------- libsrc/jsonserver/JsonClientConnection.h | 10 + 2 files changed, 156 insertions(+), 107 deletions(-) diff --git a/libsrc/jsonserver/JsonClientConnection.cpp b/libsrc/jsonserver/JsonClientConnection.cpp index d50ed951..87fb7c1f 100644 --- a/libsrc/jsonserver/JsonClientConnection.cpp +++ b/libsrc/jsonserver/JsonClientConnection.cpp @@ -27,9 +27,9 @@ JsonClientConnection::JsonClientConnection(QTcpSocket *socket, Hyperion * hyperi _socket(socket), _imageProcessor(ImageProcessorFactory::getInstance().newImageProcessor()), _hyperion(hyperion), - _receiveBuffer() + _receiveBuffer(), + _webSocketHandshakeDone(false) { - _webSocketHandshakeDone = false; // connect internal signals and slots connect(_socket, SIGNAL(disconnected()), this, SLOT(socketClosed())); connect(_socket, SIGNAL(readyRead()), this, SLOT(readData())); @@ -45,106 +45,19 @@ void JsonClientConnection::readData() { _receiveBuffer += _socket->readAll(); - if (_webSocketHandshakeDone) { // websocket mode, data frame - quint8 opCode = 0; - quint64 payloadLength = 0; - bool isMasked = false; - quint32 index = 0; - quint8 maskKey[4]; - - if ((_receiveBuffer.at(0) & 0x80) == 0x80) { // final bit - opCode = _receiveBuffer.at(0) & 0x0F; - isMasked = (_receiveBuffer.at(1) & 0x80) == 0x80; - payloadLength = _receiveBuffer.at(1) & 0x7F; - index = 2; - - switch (payloadLength) { - case 126: - payloadLength = ((_receiveBuffer.at(2) << 8) & 0xFF00) | (_receiveBuffer.at(3) & 0xFF); - index += 2; - break; - case 127: { - payloadLength = 0; - for (uint i=0; i < 8; i++) { - payloadLength |= ((quint64)(_receiveBuffer.at(index+i) & 0xFF)) << (8*(7-i)); - } - index += 8; - } - break; - default: - break; - } - - if (isMasked) { // if the data is masked we need to get the key for unmasking - for (uint i=0; i < 4; i++) { - maskKey[i] = _receiveBuffer.at(index + i); - } - index += 4; - } - - // check the type of data frame - switch (opCode) { - case 0x01: { // text - QByteArray result = _receiveBuffer.mid(index, payloadLength); - _receiveBuffer.clear(); - - // unmask data if necessary - if (isMasked) { - for (uint i=0 ; i < payloadLength; i++) { - result[i] = (result[i] ^ maskKey[i % 4]); - } - } - - handleMessage(QString(result).toStdString()); - } - break; - case 0x08: { // close - quint8 close[]={0x88, 0}; - _socket->write((const char*)close, 2); - _socket->flush(); - _socket->close(); - } - break; - case 0x09: { // ping, send pong - quint8 close[]={0x0A, 0}; - _socket->write((const char*)close, 2); - _socket->flush(); - } - break; - } - } else { - std::cout << "Someone is sending very big messages over several frames... it's not supported yet" << std::endl; - quint8 close[]={0x88, 0}; - _socket->write((const char*)close, 2); - _socket->flush(); - _socket->close(); - } - } else { // might be a handshake request or raw socket data - if(_receiveBuffer.contains("Upgrade: websocket")){ // http header, might not be a very reliable check... - std::cout << "Websocket handshake" << std::endl; - - // get the key to tprepare an answer - int start = _receiveBuffer.indexOf("Sec-WebSocket-Key") + 19; - std::string value(_receiveBuffer.mid(start, _receiveBuffer.indexOf("\r\n", start) - start).data()); - _receiveBuffer.clear(); - - // must be always appended - value += "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; - - // generate sha1 hash - QByteArray hash = QCryptographicHash::hash(value.c_str(), QCryptographicHash::Sha1); - - // prepare an answer - std::ostringstream h; - h << "HTTP/1.1 101 Switching Protocols\r\n" << - "Upgrade: websocket\r\n" << - "Connection: Upgrade\r\n" << - "Sec-WebSocket-Accept: " << QString(hash.toBase64()).toStdString() << "\r\n\r\n"; - - _socket->write(h.str().c_str()); - _socket->flush(); - _webSocketHandshakeDone = true; // we are in WebSocket mode, data frames should follow next - } else { // raw socket data, handling as usual + if (_webSocketHandshakeDone) + { + // websocket mode, data frame + handleWebSocketFrame(); + } else + { + // might be a handshake request or raw socket data + if(_receiveBuffer.contains("Upgrade: websocket")) + { + doWebSocketHandshake(); + } else + { + // raw socket data, handling as usual int bytes = _receiveBuffer.indexOf('\n') + 1; while(bytes > 0) { @@ -164,6 +77,128 @@ void JsonClientConnection::readData() } } +void JsonClientConnection::handleWebSocketFrame() +{ + if ((_receiveBuffer.at(0) & 0x80) == 0x80) + { + // final bit found, frame complete + quint8 * maskKey = NULL; + quint8 opCode = _receiveBuffer.at(0) & 0x0F; + bool isMasked = (_receiveBuffer.at(1) & 0x80) == 0x80; + quint64 payloadLength = _receiveBuffer.at(1) & 0x7F; + quint32 index = 2; + + switch (payloadLength) + { + case 126: + payloadLength = ((_receiveBuffer.at(2) << 8) & 0xFF00) | (_receiveBuffer.at(3) & 0xFF); + index += 2; + break; + case 127: + payloadLength = 0; + for (uint i=0; i < 8; i++) { + payloadLength |= ((quint64)(_receiveBuffer.at(index+i) & 0xFF)) << (8*(7-i)); + } + index += 8; + break; + default: + break; + } + + if (isMasked) + { + // if the data is masked we need to get the key for unmasking + maskKey = new quint8[4]; + for (uint i=0; i < 4; i++) + { + maskKey[i] = _receiveBuffer.at(index + i); + } + index += 4; + } + + // check the type of data frame + switch (opCode) + { + case 0x01: + { + // frame contains text, extract it + QByteArray result = _receiveBuffer.mid(index, payloadLength); + _receiveBuffer.clear(); + + // unmask data if necessary + if (isMasked) + { + for (uint i=0; i < payloadLength; i++) + { + result[i] = (result[i] ^ maskKey[i % 4]); + } + if (maskKey != NULL) + { + delete[] maskKey; + maskKey = NULL; + } + } + + handleMessage(QString(result).toStdString()); + } + break; + case 0x08: + { + // close request, confirm + quint8 close[] = {0x88, 0}; + _socket->write((const char*)close, 2); + _socket->flush(); + _socket->close(); + } + break; + case 0x09: + { + // ping received, send pong + quint8 pong[] = {0x0A, 0}; + _socket->write((const char*)pong, 2); + _socket->flush(); + } + break; + } + } else + { + std::cout << "Someone is sending very big messages over several frames... it's not supported yet" << std::endl; + quint8 close[] = {0x88, 0}; + _socket->write((const char*)close, 2); + _socket->flush(); + _socket->close(); + } +} + +void JsonClientConnection::doWebSocketHandshake() +{ + // http header, might not be a very reliable check... + std::cout << "Websocket handshake" << std::endl; + + // get the key to prepare an answer + int start = _receiveBuffer.indexOf("Sec-WebSocket-Key") + 19; + std::string value(_receiveBuffer.mid(start, _receiveBuffer.indexOf("\r\n", start) - start).data()); + _receiveBuffer.clear(); + + // must be always appended + value += "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; + + // generate sha1 hash + QByteArray hash = QCryptographicHash::hash(value.c_str(), QCryptographicHash::Sha1); + + // prepare an answer + std::ostringstream h; + h << "HTTP/1.1 101 Switching Protocols\r\n" << + "Upgrade: websocket\r\n" << + "Connection: Upgrade\r\n" << + "Sec-WebSocket-Accept: " << QString(hash.toBase64()).toStdString() << "\r\n\r\n"; + + _socket->write(h.str().c_str()); + _socket->flush(); + // we are in WebSocket mode, data frames should follow next + _webSocketHandshakeDone = true; +} + void JsonClientConnection::socketClosed() { _webSocketHandshakeDone = false; @@ -472,15 +507,20 @@ void JsonClientConnection::sendMessage(const Json::Value &message) Json::FastWriter writer; std::string serializedReply = writer.write(message); - if (!_webSocketHandshakeDone) { // raw tcp socket mode + if (!_webSocketHandshakeDone) + { + // raw tcp socket mode _socket->write(serializedReply.data(), serializedReply.length()); - } else { // websocket mode + } else + { + // websocket mode quint32 size = serializedReply.length(); // prepare data frame QByteArray response; response.append(0x81); - if (size > 125) { + if (size > 125) + { response.append(0x7E); response.append((size >> 8) & 0xFF); response.append(size & 0xFF); @@ -488,8 +528,7 @@ void JsonClientConnection::sendMessage(const Json::Value &message) response.append(size); } - QByteArray data(serializedReply.c_str(), serializedReply.length()); - response.append(data); + response.append(serializedReply.c_str(), serializedReply.length()); _socket->write(response.data(), response.length()); } diff --git a/libsrc/jsonserver/JsonClientConnection.h b/libsrc/jsonserver/JsonClientConnection.h index b036e638..6575388a 100644 --- a/libsrc/jsonserver/JsonClientConnection.h +++ b/libsrc/jsonserver/JsonClientConnection.h @@ -136,6 +136,16 @@ private: /// @param error String describing the error /// void sendErrorReply(const std::string & error); + + /// + /// Do handshake for a websocket connection + /// + void doWebSocketHandshake(); + + /// + /// Handle incoming websocket data frame + /// + void handleWebSocketFrame(); private: /// From 0ea9c87c1a0e2b219fbbb4ab78fc2644854961b2 Mon Sep 17 00:00:00 2001 From: "T. van der Zwan" Date: Sun, 9 Nov 2014 15:56:21 +0100 Subject: [PATCH 74/74] Updated RPi release Former-commit-id: e25fabd5625c4c260a81766503bd2f9c67080a36 --- deploy/hyperion.tar.gz.REMOVED.git-id | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy/hyperion.tar.gz.REMOVED.git-id b/deploy/hyperion.tar.gz.REMOVED.git-id index e7c9bf76..874b3ca0 100644 --- a/deploy/hyperion.tar.gz.REMOVED.git-id +++ b/deploy/hyperion.tar.gz.REMOVED.git-id @@ -1 +1 @@ -e1ca70a63f1ce9b6975aab98b8eb6ae1a353df14 \ No newline at end of file +c2332ae026dcd9b5ededbbe2db493ae13e5208c5 \ No newline at end of file