Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion docs/gettingstarted/installer.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,18 @@ For devices that already have MoonLight installed, go to [Update](#update-moonli

## Prepare an ESP32 device

Recommended device to get started with: [QuinLED-dig2go](https://quinled.info/quinled-dig2go/)
Recommended plug and play device to get started with, up to 4096 LEDs: [QuinLED-dig2go](https://quinled.info/quinled-dig2go/)

![Dig2Go](https://shop.allnetchina.cn/cdn/shop/products/Led_4.jpg?v=1680836018&width=1600){: style="width:250px"}

Recommended low-cost DIY board up to 10K LEDs: [ESP32-S3 n16r8](https://s.click.aliexpress.com/e/_DBAtJ2H){:target="_blank"}

![esp32-s3-n16r8v](../firmware/installer/images/esp32-s3-n8r8v.jpg){: style="width:100px"}

Recommended state-of-the-art DIY board, up to 16K - 98K LEDs !!: [ESP32-P4-Nano](https://www.waveshare.com/esp32-p4-nano.htm){:target="_blank"}

![esp32-p4-nano](../firmware/installer/images/esp32-p4-nano.jpg){: style="width:100px"}

See [Hardware](../hardware) for other options.

Connect the device to your PC using a USB cable that **supports data transfer**.
Expand Down
5 changes: 5 additions & 0 deletions interface/src/routes/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@
<a class="btn btn-primary" href="/moonbase/module?group=moonlight&module=effects">Effects</a
>
</span>
<span>
5
<a class="btn btn-primary" href="/moonbase/module?group=moonlight&module=lightscontrol">Control</a
>
</span>
<span>
🛟
<a
Expand Down
25,331 changes: 12,666 additions & 12,665 deletions lib/framework/WWWData.h

Large diffs are not rendered by default.

3 changes: 1 addition & 2 deletions src/MoonBase/Module.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,8 @@ void setDefaults(JsonObject controls, JsonArray definition) {
}

// shared static variables
SemaphoreHandle_t ModuleState::updateMutex = xSemaphoreCreateMutex();
SemaphoreHandle_t ModuleState::updateMutex = nullptr;
UpdatedItem ModuleState::updatedItem;
bool ModuleState::updatePending = false;
Char<20> ModuleState::updateOriginId;

Comment on lines 38 to 42
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's examine the file structure and the specific lines mentioned
cd src/MoonBase && wc -l Module.cpp

Repository: MoonModules/MoonLight

Length of output: 79


🏁 Script executed:

# Read the problematic code sections
cat -n Module.cpp | sed -n '35,45p'

Repository: MoonModules/MoonLight

Length of output: 107


🏁 Script executed:

# Also check lines 274-279
cat -n Module.cpp | sed -n '270,280p'

Repository: MoonModules/MoonLight

Length of output: 107


🏁 Script executed:

# Let's see the class definition and understand ModuleState
rg "class ModuleState" -A 30

Repository: MoonModules/MoonLight

Length of output: 1787


🏁 Script executed:

# Look for ModuleState::update method
rg "ModuleState::update" -B 2 -A 15

Repository: MoonModules/MoonLight

Length of output: 25907


🏁 Script executed:

# Search for usage of updatedItem and updateOriginId
rg "updatedItem|updateOriginId" -n

Repository: MoonModules/MoonLight

Length of output: 26488


🏁 Script executed:

# Check lines 274-279 to see what else the review comment applies to
head -n 285 src/MoonBase/Module.cpp | tail -n 20

Repository: MoonModules/MoonLight

Length of output: 866


🏁 Script executed:

# Look at the full postUpdate implementation to understand synchronization
rg "void postUpdate" -B 2 -A 20

Repository: MoonModules/MoonLight

Length of output: 1524


🏁 Script executed:

# Check for concurrent access patterns to updateOriginId
rg "updateOriginId" -B 1 -A 1 | head -100

Repository: MoonModules/MoonLight

Length of output: 2904


Static state members should be synchronized or converted to instance variables to prevent data races across concurrent module updates.

The static updateOriginId is written in ModuleState::update() (line 277) without mutex protection, yet read from multiple onUpdate() handlers across modules (e.g., ModuleIO.h, Module.h) also without synchronization. This creates a data race when updates from http/websocket/sveltekit contexts overlap. Unlike updatedItem, which is guarded by updateMutex in postUpdate(), the static updateOriginId slot is accessed unsynchronized and shared across all ModuleState instances.

Consider making these instance members rather than static to eliminate the shared mutable state:

  • Store updateOriginId and updatedItem per ModuleState instance
  • Pass state.updateOriginId to handlers instead of accessing a global slot

This also applies to lines 274–316, where the ModuleState::update() method assigns to both shared static members.

🤖 Prompt for AI Agents
In `@src/MoonBase/Module.cpp` around lines 38 - 42, The static shared state
(ModuleState::updateOriginId and ModuleState::updatedItem) is causing data races
because ModuleState::update() writes them without synchronization while onUpdate
handlers read them; change these static members into instance members on
ModuleState (e.g., move updateOriginId and updatedItem into the ModuleState
object and remove the static declarations), update ModuleState::update() to
write to this->updateOriginId/this->updatedItem, and adjust all callers
(onUpdate handlers in ModuleIO.h, Module.h and any postUpdate()/postUpdateMutex
usage) to read the per-instance fields or accept the originId/updatedItem as
parameters instead of accessing globals; ensure updateMutex either becomes
per-instance or remains static but protects only truly-shared resources.

void ModuleState::setupData() {
Expand Down
10 changes: 6 additions & 4 deletions src/MoonBase/Module.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,16 +46,18 @@ class ModuleState {

static UpdatedItem updatedItem;
static SemaphoreHandle_t updateMutex;
static bool updatePending;
volatile bool updatePending = false;

static Char<20> updateOriginId; // static, written by ModuleState::update, no mutex needed as written by one process at a time (http mostly, sveltekit sometimes recursively)

ModuleState() {
EXT_LOGD(MB_TAG, "ModuleState constructor");

if (updateMutex == nullptr) {
EXT_LOGE(MB_TAG, "Failed to create updateMutex");
}
if (!updateMutex) {
EXT_LOGD(MB_TAG, "creating updateMutex");
updateMutex = xSemaphoreCreateMutex();
if (!updateMutex) EXT_LOGE(MB_TAG, "Failed to create updateMutex");
}
Comment on lines 47 to 60
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: static updatedItem + per-instance updatePending can corrupt cross-module updates.

Scenario: Module A sets A.updatePending=true and writes ModuleState::updatedItem. Before A’s loop consumes it, Module B posts an update and overwrites the same static updatedItem. Now A still sees updatePending==true but processes B’s item (or vice versa). This can also drop intermediate updates.

Strongly suggest making the queued state per ModuleState instance (preferred), e.g.:

  • make UpdatedItem updatedItem;, SemaphoreHandle_t updateMutex;, and Char<20> updateOriginId; non-static members, and in ModuleState::update(...) set state.updateOriginId = originId;, or
  • if you really want one global queue, then the queued item must include module identity and the consumer must dispatch appropriately (and you likely want a small queue, not a single slot).
Proposed direction (per-instance storage)
-  static UpdatedItem updatedItem;
-  static SemaphoreHandle_t updateMutex;
-  volatile bool updatePending = false;
-  static Char<20> updateOriginId;
+  UpdatedItem updatedItem;
+  SemaphoreHandle_t updateMutex = nullptr;
+  bool updatePending = false;
+  Char<20> updateOriginId;

(And update corresponding definitions/uses in src/MoonBase/Module.cpp.)

Also applies to: 112-141

Comment on lines 53 to +60
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Find and examine Module.h to understand updateMutex declaration and ModuleState structure
fd -t f "Module.h" --exec cat -n {} +

Repository: MoonModules/MoonLight

Length of output: 14144


🏁 Script executed:

# Search for ModuleState class definition and updateMutex declaration
rg -A 20 "class ModuleState" src/MoonBase/

Repository: MoonModules/MoonLight

Length of output: 1225


🏁 Script executed:

# Check how many ModuleState instances are created and in what contexts
rg "ModuleState" src/ -t cpp -t h --context 2

Repository: MoonModules/MoonLight

Length of output: 21114


🏁 Script executed:

# Compare with PhysicalLayer and Nodes.h to understand the project's mutex pattern
fd -t f "PhysicalLayer.cpp" "Nodes.h" --exec echo "File: {}" \; -exec head -100 {} \;

Repository: MoonModules/MoonLight

Length of output: 307


🏁 Script executed:

# Search for Module instantiation/creation patterns
rg "new Module|Module(" src/ -t cpp -t h --context 2 | head -100

Repository: MoonModules/MoonLight

Length of output: 151


🏁 Script executed:

# Check Module.cpp constructor to understand instantiation context
cat src/MoonBase/Module.cpp | head -150

Repository: MoonModules/MoonLight

Length of output: 6645


🏁 Script executed:

# Compare updateMutex pattern with other mutexes in project (effectsMutex, driversMutex, etc.)
rg "xSemaphoreCreateMutex" src/ -t cpp -t h -B 3 -A 3

Repository: MoonModules/MoonLight

Length of output: 1316


🏁 Script executed:

# Check main.cpp to see how Module instances are created
rg "Module" src/main.cpp -A 2 -B 2 | head -80

Repository: MoonModules/MoonLight

Length of output: 3210


🏁 Script executed:

# Look for any dynamic Module instantiation or creation from tasks
rg "new Module|new ModuleChannels|new ModuleLights" src/ -t cpp -t h

Repository: MoonModules/MoonLight

Length of output: 47


🏁 Script executed:

# Compare PhysicalLayer mutex initialization style with ModuleState
rg "SemaphoreHandle_t.*xSemaphoreCreateMutex" src/ -B 2 -A 2

Repository: MoonModules/MoonLight

Length of output: 727


Mutex initialization pattern differs from project conventions; consider aligning with PhysicalLayer's immediate creation approach.

The lazy initialization of updateMutex in the ModuleState constructor is inconsistent with the project's established pattern. PhysicalLayer and other project code create mutexes immediately at member declaration (e.g., SemaphoreHandle_t effectsMutex = xSemaphoreCreateMutex();), not lazily. While module instances are currently global static objects initialized serially at boot before tasks start (making a race condition theoretical rather than practical), the lazy pattern should be aligned with project conventions by creating updateMutex at declaration time instead.

Also, volatile on updatePending adds no correctness benefit; the mutex alone provides necessary synchronization.

🤖 Prompt for AI Agents
In `@src/MoonBase/Module.h` around lines 53 - 60, Move updateMutex creation to
immediate member initialization to match project conventions used in
PhysicalLayer (e.g., initialize updateMutex = xSemaphoreCreateMutex() at
declaration inside ModuleState) instead of lazy-creating it in the ModuleState()
constructor; also remove the volatile qualifier from updatePending since the
mutex provides the required synchronization. Update any constructor logging
accordingly (remove the lazy-create branch) and ensure the member declarations
reference the newly-initialized updateMutex and the non-volatile updatePending.


if (!gModulesDoc) {
EXT_LOGD(MB_TAG, "Creating doc");
Expand Down
57 changes: 32 additions & 25 deletions src/MoonBase/Modules/ModuleIO.h
Original file line number Diff line number Diff line change
Expand Up @@ -604,14 +604,13 @@ class ModuleIO : public Module {
}
}


void readPins() {
uint8_t pinRS485TX = UINT8_MAX;
uint8_t pinRS485RX = UINT8_MAX;
uint8_t pinRS485DE = UINT8_MAX;
uint8_t pinRS485TX = UINT8_MAX;
uint8_t pinRS485RX = UINT8_MAX;
uint8_t pinRS485DE = UINT8_MAX;

#if FT_ENABLED(FT_ETHERNET)
EXT_LOGD(MB_TAG, "Try to configure ethernet");
EXT_LOGI(MB_TAG, "Try to configure ethernet");
EthernetSettingsService* ess = _sveltekit->getEthernetSettingsService();
#ifdef CONFIG_IDF_TARGET_ESP32S3
// Note: Ethernet pin types are signed (int8_t) and use -1, not UINT8_MAX, to indicate unset state
Expand Down Expand Up @@ -672,7 +671,7 @@ class ModuleIO : public Module {
} else if (usage == pin_Battery) {
pinBattery = pinObject["GPIO"];
EXT_LOGD(ML_TAG, "pinBattery found %d", pinBattery);
}
}
}
#endif

Expand Down Expand Up @@ -703,7 +702,7 @@ class ModuleIO : public Module {
} else if (usage == pin_RS485_TX) {
rs485_ios_updated = true;
pinRS485TX = pinObject["GPIO"];
}
}
}

// Check if all RS485 pins are specified
Expand All @@ -714,9 +713,9 @@ class ModuleIO : public Module {
uart_config_t uart_config = {
.baud_rate = 9600,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE, // Flow control handled by RS485 driver
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE, // Flow control handled by RS485 driver
.source_clk = UART_SCLK_DEFAULT,
};
uart_driver_delete(UART_NUM_1);
Expand All @@ -725,7 +724,7 @@ class ModuleIO : public Module {
ESP_ERROR_CHECK(uart_set_pin(UART_NUM_1, pinRS485TX, pinRS485RX, pinRS485DE, UART_PIN_NO_CHANGE));
ESP_ERROR_CHECK(uart_set_mode(UART_NUM_1, UART_MODE_RS485_HALF_DUPLEX));

#ifdef DEMOCODE_FOR_SHT30_SENSOR
#ifdef DEMOCODE_FOR_SHT30_SENSOR
// Modbus RTU Request: [Addr][Func][RegHi][RegLo][CountHi][CountLo][CRC_L][CRC_H]
// To read Reg 0 & 1 from Slave 0x01: 01 03 00 00 00 02 C4 0B
const uint8_t request[] = {0x01, 0x03, 0x00, 0x00, 0x00, 0x02, 0xC4, 0x0B};
Expand All @@ -736,19 +735,19 @@ class ModuleIO : public Module {

// Wait for response (timeout 1 second)
int len = uart_read_bytes(UART_NUM_1, response, 128, pdMS_TO_TICKS(100));

if (len > 8) {
EXT_LOGD(ML_TAG, "Answer received: %d %d %d %d %d %d %d %d %d", response[0], response[1], response[2], response[3], response[4], response[5], response[6], response[7], response[8]);
float humidity = ((float)response[3])*256 + (float)response[4];
float temperature = ((float)response[5])*256 +(float)response[6];
EXT_LOGD(ML_TAG, "humidity: %f temperature: %f", humidity/10, temperature/10);
// Process registers here (response[3] to response[6] contain the data)
EXT_LOGD(ML_TAG, "Answer received: %d %d %d %d %d %d %d %d %d", response[0], response[1], response[2], response[3], response[4], response[5], response[6], response[7], response[8]);
float humidity = ((float)response[3]) * 256 + (float)response[4];
float temperature = ((float)response[5]) * 256 + (float)response[6];
EXT_LOGD(ML_TAG, "humidity: %f temperature: %f", humidity / 10, temperature / 10);
// Process registers here (response[3] to response[6] contain the data)
} else if (len > 0) {
EXT_LOGD(ML_TAG, "Invalid answer length");
EXT_LOGD(ML_TAG, "Invalid answer length");
} else {
EXT_LOGD(ML_TAG, "No response from sensor");
}
#endif
EXT_LOGD(ML_TAG, "No response from sensor");
}
#endif
}
}

Expand Down Expand Up @@ -796,8 +795,12 @@ class ModuleIO : public Module {
uint32_t adc_mv_vinput = analogReadMilliVolts(pinVoltage);
analogSetAttenuation(ADC_11db);
float volts = 0;
if (current_board_id == board_SE16V1) { volts = ((float)adc_mv_vinput) * 2 / 1000; } // /2 resistor divider
else if (current_board_id == board_LightCrafter16) { volts = ((float)adc_mv_vinput) * 11.43 / (1.43 * 1000); } // 1k43/10k resistor divider
if (current_board_id == board_SE16V1) {
volts = ((float)adc_mv_vinput) * 2 / 1000;
} // /2 resistor divider
else if (current_board_id == board_LightCrafter16) {
volts = ((float)adc_mv_vinput) * 11.43 / (1.43 * 1000);
} // 1k43/10k resistor divider
batteryService->updateVoltage(volts);
voltage_readout_current_adc_attenuation = adc_get_adjusted_gain(voltage_readout_current_adc_attenuation, adc_mv_vinput);
}
Comment on lines +798 to 806
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Battery voltage: avoid pushing 0V for unsupported current_board_id.

Right now volts stays 0 unless the board is board_SE16V1 or board_LightCrafter16, but updateVoltage(volts) is still called. Consider either:

  • else return; / skip updateVoltage for unknown boards, or
  • compute a reasonable default divider for other boards (if applicable).
🤖 Prompt for AI Agents
In `@src/MoonBase/Modules/ModuleIO.h` around lines 798 - 806, The code calls
batteryService->updateVoltage(volts) even when current_board_id is neither
board_SE16V1 nor board_LightCrafter16 leaving volts at 0; change the logic in
the block containing current_board_id, volts, and updateVoltage so that for
unknown boards you either early-return/skip calling
batteryService->updateVoltage (preferred) or set a documented default divider
before calling updateVoltage; ensure you also handle
voltage_readout_current_adc_attenuation by only calling
adc_get_adjusted_gain(...) when you actually computed volts for a supported
board (symbols: current_board_id, board_SE16V1, board_LightCrafter16, volts,
batteryService->updateVoltage, voltage_readout_current_adc_attenuation,
adc_get_adjusted_gain).

Expand All @@ -809,8 +812,12 @@ class ModuleIO : public Module {
if ((current_board_id == board_SE16V1) || (current_board_id == board_LightCrafter16)) {
if (adc_mv_cinput > 330) // datasheet quiescent output voltage of 0.5V, which is ~330mV after the 10k/5k1 voltage divider. Ideally, this value should be measured at boot when nothing is displayed on the LEDs
{
if (current_board_id == board_SE16V1) { batteryService->updateCurrent((((float)(adc_mv_cinput)-250) * 50.00) / 1000); } // 40mV / A with a /2 resistor divider, so a 50mA/mV
else if (current_board_id == board_LightCrafter16) { batteryService->updateCurrent((((float)(adc_mv_cinput)-330) * 37.75) / 1000); } // 40mV / A with a 10k/5k1 resistor divider, so a 37.75mA/mV
if (current_board_id == board_SE16V1) {
batteryService->updateCurrent((((float)(adc_mv_cinput)-250) * 50.00) / 1000);
} // 40mV / A with a /2 resistor divider, so a 50mA/mV
else if (current_board_id == board_LightCrafter16) {
batteryService->updateCurrent((((float)(adc_mv_cinput)-330) * 37.75) / 1000);
} // 40mV / A with a 10k/5k1 resistor divider, so a 37.75mA/mV
} else {
batteryService->updateCurrent(0);
}
Expand Down
2 changes: 1 addition & 1 deletion src/MoonBase/NodeManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ class NodeManager : public Module {
template <typename T>
Node* checkAndAlloc(const char* name) const {
if (equalAZaz09(name, T::name())) {
EXT_LOGD(ML_TAG, "Allocate %s", name);
// EXT_LOGD(ML_TAG, "Allocate %s", name);
return allocMBObject<T>();
} else
return nullptr;
Expand Down
4 changes: 2 additions & 2 deletions src/MoonBase/Nodes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ void Node::updateControl(const JsonObject& control) {
// if (oldValue == "") return; // newControl, value already set
if (!control["name"].isNull() && !control["type"].isNull() && !control["p"].isNull()) { // name and type can be null if control is removed in compareRecursive
int pointer = control["p"];
EXT_LOGD(ML_TAG, "%s = %s t:%s p:%p", control["name"].as<const char*>(), control["value"].as<String>().c_str(), control["type"].as<const char*>(), (void*)pointer);
// EXT_LOGD(ML_TAG, "%s = %s t:%s p:%p", control["name"].as<const char*>(), control["value"].as<String>().c_str(), control["type"].as<const char*>(), (void*)pointer);

if (pointer) {
if (control["type"] == "slider" || control["type"] == "select" || control["type"] == "pin" || control["type"] == "number") {
Expand Down Expand Up @@ -542,7 +542,7 @@ void DriverNode::onUpdate(const Char<20>& oldValue, const JsonObject& control) {
break;
}

EXT_LOGI(ML_TAG, "setLightPreset %d (cPL:%d, o:%d,%d,%d,%d)", header->lightPreset, header->channelsPerLight, header->offsetRed, header->offsetGreen, header->offsetBlue, header->offsetWhite);
EXT_LOGD(ML_TAG, "setLightPreset %d (cPL:%d, o:%d,%d,%d,%d)", header->lightPreset, header->channelsPerLight, header->offsetRed, header->offsetGreen, header->offsetBlue, header->offsetWhite);

// FASTLED_ASSERT(true, "oki");

Expand Down
6 changes: 3 additions & 3 deletions src/MoonBase/Utilities.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,22 +64,22 @@ void walkThroughFiles(File folder, std::function<void(File, File)> fun) {
bool copyFile(const char* srcPath, const char* dstPath) {
File src = ESPFS.open(srcPath, "r");
if (!src) {
Serial.printf("Failed to open source file: %s\n", srcPath);
EXT_LOGD(ML_TAG, "Failed to open source file: %s", srcPath);
return false;
}
// Check if the destination folders already exist
String dstDir = String(dstPath).substring(0, String(dstPath).lastIndexOf('/'));
if (!ESPFS.exists(dstDir.c_str())) {
if (!ESPFS.mkdir(dstDir.c_str())) {
Serial.printf("Failed to create destination directory: %s\n", dstDir.c_str());
EXT_LOGD(ML_TAG, "Failed to create destination directory: %s", dstDir.c_str());
src.close();
return false;
}
}

File dst = ESPFS.open(dstPath, "w");
if (!dst) {
Serial.printf("Failed to open destination file: %s\n", dstPath);
EXT_LOGD(ML_TAG, "Failed to open destination file: %s", dstPath);
src.close();
return false;
}
Expand Down
6 changes: 3 additions & 3 deletions src/MoonLight/Layers/VirtualLayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ void VirtualLayer::addIndexP(PhysMap& physMap, nrOfLights_t indexP) {
// EXT_LOGV(ML_TAG, " more %d", mappingTableIndexes.size());
break;
}
// EXT_LOGV(ML_TAG, "\n");
// EXT_LOGV(ML_TAG, "");
}
nrOfLights_t VirtualLayer::XYZ(Coord3D& position) {
// XYZ modifiers (this is not slowing things down as you might have expected ...)
Expand Down Expand Up @@ -448,11 +448,11 @@ void VirtualLayer::onLayoutPost() {
break;
}
// else
// EXT_LOGV(ML_TAG, "%d no mapping\n", x);
// EXT_LOGV(ML_TAG, "%d no mapping", x);
}
}

EXT_LOGD(MB_TAG, "V:%d x %d x %d = v:%d = 1:0:%d + 1:1:%d + mti:%d (1:m:%d)", size.x, size.y, size.z, nrOfLights, nrOfZeroLights, nrOfOneLight, mappingTableIndexesSizeUsed, nrOfMoreLights);
EXT_LOGI(MB_TAG, "V:%d x %d x %d = v:%d = 1:0:%d + 1:1:%d + mti:%d (1:m:%d)", size.x, size.y, size.z, nrOfLights, nrOfZeroLights, nrOfOneLight, mappingTableIndexesSizeUsed, nrOfMoreLights);
}

void VirtualLayer::drawLine(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1, CRGB color, bool soft, uint8_t depth) {
Expand Down
12 changes: 6 additions & 6 deletions src/MoonLight/Modules/ModuleDrivers.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ class ModuleDrivers : public NodeManager {

// log pins
for (int i = 0; i < layerP.nrOfLedPins; i++) {
EXT_LOGD(ML_TAG, "ledPins[%d-%d] = %d (#%d)", i, layerP.nrOfLedPins, layerP.ledPins[i], layerP.ledsPerPin[i]);
if (layerP.ledsPerPin[i] > 0 && layerP.ledsPerPin[i] != UINT16_MAX) EXT_LOGD(ML_TAG, "ledPins[%d-%d] = %d (#%d)", i, layerP.nrOfLedPins, layerP.ledPins[i], layerP.ledsPerPin[i]);
}

layerP.requestMapPhysical = true;
Expand Down Expand Up @@ -156,13 +156,13 @@ class ModuleDrivers : public NodeManager {
#endif

if (node) {
EXT_LOGD(ML_TAG, "%s (p:%p pr:%d)", name, node, isInPSRAM(node));
EXT_LOGI(ML_TAG, "Add %s (p:%p pr:%d)", name, node, isInPSRAM(node));

node->constructor(layerP.layers[0], controls, &layerP.driversMutex); // pass the layer to the node (C++ constructors are not inherited, so declare it as normal functions)
node->moduleControl = _moduleLightsControl; // to access global lights control functions if needed
node->moduleIO = _moduleIO; // to get pin allocations
node->moduleNodes = (Module*)this; // to request UI update
node->setup(); // run the setup of the effect
node->moduleControl = _moduleLightsControl; // to access global lights control functions if needed
node->moduleIO = _moduleIO; // to get pin allocations
node->moduleNodes = (Module*)this; // to request UI update
node->setup(); // run the setup of the effect
node->onSizeChanged(Coord3D());
// layers[0]->nodes.reserve(index+1);

Expand Down
2 changes: 1 addition & 1 deletion src/MoonLight/Modules/ModuleEffects.h
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ class ModuleEffects : public NodeManager {
#endif

if (node) {
EXT_LOGD(ML_TAG, "%s (p:%p pr:%d)", name, node, isInPSRAM(node));
EXT_LOGI(ML_TAG, "Add %s (p:%p pr:%d)", name, node, isInPSRAM(node));

node->constructor(layerP.layers[0], controls, &layerP.effectsMutex); // pass the layer to the node
// node->moduleControl = _moduleLightsControl; // to access global lights control functions if needed
Expand Down
4 changes: 2 additions & 2 deletions src/MoonLight/Modules/ModuleLightsControl.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,9 @@ class ModuleLightsControl : public Module {
void begin() override {
Module::begin();

EXT_LOGI(ML_TAG, "Lights:%d(Header:%d) L-H:%d Node:%d PL:%d(PL-L:%d) VL:%d PM:%d C3D:%d", sizeof(Lights), sizeof(LightsHeader), sizeof(Lights) - sizeof(LightsHeader), sizeof(Node), sizeof(PhysicalLayer), sizeof(PhysicalLayer) - sizeof(Lights), sizeof(VirtualLayer), sizeof(PhysMap), sizeof(Coord3D));
EXT_LOGD(ML_TAG, "Lights:%d(Header:%d) L-H:%d Node:%d PL:%d(PL-L:%d) VL:%d PM:%d C3D:%d", sizeof(Lights), sizeof(LightsHeader), sizeof(Lights) - sizeof(LightsHeader), sizeof(Node), sizeof(PhysicalLayer), sizeof(PhysicalLayer) - sizeof(Lights), sizeof(VirtualLayer), sizeof(PhysMap), sizeof(Coord3D));

EXT_LOGI(ML_TAG, "isInPSRAM: mt:%d mti:%d ch:%d", isInPSRAM(layerP.layers[0]->mappingTable), isInPSRAM(layerP.layers[0]->mappingTableIndexes.data()), isInPSRAM(layerP.lights.channelsE));
EXT_LOGD(ML_TAG, "isInPSRAM: mt:%d mti:%d ch:%d", isInPSRAM(layerP.layers[0]->mappingTable), isInPSRAM(layerP.layers[0]->mappingTableIndexes.data()), isInPSRAM(layerP.lights.channelsE));

#ifdef BOARD_HAS_PSRAM
if (!psramFound) EXT_LOGE(ML_TAG, "Board has PSRAM but not found !!");
Expand Down
6 changes: 3 additions & 3 deletions src/MoonLight/Nodes/Drivers/D_Infrared.h
Original file line number Diff line number Diff line change
Expand Up @@ -284,11 +284,11 @@ class IRDriver : public Node {

void parse_nec_frame(rmt_symbol_word_t* rmt_nec_symbols, size_t symbol_num) {
if (false) {
EXT_LOGD(IR_DRIVER_TAG, "NEC frame start---\r\n");
EXT_LOGD(IR_DRIVER_TAG, "NEC frame start---");
for (size_t i = 0; i < symbol_num; i++) {
EXT_LOGI(IR_DRIVER_TAG, "{%d:%d},{%d:%d}\r\n", rmt_nec_symbols[i].level0, rmt_nec_symbols[i].duration0, rmt_nec_symbols[i].level1, rmt_nec_symbols[i].duration1);
EXT_LOGI(IR_DRIVER_TAG, "{%d:%d},{%d:%d}", rmt_nec_symbols[i].level0, rmt_nec_symbols[i].duration0, rmt_nec_symbols[i].level1, rmt_nec_symbols[i].duration1);
}
EXT_LOGI(IR_DRIVER_TAG, "---NEC frame end: ");
EXT_LOGI(IR_DRIVER_TAG, "---NEC frame end:");
}
// decode RMT symbols

Expand Down
12 changes: 7 additions & 5 deletions src/MoonLight/Nodes/Drivers/parlio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@

#include "soc/soc_caps.h" // for SOC_PARLIO_SUPPORTED

#include "MoonBase/Utilities.h"

#ifdef SOC_PARLIO_SUPPORTED

#include "driver/parlio_tx.h"
Expand All @@ -24,7 +26,7 @@ extern I2SClocklessLedDriver ledsDriver;

//The max_leds_per_output and first_index_per_output are modified in show_parlio and read in transpose_32_slices / create_transposed_led_output_optimized. This is safe given the driver runs on a dedicated core (APP_CPU),
uint16_t max_leds_per_output = 0;
uint32_t first_index_per_output[16];
uint32_t first_index_per_output[SOC_PARLIO_TX_UNIT_MAX_DATA_WIDTH];

// --- Namespace for specialized, high-performance worker functions ---
namespace LedMatrixDetail {
Expand Down Expand Up @@ -275,7 +277,7 @@ uint8_t IRAM_ATTR __attribute__((hot)) show_parlio(uint8_t* parallelPins, uint32
// 💫 this is only the case if all leds_per_output for all outputs is the same (we pad everything smaller than that)
// if (length != outputs * max_leds_per_output) {
// delay(100);
// Serial.printf("Parallel IO isn't set correctly. Check length, outputs, and LEDs per output. (%d != %d x %d)\n", length, outputs, max_leds_per_output);
// EXT_LOGD(ML_TAG, "Parallel IO isn't set correctly. Check length, outputs, and LEDs per output. (%d != %d x %d)", length, outputs, max_leds_per_output);
// return 1;
// }

Expand Down Expand Up @@ -349,15 +351,15 @@ uint8_t IRAM_ATTR __attribute__((hot)) show_parlio(uint8_t* parallelPins, uint32
last_outputs = outputs;
last_leds_per_output = max_leds_per_output;
parlio_setup_done = true;
Serial.printf("Parallel IO configured for %u bit width and clock speed %u KHz and %u outputs.\n", parlio_config.data_width, parlio_config.output_clk_freq_hz / 1000 / 4, outputs);
EXT_LOGD(ML_TAG, "Parallel IO configured for %u bit width and clock speed %u KHz and %u outputs.", parlio_config.data_width, parlio_config.output_clk_freq_hz / 1000 / 4, outputs);
for (uint8_t i = 0; i < SOC_PARLIO_TX_UNIT_MAX_DATA_WIDTH; i++) {
const char* status = "";
if (i >= parlio_config.data_width || (i >= outputs && i < parlio_config.data_width)) {
status = "[ignored]";
} else if (i <= outputs) {
if (parlio_config.data_gpio_nums[i] == -1) status = "[missing]";
}
Serial.printf("Parallel IO Output %u = GPIO %d %s\n", (unsigned int)(i + 1), parlio_config.data_gpio_nums[i], status);
EXT_LOGD(ML_TAG, "Parallel IO Output %u = GPIO %d %s", (unsigned int)(i + 1), parlio_config.data_gpio_nums[i], status);
}
return 0; // let's give it a frame to set up.
}
Expand Down Expand Up @@ -460,7 +462,7 @@ uint8_t IRAM_ATTR __attribute__((hot)) show_parlio(uint8_t* parallelPins, uint32

#ifdef PARLIO_TIMER
if (micros() % 100 < 3) {
Serial.printf("Parallel IO for %u pixels took %lu micros at %u FPS.\n", length, micros() - timer, strip.getFps());
EXT_LOGD(ML_TAG, "Parallel IO for %u pixels took %lu micros at %u FPS.", length, micros() - timer, strip.getFps());
}
#endif

Expand Down
Loading