Skip to content

File Dimmer.cpp

File List > Dimmer > Dimmer.cpp

Go to the documentation of this file

#ifdef ARDUINO_ARCH_STM32

#include "Dimmer.h"
#include <STM32Board.h>

OpenSkyhawk::Dimmer::Dimmer(uint16_t controlId, PinRef pin, ScaleFn scale)
    : AnalogOutput(controlId), _pin(pin), _scale(scale) {}

void OpenSkyhawk::Dimmer::configure() {
    // digitalPinHasPWM() is the core's own pin-map lookup (pins_arduino.h → PinMap_TIM), so a
    // GPIO without a timer channel is caught here instead of being discovered as a zone that
    // only switches fully on and off.
    _enabled = _pin.isGpio() && digitalPinHasPWM(_pin.gpioPin());
    if (!_enabled) {
        STM32Board::log("[Dimmer] pin is not a timer-capable GPIO — output disabled");
        return;                        // never drive a pin that cannot dim
    }
    _pin.configureAsOutput();
    _pin.writeAnalog(0);               // zone dark until the first CTRL_BCAST
    _lastDuty = 0;
    _hasState = true;
}

void OpenSkyhawk::Dimmer::apply(uint16_t value) {
    if (!_enabled) return;
    const uint16_t v    = _scale ? _scale(value) : value;
    const uint8_t  duty = (uint8_t)(v >> 8);      // what the GPIO path actually outputs
    // analogWrite() reconfigures the timer channel on every call, so skip the ones that would
    // land on the duty already being driven.
    if (_hasState && duty == _lastDuty) return;
    _pin.writeAnalog(v);
    _lastDuty = duty;
    _hasState = true;
#ifdef DIMMER_TEST
    _writeCount++;
#endif
    if (STM32Board::isDebug()) {
        auto& d = STM32Board::diagSerial();
        d.print(F("[DIM] 0x")); d.print(value, HEX);
        d.print(F(" duty=")); d.println(duty);
    }
}

#endif // ARDUINO_ARCH_STM32