Skip to content

Latest commit

 

History

History
221 lines (175 loc) · 11.4 KB

File metadata and controls

221 lines (175 loc) · 11.4 KB

AGENTS.md — WLED-MM AI Coding Agent & AI Code Review Reference

WLED-MM is C++ firmware for ESP32/ESP8266 microcontrollers controlling addressable LEDs, with a web UI (HTML/JS/CSS). Built with PlatformIO (Arduino framework) and Node.js tooling.

Refer to .github/copilot-instructions.md, .github/agent-build.instructions.md, docs/cpp.instructions.md, docs/esp-idf.instructions.md, docs/web.instructions.md, docs/cicd.instructions.md fo additional conventions and reference material.

Always reference these instructions - including coding guidelines in docs/ - first and fallback to search or bash commands only when you encounter unexpected information that does not match the info here.

Build Commands

Command Purpose Timeout
npm ci Install Node.js deps (required first) 30s
npm run build Build web UI into wled00/html_*.h / wled00/js_*.h 30s
npm test Run test suite (Node.js built-in node --test) 2 min
npm run dev Watch mode — auto-rebuilds web UI on changes continuous
pio run -e esp32_4MB_V4_M Build firmware (ESP32, most common target) 30 min
pio run -e esp32dev_compat Build firmware (ESP32 legacy target) 30 min
pio run -e esp8266_4MB_S Build firmware (ESP8266, deprecated) 30 min

Always run npm ci && npm run build before pio run. The web UI build generates required C headers for firmware compilation.

Running a Single Test

Tests use Node.js built-in test runner (node:test). The single test file is tools/cdata-test.js. Run it with:

npm test                   # runs all tests via `node --test`
node --test tools/cdata-test.js  # run just that file directly

There are no C++ unit tests. Firmware is validated by successful compilation across target environments. Always build after code changes: pio run -e esp32_4MB_V4_M.

Common Firmware Environments

esp32_4MB_V4_M, esp32_16MB_V4_S_HUB75, esp32S3_8MB_PSRAM_M_qspi, esp32S3_16MB_PSRAM_M_HUB75, esp32_16MB_V4_M_eth (ethernet support), esp32_16MB_V4_M_debug (debug), esp8266_4MB_S (deprecated), esp32dev_compat (V3 legacy framework)

Recovery / Troubleshooting

npm run build -- -f              # force web UI rebuild
rm -f wled00/html_*.h wled00/js_*.h && npm run build  # clean + rebuild UI
pio run --target clean           # clean PlatformIO build artifacts
rm -rf node_modules && npm ci    # reinstall Node.js deps

Project Structure

wled00/              # Main firmware source (C++)
  data/              # Web UI source (HTML/JS/CSS) — tabs for indentation
  html_*.h, js_*.h   # Auto-generated (NEVER edit or commit)
  src/               # Sub-modules: fonts, bundled dependencies (ArduinoJSON)
usermods/            # Community addons (.h files, included via usermods_list.cpp)
platformio.ini       # Build configuration and environments
pio-scripts/         # PlatformIO build scripts (Python)
tools/               # Node.js build tools (cdata.js) and tests
docs/                # Coding convention docs
.github/workflows/   # CI/CD (GitHub Actions)

C++ Code Style (wled00/, usermods/)

Formatting

  • 2-space indentation (no tabs in C++ files)
  • K&R brace style preferred (opening brace on same line)
  • Single-statement if bodies may omit braces: if (a == b) doStuff(a);
  • Space after keywords (if (...), for (...)), no space before function parens (doStuff(a))
  • No enforced line-length limit

Comments

  • // for inline (always space after), /* */ for block comments
  • Important: AI-generated source code blocks must be mark with // AI: below section was generated by an AI / // AI: end

Naming Conventions

Kind Convention Examples
Functions, variables camelCase setRandomColor(), effectCurrent
Classes, structs PascalCase BusConfig, UsermodTemperature
Macros, constants UPPER_CASE WLED_MAX_USERMODS, FX_MODE_STATIC
Private members _camelCase _type, _bri, _len
Enum values PascalCase PinOwner::BusDigital

Includes

  • Include "wled.h" as the primary project header
  • Project headers first, then platform/Arduino, then third-party
  • Platform-conditional includes wrapped in #ifdef ARDUINO_ARCH_ESP32 / #ifdef ESP8266
  • WLED-MM fork detection: #ifdef _MoonModules_WLED_ (defined in wled.h)

Types and Const

  • Prefer const & for read-only function parameters
  • Mark getter/query methods const; use static for methods not accessing instance state
  • Prefer constexpr over #define for compile-time constants when possible
  • Use static_assert over #if ... #error
  • Use uint_fast16_t / uint_fast8_t in hot-path code

Error Handling

  • No C++ exceptions — some builds disable them
  • Use return codes (false, -1) and global flags (errorFlag = ERR_LOW_MEM)
  • Use early returns as guard clauses: if (!enabled || (strip.isUpdating() && (millis() - last_time < MAX_USERMOD_DELAY))) return;
  • Debug output: DEBUG_PRINTF() / DEBUG_PRINTLN() (compiled out unless -D WLED_DEBUG)

Strings and Memory

  • Use F("string") for string constants (saves RAM on ESP8266)
  • Use PSTR() with DEBUG_PRINTF_P() for format strings
  • Avoid String in hot paths; acceptable in config/setup code
  • Use d_malloc() (DRAM-preferred) / p_malloc() (PSRAM-preferred) for allocation
  • No VLAs — use fixed arrays or heap allocation
  • Call reserve() on strings/vectors to pre-allocate and avoid fragmentation

ESP32 PSRAM guidelines

  • Check availability: Test chip availability with psramFound() && ESP.getPsramSize() > 0 before assuming PSRAM is present. Never rely on BOARD_HAS_PSRAMonly.
  • DMA compatibility: on ESP32 (classic), PSRAM buffers are not DMA-capable. On ESP32-S3 with octal PSRAM (CONFIG_SPIRAM_MODE_OCT), PSRAM buffers can be used with DMA when CONFIG_SOC_PSRAM_DMA_CAPABLE is defined.
  • Fragmentation: PSRAM allocations fragment less than DRAM because the region is larger. But avoid mixing small and large allocations in PSRAM — small allocations waste the MMU page granularity.
  • Performance: Prefer DRAM (or IRAM) for hot-path data that is frequently used. Prefer PSRAM for capacity-oriented buffers where slightly slower access times can be tolerated.

Background Info:

  • PSRAM access is up to 18× slower than DRAM on ESP32 (dual-SPI bus), 3–10× slower than DRAM on ESP32-S3/-S2 with quad-SPI bus. On ESP32-S3 with octal PSRAM (CONFIG_SPIRAM_MODE_OCT), the penalty is smaller (~2×) because the 8-line DTR bus can transfer 8 bits in parallel. On ESP32-P4 with hex PSRAM (CONFIG_SPIRAM_MODE_HEX), the 16-line bus runs at 200 MHz which brings it on-par with DRAM.
  • Consider that ESP32 often crashes when the largest DRAM chunk gets below 10 KB.

Preprocessor / Feature Flags

  • Feature toggling: WLED_DISABLE_* and WLED_ENABLE_* flags (exact names matter!)
  • WLED_DISABLE_*: 2D, ADALIGHT, ALEXA, MQTT, OTA, INFRARED, WEBSOCKETS, etc.
  • WLED_ENABLE_*: DMX, GIF, HUB75MATRIX, JSONLIVE, WEBSOCKETS, etc.
  • Platform: ARDUINO_ARCH_ESP32, ESP8266, CONFIG_IDF_TARGET_ESP32S3

Math Functions

  • Use sin8_t(), cos8_t() — NOT sin8(), cos8() (removed, won't compile)
  • Use sin_approx() / cos_approx() instead of sinf() / cosf()
  • Replace inoise8 / inoise16 with perlin8 / perlin16

Hot-Path Code (Pixel Pipeline)

  • Use function attributes: IRAM_ATTR, WLED_O2_ATTR, __attribute__((hot))
  • Cache class members to locals before loops
  • Pre-compute invariants outside loops; use reciprocals to avoid division
  • Unsigned range checks: if ((uint_fast16_t)(pix - start) < len)

ESP32 Tasks

  • delay(1) in custom FreeRTOS tasks (NOT yield()) — feeds IDLE watchdog
  • Do not use delay() in effects (FX.cpp) or hot pixel path

ESP32 Task Synchronization

  • Use FreeRTOS mutexes, semaphores or queues when true concurrent access from multiple FreeRTOS tasks is possible, and race-conditions can lead to unexpected behaviour.
  • Avoid portENTER_CRITICAL() / portEXIT_CRITICAL(), as these functions stall the complete system and may cause LEDs flickering. Prefer FreeRTOS mutexes, semaphores or queues.
  • Important: Not every shared resource needs a mutex. Some synchronization is guaranteed by the overall control flow, for example when function calls are sequenced within the same loop iteration.
  • Consider RAII as an alternative to mutexes or semaphores.

Web UI Code Style (wled00/data/)

  • Tab indentation for HTML, JS, and CSS
  • camelCase for JS functions/variables
  • Reuse helpers from common.js — do not duplicate utilities
  • After editing, run npm run build to regenerate headers
  • Never edit wled00/html_*.h or wled00/js_*.h directly

Usermod Pattern

Usermods live in usermods/<name>/ with a .h and readme.md.

#pragma once
#include "wled.h"

class MyUsermod : public Usermod {
  private:
    #if !defined(_MoonModules_WLED_)
      // WLEDMM: don't overload global attributes
      bool enabled = false;
      static const char _name[];
    #endif
  public:
    #if defined(_MoonModules_WLED_)
      MyUsermod(const char *name, bool enabled):Usermod(name, enabled) {} // WLEDMM: passthrough of constructor - Usermod is an abstract class
    #endif

    void setup() override { /* ... */ }
    void loop() override { /* ... */ }
    void addToConfig(JsonObject& root) override { /* ... */ }
    bool readFromConfig(JsonObject& root) override { /* ... */ }
    uint16_t getId() override { return USERMOD_ID_MYMOD; }
};
#if !defined(_MoonModules_WLED_)
  const char MyUsermod::_name[] PROGMEM = "MyUsermod"; // WLEDMM: don't double initialize _name[] and _enabled[] 
  // WLEDMM: for compatibility with upstream WLED
  static MyUsermod myUsermod;
  REGISTER_USERMOD(myUsermod);
#endif
  • Add usermod IDs to wled00/const.h, integrate new usermods via wled00/usermods_list.cpp
  • Base new usermods on usermods/EXAMPLE_v2/ (never edit the example directly)
  • Store repeated strings as static const char[] PROGMEM

CI/CD

CI runs on every push/PR via GitHub Actions (.github/workflows/wled-ci.yml):

  1. npm test (web UI build validation)
  2. Firmware compilation for all default environments (~22 targets)
  3. Post-link validation of usermod linkage (validate_modules.py)

No automated linting is configured. Match existing code style in files you edit.

General Rules

  • Repository language is English.
  • The docs/ folder is for developer/contributor information (coding conventions, architecture, etc.). User documentation is maintained in the MoonModules/WLED-Docs and wled/WLED-Docs repositories.
  • Never edit or commit auto-generated wled00/html_*.h / wled00/js_*.h.
  • When updating an existing PR, retain the original description. Only modify it to ensure technical accuracy. Add change logs after the existing description.
  • No force-push on open PRs!
  • Important: Changes to platformio.ini require maintainer approval!
  • PRs should respect .gitignore and not upload files like platformio_override.ini. PR authors may add buildenv examples for custom boards into platformio_override.ini.sample.
  • Remove dead/unused code — justify or delete it.
  • Verify feature-flag spelling exactly (misspellings are silently ignored by preprocessor).
  • Provide references when making analyses or recommendations. Support factual claims with verifiable citations, references or concrete evidence; never fabricate citations.