Skip to content

Commit f3ea59d

Browse files
committed
Update docs IO, parlio LUT tables
Back-end ======== - Module IO: add pin_RS485, add more pins to board_MHCP4Nano - Parlio: remove brightness argument and use ledsDriver lut tables
1 parent 33fb27d commit f3ea59d

File tree

6 files changed

+69
-26
lines changed

6 files changed

+69
-26
lines changed

docs/moonbase/inputoutput.md

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@ Currently the following boards are defined. Not all are supported yet 🚧
1010

1111
For each board the following presets are defined:
1212

13-
* Modded: if any change to the default preset is made.
13+
* Modded: if any change to the default preset is made. Press off to go back to standard. If any pin is not default, modded will be set.
1414
* Max Power in Watts: adjust the brightness to approach this max power, depending on the number of LEDs used. Default 10: 5V * 2A = 10W (so it runs fine on USB). Used by LED drivers, see [Drivers](../../moonlight/drivers/)
1515
* Jumper1: If the board contains a jumper, it can define pin behaviour. Eg. select between Infrared and Ethernet.
16-
* Pins: This module is the central place to assign functionality to gpio pins. Other modules and nodes use the pin assignments made here.
16+
* Pins: Assign functionality to gpio pins. Other modules and nodes use the pin assignments made here.
1717
* GPIO = gpio_num;
1818
* Usage: See below
1919
* Index: specify first, second, third, ... usage output, e.g. LED D01 to LED D16
@@ -59,13 +59,21 @@ For each board the following presets are defined:
5959
* [Dig 2Go](https://quinled.info/quinled-dig2go/), [Dig Uno](https://quinled.info/pre-assembled-quinled-dig-uno/), [Dig Quad](https://quinled.info/pre-assembled-quinled-dig-quad/): Choose the esp32-d0 (4MB) board in the [MoonLight Installer](../../gettingstarted/installer/)
6060
* [Dig Octa](https://quinled.info/quinled-dig-octa/): Choose the esp32-d0-16mb board in the [MoonLight Installer](../../gettingstarted/installer/)
6161
* On first install, erase flash first (Especially when other firmware like WLED was on it) as MoonLight uses a partition scheme with 3MB of flash (currently no OTA support).
62-
* You might need to reset your router if you first run WLED on the same MCU and no new IP is assigned.
62+
* After install, select the QuinLED board preset to have the pins assigned correctly.
63+
64+
!!! tip "Reset router"
65+
You might need to reset your router if you first run WLED on the same board and no new IP is assigned.
6366

6467
!!! Tip "Dig Uno USB"
6568
Remove fuse to connect USB cable to flash the board.
6669

6770
### MyHome-Control ESP32-P4 shield
6871

72+
* Choose the esp32-p4-nano board in the [MoonLight Installer](../../gettingstarted/installer/)
73+
* On new boards, the WiFi coprocessor needs to be updated first to a recent version, currently v2.0.17, see the link in the [MoonLight Installer](../../gettingstarted/installer/)
74+
* After install, select the MHC P4 shield board preset to have the pins assigned correctly.
75+
* Add the Parallel LED Driver, see [Drivers](). It uses @troyhacks his parallel IO driver to drive all the 12 LED pins of the P4 shield.
76+
6977
![ESP32-P4 shield](https://shop.myhome-control.de/thumbnail/87/41/c2/1762031307/WLED_ESP32_P4_Shield_02_1920x1326.jpg?ts=1762031315){: style="width:320px"}
7078

7179
[ESP32-P4 shield](https://shop.myhome-control.de/en/ABC-WLED-ESP32-P4-shield/HW10027)

platformio.ini

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ build_flags =
5656
-D BUILD_TARGET=\"$PIOENV\"
5757
-D APP_NAME=\"MoonLight\" ; 🌙 Must only contain characters from [a-zA-Z0-9-_] as this is converted into a filename
5858
-D APP_VERSION=\"0.6.1\" ; semver compatible version string
59-
-D APP_DATE=\"2025120714\" ; 🌙
59+
-D APP_DATE=\"2025121212\" ; 🌙
6060

6161
-D PLATFORM_VERSION=\"pioarduino-55.03.34\" ; 🌙 make sure it matches with above plaftform
6262

src/MoonBase/Modules/ModuleIO.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ enum IO_PinUsage {
5858
pin_SPI_MOSI,
5959
pin_PHY_CS,
6060
pin_PHY_IRQ,
61+
pin_RS485,
6162
pin_Reserved,
6263
pin_count
6364
};
@@ -180,6 +181,7 @@ class ModuleIO : public Module {
180181
addControlValue(control, "SPI MOSI");
181182
addControlValue(control, "PHY CS");
182183
addControlValue(control, "PHY IRQ");
184+
addControlValue(control, "RS-485");
183185
addControlValue(control, "Reserved");
184186

185187
control = addControl(rows, "index", "number", 1, 32); // max 32 of one type, e.g 32 led pins
@@ -377,6 +379,18 @@ class ModuleIO : public Module {
377379
object["maxPower"] = 10; // USB compliant
378380
uint8_t ledPins[16] = {21, 20, 25, 5, 7, 23, 8, 27, 3, 22, 24, 4, 46, 47, 2, 48}; // LED_PINS
379381
for (int i = 0; i < sizeof(ledPins); i++) pinAssigner.assignPin(ledPins[i], pin_LED);
382+
pinAssigner.assignPin(33, pin_I2S_SD);
383+
pinAssigner.assignPin(26, pin_I2S_WS);
384+
pinAssigner.assignPin(32, pin_I2S_SCK);
385+
pinAssigner.assignPin(36, pin_I2S_MCLK);
386+
pinAssigner.assignPin(3, pin_RS485);
387+
pinAssigner.assignPin(4, pin_RS485);
388+
pinAssigner.assignPin(22, pin_RS485);
389+
pinAssigner.assignPin(24, pin_RS485);
390+
pinAssigner.assignPin(2, pin_Exposed);
391+
pinAssigner.assignPin(46, pin_Exposed);
392+
pinAssigner.assignPin(47, pin_Exposed);
393+
pinAssigner.assignPin(48, pin_Exposed);
380394
} else if (boardID == board_YvesV48) {
381395
pinAssigner.assignPin(3, pin_LED);
382396
} else if (boardID == board_TroyP4Nano) {

src/MoonLight/Nodes/Drivers/D_ParallelLEDDriver.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,15 +52,18 @@ class ParallelLEDDriver : public DriverNode {
5252
if (!initDone) return;
5353

5454
if (layer->layerP->lights.header.isPositions == 0) {
55-
DriverNode::loop();
55+
DriverNode::loop(); // This populates the LUT tables!
5656

5757
uint8_t nrOfPins = min(layerP.nrOfLedPins, layerP.nrOfAssignedPins);
5858

5959
#ifndef CONFIG_IDF_TARGET_ESP32P4
6060
if (ledsDriver.total_leds > 0) ledsDriver.showPixels(WAIT);
6161
#else
62-
show_parlio(pins, layer->layerP->lights.header.nrOfLights, layer->layerP->lights.channels, ledsDriver._brightness, layer->layerP->lights.header.channelsPerLight == 4, nrOfPins, layer->layerP->ledsPerPin[0], // different ledsPerPin not supported yet
63-
layer->layerP->lights.header.offsetRed, layer->layerP->lights.header.offsetGreen, layer->layerP->lights.header.offsetBlue);
62+
// Pass the LUT tables instead of brightness
63+
// No brightness parameter - LUTs are accessed directly!
64+
show_parlio(pins, layer->layerP->lights.header.nrOfLights, layer->layerP->lights.channels,
65+
// REMOVED: ledsDriver._brightness,
66+
layer->layerP->lights.header.channelsPerLight == 4, nrOfPins, layer->layerP->ledsPerPin[0], layer->layerP->lights.header.offsetRed, layer->layerP->lights.header.offsetGreen, layer->layerP->lights.header.offsetBlue);
6467
#endif
6568
}
6669
#else // ESP32_LEDSDRIVER

src/MoonLight/Nodes/Drivers/parlio.cpp

Lines changed: 36 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,17 @@
1616
#include "driver/parlio_tx.h"
1717
#include "portmacro.h"
1818

19+
// Access the global LED driver to use its LUT tables directly
20+
#include "I2SClocklessLedDriver.h"
21+
extern I2SClocklessLedDriver ledsDriver;
22+
1923
// --- Namespace for specialized, high-performance worker functions ---
2024
namespace LedMatrixDetail {
2125

2226
// This intermediate step is common to all packing functions.
2327
// It transposes the data for 32 time-slices into a cache-friendly temporary buffer.
2428
inline void transpose_32_slices(uint32_t (&transposed_slices)[32], // Output buffer (on stack)
25-
const uint8_t* input_buffer, const uint32_t pixel_in_pin, const uint32_t component_in_pixel, const uint32_t pixels_per_pin, const uint32_t num_active_pins,
26-
const uint32_t COMPONENTS_PER_PIXEL, const uint32_t* waveform_cache, const uint8_t* brightness_cache) {
29+
const uint8_t* input_buffer, const uint32_t pixel_in_pin, const uint32_t component_in_pixel, const uint32_t pixels_per_pin, const uint32_t num_active_pins, const uint32_t COMPONENTS_PER_PIXEL, const uint32_t* waveform_cache, const uint8_t* brightness_cache) {
2730
memset(transposed_slices, 0, sizeof(uint32_t) * 32);
2831

2932
for (uint32_t pin = 0; pin < num_active_pins; ++pin) {
@@ -145,27 +148,23 @@ uint8_t gamma8(uint8_t b) { // we do nothing with gamma for now
145148
}
146149

147150
// 1. Add the RGB offsets parameter to the function signature
148-
void create_transposed_led_output_optimized(const uint8_t* input_buffer, uint16_t* output_buffer, const uint32_t pixels_per_pin, const uint32_t num_active_pins, const bool is_rgbw, const uint8_t bri,
149-
const uint8_t offsetR, const uint8_t offsetG, const uint8_t offsetB) {
151+
void create_transposed_led_output_optimized(const uint8_t* input_buffer, uint16_t* output_buffer, const uint32_t pixels_per_pin, const uint32_t num_active_pins, const bool is_rgbw, const uint8_t offsetR, const uint8_t offsetG, const uint8_t offsetB) {
152+
// Only keep waveform cache (for WS2812 protocol timing)
150153
static uint32_t waveform_cache[256];
151-
static uint8_t brightness_cache[256];
152-
static uint8_t last_bri = 0;
154+
static bool waveform_cache_initialized = false;
153155

154156
static const uint16_t bitpatterns[16] = {
155-
0b1000100010001000, 0b1000100010001110, 0b1000100011101000, 0b1000100011101110, 0b1000111010001000, 0b1000111010001110, 0b1000111011101000, 0b1000111011101110,
156-
0b1110100010001000, 0b1110100010001110, 0b1110100011101000, 0b1110100011101110, 0b1110111010001000, 0b1110111010001110, 0b1110111011101000, 0b1110111011101110,
157+
0b1000100010001000, 0b1000100010001110, 0b1000100011101000, 0b1000100011101110, 0b1000111010001000, 0b1000111010001110, 0b1000111011101000, 0b1000111011101110, 0b1110100010001000, 0b1110100010001110, 0b1110100011101000, 0b1110100011101110, 0b1110111010001000, 0b1110111010001110, 0b1110111011101000, 0b1110111011101110,
157158
};
158159

159-
if (bri != last_bri) {
160-
for (int i = 0; i < 256; ++i) {
161-
brightness_cache[i] = (gamma8(i) * bri) >> 8;
162-
}
160+
// Initialize waveform cache ONCE (doesn't depend on brightness)
161+
if (!waveform_cache_initialized) {
163162
for (int i = 0; i < 256; ++i) {
164163
const uint16_t p1 = bitpatterns[i >> 4];
165164
const uint16_t p2 = bitpatterns[i & 0x0F];
166165
waveform_cache[i] = (uint32_t(p2) << 16) | p1;
167166
}
168-
last_bri = bri;
167+
waveform_cache_initialized = true;
169168
}
170169

171170
const uint32_t COMPONENTS_PER_PIXEL = is_rgbw ? 4 : 3;
@@ -191,6 +190,7 @@ void create_transposed_led_output_optimized(const uint8_t* input_buffer, uint16_
191190

192191
uint8_t* out_base_ptr = reinterpret_cast<uint8_t*>(output_buffer);
193192

193+
// Component mapping for color order
194194
uint8_t component_map[4] = {0, 1, 2, 3}; // Default to RGB(W)
195195
component_map[0] = offsetR;
196196
component_map[1] = offsetG;
@@ -205,6 +205,26 @@ void create_transposed_led_output_optimized(const uint8_t* input_buffer, uint16_
205205

206206
uint32_t transposed_slices[32];
207207

208+
// Select the appropriate LUT based on which INPUT channel we're processing
209+
const uint8_t* brightness_cache;
210+
switch (component_in_pixel) {
211+
case 0:
212+
brightness_cache = ledsDriver.__red_map;
213+
break;
214+
case 1:
215+
brightness_cache = ledsDriver.__green_map;
216+
break;
217+
case 2:
218+
brightness_cache = ledsDriver.__blue_map;
219+
break;
220+
case 3:
221+
brightness_cache = ledsDriver.__white_map;
222+
break;
223+
default:
224+
brightness_cache = ledsDriver.__red_map;
225+
break; // Fallback
226+
}
227+
208228
LedMatrixDetail::transpose_32_slices(transposed_slices, input_buffer, pixel_in_pin, input_component, pixels_per_pin, num_active_pins, COMPONENTS_PER_PIXEL, waveform_cache, brightness_cache);
209229

210230
const uint32_t component_start_word = (pixel_in_pin * WAVEFORM_WORDS_PER_PIXEL) + (component_in_pixel * 32);
@@ -241,8 +261,7 @@ parlio_transmit_config_t transmit_config = {.idle_value = 0x00, // the idle val
241261

242262
static portMUX_TYPE parlio_spinlock = portMUX_INITIALIZER_UNLOCKED;
243263

244-
uint8_t IRAM_ATTR __attribute__((hot)) show_parlio(uint8_t* parallelPins, uint32_t length, uint8_t* buffer_in, uint8_t bri, bool isRGBW, uint8_t outputs, uint16_t leds_per_output, uint8_t offSetR,
245-
uint8_t offsetG, uint8_t offsetB) {
264+
uint8_t IRAM_ATTR __attribute__((hot)) show_parlio(uint8_t* parallelPins, uint32_t length, uint8_t* buffer_in, bool isRGBW, uint8_t outputs, uint16_t leds_per_output, uint8_t offSetR, uint8_t offsetG, uint8_t offsetB) {
246265
if (length != outputs * leds_per_output) {
247266
delay(100);
248267
Serial.printf("Parallel IO isn't set correctly. Check length, outputs, and LEDs per output. (%d != %d x %d)\n", length, outputs, leds_per_output);
@@ -299,7 +318,7 @@ uint8_t IRAM_ATTR __attribute__((hot)) show_parlio(uint8_t* parallelPins, uint32
299318
parlio_config.flags.invert_valid_out = 0;
300319

301320
if (parlio_tx_unit != NULL) {
302-
ESP_ERROR_CHECK(parlio_tx_unit_wait_all_done(parlio_tx_unit, -1));
321+
ESP_ERROR_CHECK(parlio_tx_unit_wait_all_done(parlio_tx_unit, portMAX_DELAY)); // wait forever
303322
ESP_ERROR_CHECK(parlio_tx_unit_disable(parlio_tx_unit));
304323
ESP_ERROR_CHECK(parlio_del_tx_unit(parlio_tx_unit));
305324
parlio_tx_unit = NULL;
@@ -364,7 +383,7 @@ uint8_t IRAM_ATTR __attribute__((hot)) show_parlio(uint8_t* parallelPins, uint32
364383
// offsetB = 2;
365384
#endif
366385

367-
create_transposed_led_output_optimized(parallel_buffer_remapped, parallel_buffer_repacked, leds_per_output, outputs, isRGBW, bri, offSetR, offsetG, offsetB);
386+
create_transposed_led_output_optimized(parallel_buffer_remapped, parallel_buffer_repacked, leds_per_output, outputs, isRGBW, offSetR, offsetG, offsetB);
368387

369388
// Calculate the exact size of ONE PIXEL's data in bits and bytes.
370389
const uint32_t symbols_per_pixel = isRGBW ? 128 : 96;

src/MoonLight/Nodes/Drivers/parlio.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,5 @@
1414

1515
#if FT_MOONLIGHT
1616

17-
uint8_t show_parlio(uint8_t* parallelPins, uint32_t length, uint8_t* buffer_in, uint8_t bri, bool isRGBW, uint8_t outputs, uint16_t leds_per_output, uint8_t offSetR, uint8_t offsetG, uint8_t offsetB);
18-
17+
uint8_t show_parlio(uint8_t* parallelPins, uint32_t length, uint8_t* buffer_in, bool isRGBW, uint8_t outputs, uint16_t leds_per_output, uint8_t offSetR, uint8_t offsetG, uint8_t offsetB);
1918
#endif

0 commit comments

Comments
 (0)