diff --git a/sources/leddevice/LedDeviceSchemas.qrc b/sources/leddevice/LedDeviceSchemas.qrc index 3df33f8d7..6d563b21c 100644 --- a/sources/leddevice/LedDeviceSchemas.qrc +++ b/sources/leddevice/LedDeviceSchemas.qrc @@ -19,6 +19,7 @@ schemas/schema-sk6812spi.json schemas/schema-sk6822spi.json schemas/schema-sk9822.json + schemas/schema-hd108.json schemas/schema-tinkerforge.json schemas/schema-tpm2net.json schemas/schema-tpm2.json diff --git a/sources/leddevice/dev_spi/LedDeviceHD108.cpp b/sources/leddevice/dev_spi/LedDeviceHD108.cpp new file mode 100644 index 000000000..20ff5c999 --- /dev/null +++ b/sources/leddevice/dev_spi/LedDeviceHD108.cpp @@ -0,0 +1,129 @@ +/* LedDeviceHD108.cpp +* +* MIT License +* +* Copyright (c) 2023 awawa-dev +* +* Project homesite: https://github.com/awawa-dev/HyperHDR +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. + +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. + */ + +#include "LedDeviceHD108.h" + +// Local HyperHDR includes +#include +#include +#include + +const int HD108_MAX_LEVEL = 0x1F; +const uint32_t HD108_MAX_THRESHOLD = 100u; +const uint32_t HD108_MAX_THRESHOLD_2 = HD108_MAX_THRESHOLD * HD108_MAX_THRESHOLD; +const uint16_t HD108_MAX_LEVEL_BIT = 0b1000000000000000; + +LedDeviceHD108::LedDeviceHD108(const QJsonObject& deviceConfig) + : ProviderSpi(deviceConfig) + , _globalBrightnessControlThreshold(HD108_MAX_THRESHOLD) + , _globalBrightnessControlMaxLevel(HD108_MAX_LEVEL) +{ +} + +LedDevice* LedDeviceHD108::construct(const QJsonObject& deviceConfig) +{ + return new LedDeviceHD108(deviceConfig); +} + +bool LedDeviceHD108::init(const QJsonObject& deviceConfig) +{ + bool isInitOK = false; + + // Initialise sub-class + if (ProviderSpi::init(deviceConfig)) + { + _globalBrightnessControlThreshold = static_cast(std::min( + std::lround(deviceConfig["globalBrightnessControlThreshold"].toDouble(HD108_MAX_THRESHOLD) * HD108_MAX_THRESHOLD), + (long)HD108_MAX_THRESHOLD_2)); + _globalBrightnessControlMaxLevel = deviceConfig["globalBrightnessControlMaxLevel"].toInt(HD108_MAX_LEVEL); + Info(_log, "[HD108] Using global brightness control with threshold of %d and max level of %d", _globalBrightnessControlThreshold, _globalBrightnessControlMaxLevel); + + _ledBuffer.resize(0, 0x00); + _ledBuffer.resize((_ledCount * 8) + 16, 0x00); + + isInitOK = true; + } + return isInitOK; +} + +static inline uint16_t MSB_FIRST(uint16_t x, bool littleEndian) +{ + if (littleEndian) + return (x >> 8) | (x << 8); + else + return x; +} + +int LedDeviceHD108::write(const std::vector& ledValues) +{ + int littleEndian = 1; + + if (*(char*)&littleEndian != 1) + littleEndian = 0; + + + if (_ledCount != ledValues.size()) + { + Warning(_log, "HD108 led's number has changed (old: %d, new: %d). Rebuilding buffer.", _ledCount, ledValues.size()); + _ledCount = static_cast(ledValues.size()); + _ledBuffer.resize(0, 0x00); + _ledBuffer.resize((_ledCount * 8) + 16, 0x00); + } + + + int index = 8; + uint16_t* data = reinterpret_cast(_ledBuffer.data()); + for (auto const& rgb : ledValues) + { + const int isLit = (rgb.red || rgb.green || rgb.blue); + uint16_t red = rgb.red * 256u; + uint16_t green = rgb.green * 256u; + uint16_t blue = rgb.blue * 256u; + uint16_t level = HD108_MAX_LEVEL_BIT; + + for (int i = 0; i < 3 && isLit; i++) + { + level |= (_globalBrightnessControlMaxLevel & HD108_MAX_LEVEL) << (5 * i); + } + + if (_globalBrightnessControlThreshold < HD108_MAX_THRESHOLD_2) + { + red = (red * _globalBrightnessControlThreshold) / HD108_MAX_THRESHOLD_2; + green = (green * _globalBrightnessControlThreshold) / HD108_MAX_THRESHOLD_2; + blue = (blue * _globalBrightnessControlThreshold) / HD108_MAX_THRESHOLD_2; + } + + + data[index + 0] = MSB_FIRST(level, littleEndian); + data[index + 1] = MSB_FIRST(red, littleEndian); + data[index + 2] = MSB_FIRST(green, littleEndian); + data[index + 3] = MSB_FIRST(blue, littleEndian); + index += 4; + } + + return writeBytes(_ledBuffer.size(), _ledBuffer.data()); +} diff --git a/sources/leddevice/dev_spi/LedDeviceHD108.h b/sources/leddevice/dev_spi/LedDeviceHD108.h new file mode 100644 index 000000000..5ab0af43b --- /dev/null +++ b/sources/leddevice/dev_spi/LedDeviceHD108.h @@ -0,0 +1,53 @@ +/* LedDeviceHD108.h +* +* MIT License +* +* Copyright (c) 2023 awawa-dev +* +* Project homesite: https://github.com/awawa-dev/HyperHDR +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. + +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. + */ + +#ifndef LEDEVICEHD108_H +#define LEDEVICEHD108_H + +// HyperHDR includes +#include "ProviderSpi.h" + +/// +/// Implementation of the LedDevice interface for writing to LedDeviceHD108 led device via SPI. +/// +class LedDeviceHD108 : public ProviderSpi +{ +public: + explicit LedDeviceHD108(const QJsonObject& deviceConfig); + + static LedDevice* construct(const QJsonObject& deviceConfig); + +private: + uint32_t _globalBrightnessControlThreshold; + uint16_t _globalBrightnessControlMaxLevel; + + bool init(const QJsonObject& deviceConfig) override; + + int write(const std::vector& ledValues) override; +}; + +#endif // LEDEVICEHD108_H diff --git a/sources/leddevice/schemas/schema-hd108.json b/sources/leddevice/schemas/schema-hd108.json new file mode 100644 index 000000000..bfde25f13 --- /dev/null +++ b/sources/leddevice/schemas/schema-hd108.json @@ -0,0 +1,54 @@ +{ + "type":"object", + "required":true, + "properties":{ + "output": { + "type": "string", + "title":"edt_dev_spec_spipath_title", + "default" : "/dev/spidev0.0", + "required" : true, + "propertyOrder" : 1 + }, + "rate": { + "type": "integer", + "format" : "stepper", + "step" : 1000000, + "title":"edt_dev_spec_baudrate_title", + "default": 1000000, + "minimum": 1000000, + "required" : true, + "propertyOrder" : 2 + }, + "invert": { + "type": "boolean", + "format": "checkbox", + "title":"edt_dev_spec_invert_title", + "default": false, + "propertyOrder" : 3 + }, + "globalBrightnessControlMaxLevel": { + "type": "integer", + "format" : "stepper", + "step" : 1, + "title":"edt_dev_spec_globalBrightnessControlMaxLevel_title", + "default": 31, + "minimum": 1, + "maximum": 31, + "required" : true, + "propertyOrder" : 4 + }, + "globalBrightnessControlThreshold": { + "type": "number", + "format" : "stepper", + "title": "edt_conf_color_brightness_title", + "default": 100.0, + "minimum": 0, + "maximum": 100.0, + "step" : 0.25, + "append" : "edt_append_percent", + "required" : true, + "propertyOrder" : 5 + } + }, + "additionalProperties": true +} diff --git a/www/js/light_source.js b/www/js/light_source.js index d8895fcea..913b5d421 100644 --- a/www/js/light_source.js +++ b/www/js/light_source.js @@ -880,7 +880,7 @@ $(document).ready(function() var yeelight_title = 'wiz_yeelight_title'; changeWizard(data, yeelight_title, startWizardYeelight); } - else if (["apa102", "apa104", "awa_spi", "lpd6803", "lpd8806", "p9813", "sk6812spi", "sk6822spi", "sk9822", "ws2801", "ws2812spi", "wled", "adalight", "atmo", "dmx", "karate", "sedu", "tpm2"].includes(ledType)) + else if (["apa102", "apa104", "awa_spi", "lpd6803", "lpd8806", "p9813", "sk6812spi", "sk6822spi", "sk9822", "ws2801", "ws2812spi", "wled", "adalight", "atmo", "dmx", "karate", "sedu", "tpm2", "hd108"].includes(ledType)) { let selectorControl = $("