Using projectMM as a Library¶
projectMM can be consumed as a PlatformIO library. Your project gets the full module runtime (Module, StatefulModule, ModuleManager, Scheduler, PAL, HTTP/WS server) plus all infrastructure modules (WiFi, network, system status, device discovery, tasks, file manager) without copying any source. LED-domain modules (effects, layers, drivers) are excluded from the library build by default — your project provides only what it needs.
Add to your project¶
In your platformio.ini:
lib_deps =
https://github.com/ewowi/projectMM
bblanchon/ArduinoJson @ ^7
ESP32Async/ESPAsyncWebServer#v3.10.3
PlatformIO fetches the repo, compiles src/ (excluding main.cpp and modules/ModuleRegistrations.cpp), and links it into your firmware. Add these flags to match projectMM's build requirements:
lib_compat_mode = strict
build_flags =
-std=c++17
-DBUILD_TARGET=\"$PIOENV\"
lib_compat_mode = strict avoids silent dependency resolution surprises. -DBUILD_TARGET is required by Pal.h's platform_version() string.
Provide your own entry point¶
The library omits src/main.cpp. Your project must provide setup() and loop(). The minimal form using pal::embeddedSetup():
#include "core/AppSetup.h"
#include "core/Scheduler.h"
#include "core/ModuleManager.h"
#include "core/HttpServer.h"
#include "core/WsServer.h"
static Scheduler scheduler;
static ModuleManager mm(scheduler);
static HttpServer server(80);
static WsServer ws;
void setup() {
Serial.begin(115200);
pal::embeddedSetup(mm, scheduler, server, ws, nullptr, nullptr);
}
void loop() { pal::suspend_forever(); }
pal::embeddedSetup() wires HTTP routes, starts the WebSocket server, and creates the FreeRTOS tasks. The optional last argument is a firstBootFn(ModuleManager&) callback invoked once when no saved state exists, which is the right place to register default modules.
Register your own modules¶
src/modules/ModuleRegistrations.cpp is excluded from the library build. Your project supplies its own registrations:
// src/MyRegistrations.cpp (in your project, not the library)
#include "core/TypeRegistry.h"
#include "MyEffect.h"
#include "MyDriver.h"
REGISTER_MODULE(MyEffect)
REGISTER_MODULE(MyDriver)
REGISTER_MODULE must be in a translation unit compiled into the executable, not a static library. Infrastructure modules (WiFi, network, system status, device discovery, tasks, file manager) are registered automatically via the library's src/core/CoreRegistrations.cpp — you do not need to list them.
ProducerModule and ConsumerModule¶
For domain-specific pixel pipelines, extend ProducerModule (writes a pixel buffer) and ConsumerModule (reads one):
// src/core/ProducerModule.h
class ProducerModule : public StatefulModule {
public:
// Call once in setup() to declare the pixel buffer.
void declareBuffer(void* buf, size_t len, size_t elemSize);
void* bufferPtr() const; // pointer to the first element
size_t bufferLen() const; // number of elements
size_t bufferElemSize() const; // bytes per element
size_t bufferBytes() const; // total bytes (len * elemSize)
};
// src/core/ConsumerModule.h
class ConsumerModule : public StatefulModule {
public:
// Wire at runtime: setInput("producer", &myEffect)
void setInput(const char* key, Module* m) override;
protected:
ProducerModule* producer_ = nullptr; // available in loop()
};
Typical pattern (FastLED example):
class WaveRainbow2DEffect : public ProducerModule {
public:
void setup() override {
declareBuffer(leds, NUM_LEDS, sizeof(CRGB));
addControl(speed_, "speed", "slider", 1.0f, 10.0f);
}
void loop() override { /* write to leds[] */ }
// ...
private:
CRGB leds[NUM_LEDS];
float speed_ = 3.0f;
};
class MyDriver : public ConsumerModule {
public:
void loop() override {
if (!producer_) return;
// producer_->bufferPtr() gives the CRGB array
FastLED.show();
}
};
The buffer is owned by the ProducerModule subclass. The ConsumerModule reads it via producer_->bufferPtr() or, when both share a global array, directly.
Note: EffectsLayer and DriverLayer (the built-in LED pipeline) do not extend these base classes. They use a double-buffered atomic Channel* designed for the dual-core handoff. ProducerModule / ConsumerModule are for new single-buffer domain consumers.
What the library includes¶
| Path | Contents |
|---|---|
src/core/ |
Module, StatefulModule, ModuleManager, Scheduler, TypeRegistry, HTTP/WS servers, KvStore, Logger, ProducerModule, ConsumerModule |
src/core/CoreRegistrations.cpp |
Auto-registered infrastructure: Network, WifiAp, WifiSta, SystemStatus, DeviceDiscovery, Tasks, FileManager |
src/pal/ |
Pal.h — platform abstraction (timing, GPIO, heap, WiFi, UDP, OTA, filesystem) |
src/modules/ |
Built-in LED modules (compiled but NOT auto-registered; excluded from library.json srcFilter) |
src/frontend/ |
frontend_bundle.h — inlined HTML/CSS/JS served over HTTP |
src/main.cpp and src/modules/ModuleRegistrations.cpp are excluded from the library build via library.json's srcFilter.
Pin a specific release¶
lib_deps =
https://github.com/ewowi/projectMM#v1.4.0
See GitHub releases for available tags.