feat(board): ledshield_rgb — Montana Black RGB-LED-Shield (analoge RGB-Streifen)
Viertes Analog-Profil neben den SM2135EH-Lampen: PWM-Kanaele R=PB13 (ch1), G=PB16 (ch2), B=PB15 (ch3) bei 800 Hz, PIN-belegt aus der Original-Firmware (qcl_build_config_lamp.h, HARDWARE_MOBL_LED_SHIELD). Kein CW/WW-Zweig: ct_min == ct_max ist das Erkennungszeichen fuer 'weiss = RGB-Mischung', nl_color.c gibt in diesem Fall alle drei Farbkanalgleich. Neues Bauziel in allen Skripten; test_release zaehlt Dateizahlen jetzt aus TARGETS statt hart 8/16.
This commit is contained in:
+1
-1
@@ -10,7 +10,7 @@ if(NOT CMAKE_BUILD_TYPE)
|
|||||||
set(CMAKE_BUILD_TYPE Release)
|
set(CMAKE_BUILD_TYPE Release)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(NINLIGHT_DEVICES evk e27_rgb e14_rgb gu10_rgb e27_cw e14_cw gu10_cw g95_cw)
|
set(NINLIGHT_DEVICES evk e27_rgb e14_rgb gu10_rgb e27_cw e14_cw gu10_cw g95_cw ledshield_rgb)
|
||||||
set(NINLIGHT_DEVICE "" CACHE STRING "Target device")
|
set(NINLIGHT_DEVICE "" CACHE STRING "Target device")
|
||||||
if(NOT NINLIGHT_DEVICE IN_LIST NINLIGHT_DEVICES)
|
if(NOT NINLIGHT_DEVICE IN_LIST NINLIGHT_DEVICES)
|
||||||
message(FATAL_ERROR "Set -DNINLIGHT_DEVICE=<one of: ${NINLIGHT_DEVICES}>")
|
message(FATAL_ERROR "Set -DNINLIGHT_DEVICE=<one of: ${NINLIGHT_DEVICES}>")
|
||||||
|
|||||||
@@ -31,6 +31,14 @@ const nl_board_t nl_boards[] = {
|
|||||||
* PB16=WW, PB15=CW - verify on a series bulb. */
|
* PB16=WW, PB15=CW - verify on a series bulb. */
|
||||||
{"g95_cw", "Klyqa G95 Vintage CW/WW", NL_DRV_PWM, false, 2000, 5000, 600,
|
{"g95_cw", "Klyqa G95 Vintage CW/WW", NL_DRV_PWM, false, 2000, 5000, 600,
|
||||||
{NONE, NONE, NONE, CH(1, NL_PB(13)), CH(3, NL_PB(15))}, NO_SM},
|
{NONE, NONE, NONE, CH(1, NL_PB(13)), CH(3, NL_PB(15))}, NO_SM},
|
||||||
|
/* Montana Black LED Shield (Klyqa LED-Strippen-Steuergeraet,
|
||||||
|
product_id @qcx.montana.black.ledshield.rgb): analog RGB-Streifen an
|
||||||
|
lowseitigen MOSFETs, kein CW/WW-Zweig. PIN-Kanaele und 800 Hz aus der
|
||||||
|
Original-Firmware (fw-w600 qcl_build_config_lamp.h, HARDWARE_MOBL_LED_SHIELD);
|
||||||
|
dort invertiert init mit invertiertem Duty, effektive Wirkung wie hier
|
||||||
|
active-high. ct_min == ct_max markiert "weiss = RGB-Kombination". */
|
||||||
|
{"ledshield_rgb", "Montana Black RGB LED Shield", NL_DRV_PWM, true, 4000, 4000, 800,
|
||||||
|
{CH(1, NL_PB(13)), CH(2, NL_PB(16)), CH(3, NL_PB(15)), NONE, NONE}, NO_SM},
|
||||||
};
|
};
|
||||||
|
|
||||||
const size_t nl_boards_count = sizeof(nl_boards) / sizeof(nl_boards[0]);
|
const size_t nl_boards_count = sizeof(nl_boards) / sizeof(nl_boards[0]);
|
||||||
|
|||||||
@@ -62,6 +62,11 @@ void nl_visual_to_channels(const nl_visual_t *v, uint16_t ct_min, uint16_t ct_ma
|
|||||||
/* keep the lowest brightness step visible */
|
/* keep the lowest brightness step visible */
|
||||||
if (!out[NL_CH_R] && !out[NL_CH_G] && !out[NL_CH_B] && lin[maxi] > 0)
|
if (!out[NL_CH_R] && !out[NL_CH_G] && !out[NL_CH_B] && lin[maxi] > 0)
|
||||||
out[NL_CH_R + maxi] = 1;
|
out[NL_CH_R + maxi] = 1;
|
||||||
|
} else if (ct_min == ct_max) {
|
||||||
|
/* RGB-only board (ledshield_rgb): no white channels wired, ct range
|
||||||
|
* collapsed to a point. White shows as an equal RGB mix. */
|
||||||
|
uint8_t w = to8(level);
|
||||||
|
out[NL_CH_R] = out[NL_CH_G] = out[NL_CH_B] = w ? w : 1;
|
||||||
} else {
|
} else {
|
||||||
uint32_t cw = level * nl_ct_cw_fraction(v->ct, ct_min, ct_max) / 65535u;
|
uint32_t cw = level * nl_ct_cw_fraction(v->ct, ct_min, ct_max) / 65535u;
|
||||||
uint32_t ww = level - cw;
|
uint32_t ww = level - cw;
|
||||||
|
|||||||
+17
-7
@@ -5,12 +5,12 @@
|
|||||||
void setUp(void) {}
|
void setUp(void) {}
|
||||||
void tearDown(void) {}
|
void tearDown(void) {}
|
||||||
|
|
||||||
static const char *IDS[] = {"evk", "e27_rgb", "e14_rgb", "gu10_rgb", "e27_cw", "e14_cw", "gu10_cw", "g95_cw"};
|
static const char *IDS[] = {"evk", "e27_rgb", "e14_rgb", "gu10_rgb", "e27_cw", "e14_cw", "gu10_cw", "g95_cw", "ledshield_rgb"};
|
||||||
|
|
||||||
static void test_all_devices_present(void)
|
static void test_all_devices_present(void)
|
||||||
{
|
{
|
||||||
TEST_ASSERT_EQUAL_UINT(8, nl_boards_count);
|
TEST_ASSERT_EQUAL_UINT(9, nl_boards_count);
|
||||||
for (size_t i = 0; i < 8; i++) {
|
for (size_t i = 0; i < 9; i++) {
|
||||||
const nl_board_t *b = nl_board_find(IDS[i]);
|
const nl_board_t *b = nl_board_find(IDS[i]);
|
||||||
TEST_ASSERT_NOT_NULL_MESSAGE(b, IDS[i]);
|
TEST_ASSERT_NOT_NULL_MESSAGE(b, IDS[i]);
|
||||||
TEST_ASSERT_EQUAL_STRING(IDS[i], b->id);
|
TEST_ASSERT_EQUAL_STRING(IDS[i], b->id);
|
||||||
@@ -34,9 +34,12 @@ static void test_rgb_flags_and_ct_ranges(void)
|
|||||||
5000 K - hard-coded per board rather than derived, because "warm variant" is a
|
5000 K - hard-coded per board rather than derived, because "warm variant" is a
|
||||||
property of that one product, not of a class of them. */
|
property of that one product, not of a class of them. */
|
||||||
bool g95 = strcmp(b->id, "g95_cw") == 0;
|
bool g95 = strcmp(b->id, "g95_cw") == 0;
|
||||||
|
/* ledshield_rgb has no white LEDs at all: ct_min == ct_max is the
|
||||||
|
sentinel "white mode renders as an RGB mix" (see nl_color.c). */
|
||||||
|
bool shield = strcmp(b->id, "ledshield_rgb") == 0;
|
||||||
TEST_ASSERT_EQUAL_MESSAGE(rgb, b->has_rgb, b->id);
|
TEST_ASSERT_EQUAL_MESSAGE(rgb, b->has_rgb, b->id);
|
||||||
TEST_ASSERT_EQUAL_UINT16_MESSAGE(g95 ? 2000 : 2700, b->ct_min, b->id);
|
TEST_ASSERT_EQUAL_UINT16_MESSAGE(shield ? 4000 : (g95 ? 2000 : 2700), b->ct_min, b->id);
|
||||||
TEST_ASSERT_EQUAL_UINT16_MESSAGE(g95 ? 5000 : 6500, b->ct_max, b->id);
|
TEST_ASSERT_EQUAL_UINT16_MESSAGE(shield ? 4000 : (g95 ? 5000 : 6500), b->ct_max, b->id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,8 +50,15 @@ static void test_pwm_boards_consistent(void)
|
|||||||
if (b->driver != NL_DRV_PWM)
|
if (b->driver != NL_DRV_PWM)
|
||||||
continue;
|
continue;
|
||||||
TEST_ASSERT_TRUE(b->pwm_freq_hz > 0);
|
TEST_ASSERT_TRUE(b->pwm_freq_hz > 0);
|
||||||
TEST_ASSERT_TRUE(b->pwm[NL_CH_CW].pwm_ch >= 0);
|
if (b->ct_min == b->ct_max) {
|
||||||
TEST_ASSERT_TRUE(b->pwm[NL_CH_WW].pwm_ch >= 0);
|
/* RGB-only board: white is an RGB mix, no white channels wired. */
|
||||||
|
TEST_ASSERT_TRUE(b->has_rgb);
|
||||||
|
TEST_ASSERT_EQUAL(-1, b->pwm[NL_CH_CW].pwm_ch);
|
||||||
|
TEST_ASSERT_EQUAL(-1, b->pwm[NL_CH_WW].pwm_ch);
|
||||||
|
} else {
|
||||||
|
TEST_ASSERT_TRUE(b->pwm[NL_CH_CW].pwm_ch >= 0);
|
||||||
|
TEST_ASSERT_TRUE(b->pwm[NL_CH_WW].pwm_ch >= 0);
|
||||||
|
}
|
||||||
for (int c = NL_CH_R; c <= NL_CH_B; c++)
|
for (int c = NL_CH_R; c <= NL_CH_B; c++)
|
||||||
TEST_ASSERT_EQUAL_MESSAGE(b->has_rgb, b->pwm[c].pwm_ch >= 0, b->id);
|
TEST_ASSERT_EQUAL_MESSAGE(b->has_rgb, b->pwm[c].pwm_ch >= 0, b->id);
|
||||||
for (int c = 0; c < NL_CH_COUNT; c++) {
|
for (int c = 0; c < NL_CH_COUNT; c++) {
|
||||||
|
|||||||
@@ -88,6 +88,22 @@ static void test_one_percent_is_visible(void)
|
|||||||
TEST_ASSERT_EQUAL_INT(1, ch[NL_CH_CW] + ch[NL_CH_WW]);
|
TEST_ASSERT_EQUAL_INT(1, ch[NL_CH_CW] + ch[NL_CH_WW]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_rgb_only_white_mode(void)
|
||||||
|
{
|
||||||
|
uint8_t ch[NL_CH_COUNT];
|
||||||
|
/* ct_min == ct_max: RGB-only board, white = equal RGB mix, no division. */
|
||||||
|
nl_visual_t v = vis(10000, NL_MODE_WHITE, 0, 0, 0, 4000);
|
||||||
|
nl_visual_to_channels(&v, 4000, 4000, ch);
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(255, ch[NL_CH_R]);
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(255, ch[NL_CH_G]);
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(255, ch[NL_CH_B]);
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(0, ch[NL_CH_CW]);
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(0, ch[NL_CH_WW]);
|
||||||
|
v.bri = 100; /* 1 % stays visible */
|
||||||
|
nl_visual_to_channels(&v, 4000, 4000, ch);
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(1, ch[NL_CH_R]);
|
||||||
|
}
|
||||||
|
|
||||||
static void test_zero_brightness_is_dark(void)
|
static void test_zero_brightness_is_dark(void)
|
||||||
{
|
{
|
||||||
uint8_t ch[NL_CH_COUNT];
|
uint8_t ch[NL_CH_COUNT];
|
||||||
@@ -107,6 +123,7 @@ int main(void)
|
|||||||
RUN_TEST(test_white_mix_keeps_total);
|
RUN_TEST(test_white_mix_keeps_total);
|
||||||
RUN_TEST(test_color_mode);
|
RUN_TEST(test_color_mode);
|
||||||
RUN_TEST(test_one_percent_is_visible);
|
RUN_TEST(test_one_percent_is_visible);
|
||||||
|
RUN_TEST(test_rgb_only_white_mode);
|
||||||
RUN_TEST(test_zero_brightness_is_dark);
|
RUN_TEST(test_zero_brightness_is_dark);
|
||||||
return UNITY_END();
|
return UNITY_END();
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -3,7 +3,7 @@
|
|||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
ROOT=$(cd "$(dirname "$0")/.." && pwd)
|
ROOT=$(cd "$(dirname "$0")/.." && pwd)
|
||||||
OUT=${BUILD_ROOT:-$ROOT/build}
|
OUT=${BUILD_ROOT:-$ROOT/build}
|
||||||
DEVICES=(evk e27_rgb e14_rgb gu10_rgb e27_cw e14_cw gu10_cw g95_cw)
|
DEVICES=(evk e27_rgb e14_rgb gu10_rgb e27_cw e14_cw gu10_cw g95_cw ledshield_rgb)
|
||||||
for d in "${DEVICES[@]}"; do
|
for d in "${DEVICES[@]}"; do
|
||||||
echo "== $d"
|
echo "== $d"
|
||||||
cmake -S "$ROOT" -B "$OUT/$d" -G Ninja \
|
cmake -S "$ROOT" -B "$OUT/$d" -G Ninja \
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
# Baut Firmwareabzuege OHNE einkompilierte WLAN-Zugangsdaten: die Lampen starten damit
|
# Baut Firmwareabzuege OHNE einkompilierte WLAN-Zugangsdaten: die Lampen starten damit
|
||||||
# im Onboarding-AP (ninlight-<mac4>), statt sich in ein fremdes WLAN einzuwaehlen.
|
# im Onboarding-AP (ninlight-<mac4>), statt sich in ein fremdes WLAN einzuwaehlen.
|
||||||
#
|
#
|
||||||
# Aufruf: tools/build_nocreds.sh [ziel ...] (ohne Argumente: alle acht Ziele)
|
# Aufruf: tools/build_nocreds.sh [ziel ...] (ohne Argumente: alle Ziele)
|
||||||
#
|
#
|
||||||
# Ergebnis: je Ziel ein Build-Verzeichnis unter $NINLIGHT_NOCREDS_BUILD_REL (Vorgabe
|
# Ergebnis: je Ziel ein Build-Verzeichnis unter $NINLIGHT_NOCREDS_BUILD_REL (Vorgabe
|
||||||
# "build-nocreds", relativ zum Repo-Wurzelverzeichnis) mit den ueblichen Artefakten
|
# "build-nocreds", relativ zum Repo-Wurzelverzeichnis) mit den ueblichen Artefakten
|
||||||
@@ -22,7 +22,7 @@ ROOT=$(cd "$(dirname "$0")/.." && pwd)
|
|||||||
DOCKER=${NINLIGHT_DOCKER:-$ROOT/tools/docker.sh}
|
DOCKER=${NINLIGHT_DOCKER:-$ROOT/tools/docker.sh}
|
||||||
BUILD_REL=${NINLIGHT_NOCREDS_BUILD_REL:-build-nocreds}
|
BUILD_REL=${NINLIGHT_NOCREDS_BUILD_REL:-build-nocreds}
|
||||||
|
|
||||||
DEVICES=(evk e27_rgb e14_rgb gu10_rgb e27_cw e14_cw gu10_cw g95_cw)
|
DEVICES=(evk e27_rgb e14_rgb gu10_rgb e27_cw e14_cw gu10_cw g95_cw ledshield_rgb)
|
||||||
if [ $# -gt 0 ]; then
|
if [ $# -gt 0 ]; then
|
||||||
TARGETS=("$@")
|
TARGETS=("$@")
|
||||||
else
|
else
|
||||||
|
|||||||
+2
-2
@@ -14,7 +14,7 @@ set -euo pipefail
|
|||||||
|
|
||||||
VERSION=${1:?Aufruf: tools/release.sh <version>}
|
VERSION=${1:?Aufruf: tools/release.sh <version>}
|
||||||
ROOT=$(cd "$(dirname "$0")/.." && pwd)
|
ROOT=$(cd "$(dirname "$0")/.." && pwd)
|
||||||
TARGETS=(evk e27_rgb e14_rgb gu10_rgb e27_cw e14_cw gu10_cw g95_cw)
|
TARGETS=(evk e27_rgb e14_rgb gu10_rgb e27_cw e14_cw gu10_cw g95_cw ledshield_rgb)
|
||||||
BUILD_REL=${NINLIGHT_NOCREDS_BUILD_REL:-build-nocreds}
|
BUILD_REL=${NINLIGHT_NOCREDS_BUILD_REL:-build-nocreds}
|
||||||
BUILD_DIR="$ROOT/$BUILD_REL"
|
BUILD_DIR="$ROOT/$BUILD_REL"
|
||||||
NAME="ninlight-$VERSION"
|
NAME="ninlight-$VERSION"
|
||||||
@@ -196,7 +196,7 @@ diesem Archiv sind **ohne einkompilierte WLAN-Zugangsdaten** gebaut: eine damit
|
|||||||
Lampe startet im Onboarding-Zugangspunkt \`ninlight-<mac4>\` (Passwort \`ninlight\`) und
|
Lampe startet im Onboarding-Zugangspunkt \`ninlight-<mac4>\` (Passwort \`ninlight\`) und
|
||||||
wird mit \`onboard.sh\` ins WLAN gebracht.
|
wird mit \`onboard.sh\` ins WLAN gebracht.
|
||||||
|
|
||||||
Ziele: evk, e27_rgb, e14_rgb, gu10_rgb, e27_cw, e14_cw, gu10_cw, g95_cw.
|
Ziele: evk, e27_rgb, e14_rgb, gu10_rgb, e27_cw, e14_cw, gu10_cw, g95_cw, ledshield_rgb.
|
||||||
|
|
||||||
## Was liegt wo
|
## Was liegt wo
|
||||||
|
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ FAILED=0
|
|||||||
pass() { echo "ok - $1"; }
|
pass() { echo "ok - $1"; }
|
||||||
fail() { echo "FEHLER - $1" >&2; FAILED=1; }
|
fail() { echo "FEHLER - $1" >&2; FAILED=1; }
|
||||||
|
|
||||||
TARGETS=(evk e27_rgb e14_rgb gu10_rgb e27_cw e14_cw gu10_cw g95_cw)
|
TARGETS=(evk e27_rgb e14_rgb gu10_rgb e27_cw e14_cw gu10_cw g95_cw ledshield_rgb)
|
||||||
|
|
||||||
# Wegwerf-Zugangsdaten - niemals die echten aus dem Repo.
|
# Wegwerf-Zugangsdaten - niemals die echten aus dem Repo.
|
||||||
SECRET_SSID="TestWLAN-4b7f-geheim"
|
SECRET_SSID="TestWLAN-4b7f-geheim"
|
||||||
@@ -233,9 +233,9 @@ if [ -x "$A/onboard.sh" ]; then pass "onboard.sh ist ausfuehrbar"; else fail "on
|
|||||||
N_OTA=$(find "$A/ota" -type f | wc -l | tr -d ' ')
|
N_OTA=$(find "$A/ota" -type f | wc -l | tr -d ' ')
|
||||||
N_FLASH=$(find "$A/flash" -type f | wc -l | tr -d ' ')
|
N_FLASH=$(find "$A/flash" -type f | wc -l | tr -d ' ')
|
||||||
N_MIG=$(find "$A/migrate" -type f | wc -l | tr -d ' ')
|
N_MIG=$(find "$A/migrate" -type f | wc -l | tr -d ' ')
|
||||||
[ "$N_OTA" = "8" ] && pass "ota/ enthaelt 8 Dateien" || fail "ota/ enthaelt $N_OTA statt 8"
|
[ "$N_OTA" = "${#TARGETS[@]}" ] && pass "ota/ enthaelt ${#TARGETS[@]} Dateien" || fail "ota/ enthaelt $N_OTA statt ${#TARGETS[@]}"
|
||||||
[ "$N_FLASH" = "8" ] && pass "flash/ enthaelt 8 Dateien" || fail "flash/ enthaelt $N_FLASH statt 8"
|
[ "$N_FLASH" = "${#TARGETS[@]}" ] && pass "flash/ enthaelt ${#TARGETS[@]} Dateien" || fail "flash/ enthaelt $N_FLASH statt ${#TARGETS[@]}"
|
||||||
[ "$N_MIG" = "16" ] && pass "migrate/ enthaelt 16 Dateien" || fail "migrate/ enthaelt $N_MIG statt 16"
|
[ "$N_MIG" = "$(( 2 * ${#TARGETS[@]} ))" ] && pass "migrate/ enthaelt $(( 2 * ${#TARGETS[@]} )) Dateien" || fail "migrate/ enthaelt $N_MIG statt $(( 2 * ${#TARGETS[@]} ))"
|
||||||
MISS=""
|
MISS=""
|
||||||
for t in "${TARGETS[@]}"; do
|
for t in "${TARGETS[@]}"; do
|
||||||
[ -f "$A/ota/ninlight_${t}_ota.img" ] || MISS="$MISS ota/$t"
|
[ -f "$A/ota/ninlight_${t}_ota.img" ] || MISS="$MISS ota/$t"
|
||||||
@@ -373,7 +373,7 @@ EXTRACT="$TMPROOT/x4"; mkdir -p "$EXTRACT"
|
|||||||
tar xzf "$SANDBOX/dist/ninlight-0.9.9.tar.gz" -C "$EXTRACT"
|
tar xzf "$SANDBOX/dist/ninlight-0.9.9.tar.gz" -C "$EXTRACT"
|
||||||
A4="$EXTRACT/ninlight-0.9.9"
|
A4="$EXTRACT/ninlight-0.9.9"
|
||||||
if [ ! -d "$A4/migrate" ]; then pass "migrate/ liegt nicht vor"; else fail "migrate/ trotzdem vorhanden"; fi
|
if [ ! -d "$A4/migrate" ]; then pass "migrate/ liegt nicht vor"; else fail "migrate/ trotzdem vorhanden"; fi
|
||||||
if [ -d "$A4/ota" ] && [ "$(find "$A4/ota" -type f | wc -l | tr -d ' ')" = "8" ]; then
|
if [ -d "$A4/ota" ] && [ "$(find "$A4/ota" -type f | wc -l | tr -d ' ')" = "${#TARGETS[@]}" ]; then
|
||||||
pass "ota/ trotzdem vollstaendig"
|
pass "ota/ trotzdem vollstaendig"
|
||||||
else
|
else
|
||||||
fail "ota/ unvollstaendig"
|
fail "ota/ unvollstaendig"
|
||||||
|
|||||||
Reference in New Issue
Block a user