Skip to content

Commit 5475483

Browse files
authored
Implemented 16Bit HD108-Leds in HyperionNG (#1826)
* Implemented 16Bit HD108-Leds in HyperionNG * Update HD108 integration, schema, and indentions
1 parent 5539220 commit 5475483

File tree

5 files changed

+228
-2
lines changed

5 files changed

+228
-2
lines changed

assets/webconfig/js/content_leds.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ var bottomRight2bottomLeft = null;
1818
var bottomLeft2topLeft = null;
1919
var toggleKeystoneCorrectionArea = false;
2020

21-
var devSPI = ['apa102', 'apa104', 'ws2801', 'lpd6803', 'lpd8806', 'p9813', 'sk6812spi', 'sk6822spi', 'sk9822', 'ws2812spi'];
21+
var devSPI = ['apa102', 'apa104', 'hd108', 'lpd6803', 'lpd8806', 'p9813', 'sk6812spi', 'sk6822spi', 'sk9822', 'ws2801', 'ws2812spi'];
2222
var devFTDI = ['apa102_ftdi', 'sk6812_ftdi', 'ws2812_ftdi'];
2323
var devRPiPWM = ['ws281x'];
2424
var devRPiGPIO = ['piblaster'];
@@ -1115,6 +1115,7 @@ $(document).ready(function () {
11151115
case "ws2812spi":
11161116
case "piblaster":
11171117
case "ws281x":
1118+
case "hd108":
11181119

11191120
//Serial devices
11201121
case "adalight":
@@ -1480,6 +1481,7 @@ $(document).ready(function () {
14801481
case "apa102_ftdi":
14811482
case "sk6812_ftdi":
14821483
case "ws2812_ftdi":
1484+
case "hd108":
14831485
default:
14841486
}
14851487

@@ -1962,6 +1964,7 @@ function saveLedConfig(genDefLayout = false) {
19621964
case "apa102_ftdi":
19631965
case "sk6812_ftdi":
19641966
case "ws2812_ftdi":
1967+
case "hd108":
19651968
default:
19661969
if (genDefLayout === true) {
19671970
ledConfig = {
@@ -2219,6 +2222,7 @@ var updateOutputSelectList = function (ledType, discoveryInfo) {
22192222
case "sk6822spi":
22202223
case "sk9822":
22212224
case "ws2812spi":
2225+
case "hd108":
22222226
case "piblaster":
22232227
for (const device of discoveryInfo.devices) {
22242228
enumVals.push(device.systemLocation);

libsrc/leddevice/LedDeviceSchemas.qrc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
<file alias="schema-ws2812_ftdi">schemas/schema-ws2812_ftdi.json</file>
4343
<file alias="schema-apa102_ftdi">schemas/schema-apa102_ftdi.json</file>
4444
<file alias="schema-sk6812_ftdi">schemas/schema-sk6812_ftdi.json</file>
45-
<file alias="schema-skydimo">schemas/schema-skydimo.json</file>
45+
<file alias="schema-skydimo">schemas/schema-skydimo.json</file>
46+
<file alias="schema-hd108">schemas/schema-hd108.json</file>
4647
</qresource>
4748
</RCC>
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
#include "LedDeviceHD108.h"
2+
3+
/**
4+
* @brief Constructor for the HD108 LED device.
5+
*
6+
* @param deviceConfig JSON configuration object for this device.
7+
*/
8+
LedDeviceHD108::LedDeviceHD108(const QJsonObject &deviceConfig)
9+
: ProviderSpi(deviceConfig)
10+
{
11+
// By default, set the global brightness register to full (16-bit max)
12+
_global_brightness = 0xFFFF;
13+
}
14+
15+
/**
16+
* @brief Factory method: creates an instance of LedDeviceHD108.
17+
*
18+
* @param deviceConfig The JSON configuration for the device.
19+
* @return A pointer to the newly constructed LedDeviceHD108 instance.
20+
*/
21+
LedDevice* LedDeviceHD108::construct(const QJsonObject &deviceConfig)
22+
{
23+
return new LedDeviceHD108(deviceConfig);
24+
}
25+
26+
/**
27+
* @brief Initializes the HD108 device using the given JSON configuration.
28+
*
29+
* This reads certain device-specific parameters, such as the maximum brightness
30+
* level, and configures the global brightness register accordingly.
31+
*
32+
* @param deviceConfig The JSON object containing device parameters.
33+
* @return True if initialization succeeded, false otherwise.
34+
*/
35+
bool LedDeviceHD108::init(const QJsonObject &deviceConfig)
36+
{
37+
bool isInitOK = false;
38+
39+
// First, let the base SPI provider perform its initialization
40+
if (ProviderSpi::init(deviceConfig))
41+
{
42+
// Read brightnessControlMaxLevel from the config, falling back to a default if absent
43+
_brightnessControlMaxLevel = deviceConfig["brightnessControlMaxLevel"].toInt(HD108_BRIGHTNESS_MAX_LEVEL);
44+
45+
// Log the brightness info
46+
Info(_log,
47+
"[%s] Setting maximum brightness to [%d] = %d%%",
48+
QSTRING_CSTR(_activeDeviceType),
49+
_brightnessControlMaxLevel,
50+
_brightnessControlMaxLevel * 100 / HD108_BRIGHTNESS_MAX_LEVEL);
51+
52+
// Combine the brightness levels into the HD108's 16-bit brightness field.
53+
// According to the HD108 spec, this is composed of a control bit plus
54+
// the brightness level split into three segments for R, G, B.
55+
_global_brightness = (1 << 15)
56+
| (_brightnessControlMaxLevel << 10)
57+
| (_brightnessControlMaxLevel << 5)
58+
| _brightnessControlMaxLevel;
59+
60+
isInitOK = true;
61+
}
62+
63+
return isInitOK;
64+
}
65+
66+
/**
67+
* @brief Writes a vector of RGB colors to the HD108 LEDs.
68+
*
69+
* The HD108 protocol requires:
70+
* - A start frame of 64 bits (8 bytes) all set to 0x00.
71+
* - For each LED, 64 bits:
72+
* - 16 bits of global brightness
73+
* - 16 bits for red
74+
* - 16 bits for green
75+
* - 16 bits for blue
76+
* - An end frame of at least (ledCount / 16 + 1) bytes of 0xFF.
77+
*
78+
* Each 8-bit color value is expanded to 16 bits by copying it into both the high
79+
* and low byte (e.g. 0x7F -> 0x7F7F). This ensures a correct mapping to the HD108's
80+
* internal 16-bit color resolution and allows for a true "off" state at 0x0000.
81+
*
82+
* @param ledValues A vector of ColorRgb (red, green, blue) structures.
83+
* @return The result of the SPI write operation (0 for success, or an error code).
84+
*/
85+
int LedDeviceHD108::write(const std::vector<ColorRgb> & ledValues)
86+
{
87+
// Calculate how much space we need in total:
88+
// - 8 bytes for the start frame
89+
// - 8 bytes per LED (16 bits global brightness + 16 bits R + G + B)
90+
// - end frame: ledCount / 16 + 1 bytes of 0xFF
91+
const size_t ledCount = ledValues.size();
92+
const size_t totalSize = 8 // start frame
93+
+ (ledCount * 8) // LED data (8 bytes each)
94+
+ (ledCount / 16 + 1); // end frame bytes
95+
96+
// Reserve enough space to avoid multiple allocations
97+
std::vector<uint8_t> hd108Data;
98+
hd108Data.reserve(totalSize);
99+
100+
// 1) Start frame: 64 bits of 0x00
101+
hd108Data.insert(hd108Data.end(), 8, 0x00);
102+
103+
// 2) For each LED, insert 8 bytes: 16 bits brightness, 16 bits R, 16 bits G, 16 bits B
104+
for (const ColorRgb &color : ledValues)
105+
{
106+
// Expand 8-bit color components to 16 bits each
107+
uint16_t red16 = (static_cast<uint16_t>(color.red) << 8) | color.red;
108+
uint16_t green16 = (static_cast<uint16_t>(color.green) << 8) | color.green;
109+
uint16_t blue16 = (static_cast<uint16_t>(color.blue) << 8) | color.blue;
110+
111+
// Global brightness (16 bits)
112+
hd108Data.push_back(_global_brightness >> 8);
113+
hd108Data.push_back(_global_brightness & 0xFF);
114+
115+
// Red (16 bits)
116+
hd108Data.push_back(red16 >> 8);
117+
hd108Data.push_back(red16 & 0xFF);
118+
119+
// Green (16 bits)
120+
hd108Data.push_back(green16 >> 8);
121+
hd108Data.push_back(green16 & 0xFF);
122+
123+
// Blue (16 bits)
124+
hd108Data.push_back(blue16 >> 8);
125+
hd108Data.push_back(blue16 & 0xFF);
126+
}
127+
128+
// 3) End frame: at least (ledCount / 16 + 1) bytes of 0xFF
129+
hd108Data.insert(hd108Data.end(), (ledCount / 16) + 1, 0xFF);
130+
131+
// Finally, transmit the assembled data via SPI
132+
return writeBytes(hd108Data.size(), hd108Data.data());
133+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
#ifndef LEDDEVICEHD108_H
2+
#define LEDDEVICEHD108_H
3+
4+
#include "ProviderSpi.h"
5+
6+
/// The maximal level supported by the HD108 brightness control field, 31
7+
const int HD108_BRIGHTNESS_MAX_LEVEL = 31;
8+
9+
10+
class LedDeviceHD108 : public ProviderSpi
11+
{
12+
13+
public:
14+
15+
///
16+
/// @brief Constructs an HD108 LED-device
17+
///
18+
/// @param deviceConfig Device's configuration as JSON-Object
19+
///
20+
explicit LedDeviceHD108(const QJsonObject &deviceConfig);
21+
22+
///
23+
/// @brief Constructs the LED-device
24+
///
25+
/// @param[in] deviceConfig Device's configuration as JSON-Object
26+
/// @return LedDevice constructed
27+
///
28+
static LedDevice* construct(const QJsonObject &deviceConfig);
29+
30+
private:
31+
32+
///
33+
/// @brief Initialise the device's configuration
34+
///
35+
/// @param[in] deviceConfig the JSON device configuration
36+
/// @return True, if success
37+
///
38+
bool init(const QJsonObject &deviceConfig) override;
39+
40+
///
41+
/// @brief Writes the RGB-Color values to the LEDs.
42+
///
43+
/// @param[in] ledValues The RGB-color per LED
44+
/// @return Zero on success, else negative
45+
///
46+
int write(const std::vector<ColorRgb> & ledValues) override;
47+
48+
/// The brighness level. Possibile values 1 .. 31.
49+
int _brightnessControlMaxLevel;
50+
uint16_t _global_brightness;
51+
};
52+
53+
#endif // LEDDEVICEHD108_H
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
{
2+
"type":"object",
3+
"required":true,
4+
"properties":{
5+
"output": {
6+
"type": "string",
7+
"title":"edt_dev_spec_spipath_title",
8+
"propertyOrder" : 1
9+
},
10+
"rate": {
11+
"type": "integer",
12+
"title":"edt_dev_spec_baudrate_title",
13+
"default": 3000000,
14+
"propertyOrder" : 2
15+
},
16+
"brightnessControlMaxLevel": {
17+
"type": "integer",
18+
"title":"edt_conf_color_brightness_title",
19+
"default": 31,
20+
"minimum": 1,
21+
"maximum": 31,
22+
"propertyOrder" : 4
23+
},
24+
"rewriteTime": {
25+
"type": "integer",
26+
"title":"edt_dev_general_rewriteTime_title",
27+
"default": 0,
28+
"append" : "edt_append_ms",
29+
"minimum": 0,
30+
"access" : "expert",
31+
"propertyOrder" : 5
32+
}
33+
},
34+
"additionalProperties": true
35+
}

0 commit comments

Comments
 (0)