blink-led
The periodic output shape. One GPIO, toggled on a timer, nothing published. lib/thesada-mod-example-blink-led/ on the firmware dev branch; ships with the next release.
What it demonstrates
| Thing | Where in the code |
|---|---|
Config in begin(), nothing in the constructor |
begin() reads pin and interval_ms and sets the pin up as an output |
A non-blocking loop() |
compare millis() against the interval, return early, toggle when due. No delay(), so every other module keeps running between toggles |
| Output only | no MQTT publish, no bus event, no shell command; the pin level is the whole result |
module.status |
one line: pin=21 state=on interval_ms=500 |
| Registration | MODULE_REGISTER(ExampleBlinkLed, PRIORITY_OUTPUT), guarded by ENABLE_EXAMPLE_BLINK_LED |
A missing key falls back to the default in begin(): pin 21, 500 ms.
Config
"example_blink_led": {
"enabled": true,
"pin": 21,
"interval_ms": 500
}
enabled is required: optional modules default off, and without "enabled": true the registry logs registry.module_skip ... reason=disabled instead of starting the module.
Turn it on
Uncomment ENABLE_EXAMPLE_BLINK_LED in src/thesada_config.h, build and flash as for any module. Boot log:
[INF][Registry] registry.module_init priority=60 name=ExampleBlinkLed
[INF][ExBlink] example_blink.ready pin=21 interval_ms=500
Expected output
The pin flips every interval_ms, so with the defaults an LED on pin 21 blinks at 1 Hz. Toggles are not logged. module.status shows the current level, one line among the other modules:
module.status
ExampleBlinkLed pin=21 state=on interval_ms=500
The code
src/ExampleBlinkLed.h:
// thesada-fw - ExampleBlinkLed.h
// Starter module: blink an LED on a configurable timer. The periodic output
// shape - config in begin(), non-blocking timer in loop(), status() output.
// SPDX-License-Identifier: GPL-3.0-only
#pragma once
#include <Arduino.h>
#include <Module.h>
class ExampleBlinkLed : public Module {
public:
void begin() override;
void loop() override;
const char* name() override { return "ExampleBlinkLed"; }
const char* configKey() override { return "example_blink_led"; }
void status(ShellOutput out) override;
private:
uint32_t _intervalMs = 500;
uint32_t _last = 0;
int _pin = 21;
bool _on = false;
};
src/ExampleBlinkLed.cpp:
// thesada-fw - ExampleBlinkLed.cpp
// SPDX-License-Identifier: GPL-3.0-only
#include <thesada_config.h>
#include "ExampleBlinkLed.h"
#include <Config.h>
#include <Log.h>
#include <ModuleRegistry.h>
#include <ArduinoJson.h>
#include <stdio.h>
#ifdef ENABLE_EXAMPLE_BLINK_LED
static const char* TAG = "ExBlink";
// Read pin and interval from config, initialize the GPIO.
// in: config block example_blink_led (pin, interval_ms).
// out: GPIO configured as OUTPUT.
void ExampleBlinkLed::begin() {
JsonObject cfg = Config::get();
_intervalMs = cfg["example_blink_led"]["interval_ms"] | 500;
_pin = cfg["example_blink_led"]["pin"] | 21;
pinMode(_pin, OUTPUT);
digitalWrite(_pin, _on ? HIGH : LOW);
Log::kvf(TAG, "example_blink.ready pin=%d interval_ms=%u", _pin, (unsigned)_intervalMs);
}
// Non-blocking toggle driven by millis().
// in: millis(). out: pin level toggled every interval_ms.
void ExampleBlinkLed::loop() {
if (millis() - _last < _intervalMs) {
return;
}
_last = millis();
_on = !_on;
digitalWrite(_pin, _on ? HIGH : LOW);
}
// One line for `module.status`.
// in: out sink. out: "pin=<n> state=on|off interval_ms=<n>".
void ExampleBlinkLed::status(ShellOutput out) {
char line[64];
snprintf(line, sizeof(line), "pin=%d state=%s interval_ms=%u", _pin, _on ? "on" : "off", (unsigned)_intervalMs);
out(line);
}
MODULE_REGISTER(ExampleBlinkLed, PRIORITY_OUTPUT)
#endif // ENABLE_EXAMPLE_BLINK_LED