Close

Code For Pulsing Lights

A project log for TARDIS Themed Board Game Cabinet

Board game cabinet with a Doctor Who TARDIS theme with door sensors, LED strips, roof light and sound

cafelizardocafelizardo 04/04/2022 at 08:200 Comments

I defined a set of classes for controlling the LED lights.  The base class is called Flasher which will cycle an LED on and off at a specified rate.  The Pulser class will slowly brighten and dim an LED using a PWM output pin.  Finally the RGB_Pulser class will work on a set of three PWM pins.

I use these classes to control the various lights as follows:

// Flasher ctor takes led, on perid and off period
Flasher builtin_led(LED_BUILTIN, 10, 1000); // flash power light at a steady interval

// Pulser ctor takes pin, interval, maxValue, initial, increment, offset
Pulser roof_light(ROOF_LIGHT, 5, 63, 20, 10, 0);
//Flasher roof_light(ROOF_LIGHT, 100, 50);

//Pulser cabinet_light(CABINET_LIGHT, 5, 63, 20, 10, 0);
Flasher cabinet_light(CABINET_LIGHT);

// RGB_Pulser ctor takes pins for red green and blue
RGB_Pulser sign_color(SIGN_RED_LED, SIGN_GREEN_LED, SIGN_BLUE_LED);
// Flasher -- based on tutorial from Adafruit
// https://learn.adafruit.com/multi-tasking-the-arduino-part-1


#ifndef _Flasher_h_
#define _Flasher_h_

#define FLASHER_VERSION "$Id: Flasher.h 2 2018-08-29 07:08:22Z cafelizardo $"

enum { DISABLED = -1, OFF=LOW, ON=HIGH } LED_STATE;

class Flasher
{
    protected:
    // configuration variables
    int ledPin;                      // number of the LED pin
    long onTime;                     // milliseconds of on-time
    long offTime;                    // milliseconds of off-time
    bool inverted;                   // invert output

    // state variables
    int ledState;                    // current value: DISABLED, OFF or ON
    unsigned long previousMillis;    // last time LED was last updated

    public:
    // constructor -- creates a Flasher object
    // and initialized the member variables and state
    Flasher(int pin = 0, long on = 0, long off = 0)
    {
        this->ledPin = pin;
        pinMode(ledPin, OUTPUT);

        this->onTime = on;
        this->offTime = off;
        this->inverted = false;
        this->ledState = OFF;
        this->previousMillis = 0;
    } // ctor

    // modifiers
    Flasher& setOnTime(long onTime) {
        this->onTime = onTime;
        return *this;
    }
    Flasher& setOffTime(long offTime) {
        this->offTime = offTime;
        return *this;
    }
    Flasher& setInvert(bool invert) {
        this->inverted = invert;
        return *this;
    }
    virtual Flasher& output(int value)
    {
        if (this->ledState == DISABLED) {
            digitalWrite(this->ledPin, this->inverted ? HIGH : LOW);
        } else {
            digitalWrite(this->ledPin, constrain(this->inverted ? 255 - value : value, 0, 255) );
        }
        return *this;
    }
    virtual Flasher& output()
    {
        //Serial.print("Flasher.output "); Serial.print(this->ledPin, DEC); Serial.println("");
        this->output(this->ledState);
        return *this;
    }
    virtual Flasher& disable() {
        //Serial.print("disabling "); Serial.print(this->ledPin, DEC); Serial.println("");
        this->ledState = DISABLED;
        this->output();
        return *this;
    }
    virtual Flasher& enable() {
        //Serial.print("enabling "); Serial.print(this->ledPin, DEC); Serial.println("");
        this->ledState = OFF;
        this->previousMillis = 0;
        this->output();
        return *this;
    }

    virtual Flasher& update()
    {
        if (this->ledState == DISABLED) return *this;

        //Serial.print("flasher update "); Serial.print(this->ledPin, DEC); Serial.println("");
        // check to see if it's tme to change the state of the LED
        unsigned long currentMillis = millis();

        bool toggle = false;
        if ( (this->ledState == ON) && (currentMillis - this->previousMillis >= this->onTime) )
            toggle = true;
        else if ( (this->ledState == OFF) && (currentMillis - this->previousMillis >= this->offTime) )
            toggle = true;

        if (toggle)
        {
            this->ledState = !this->ledState;
            this->output();
            this->previousMillis = currentMillis;
        }
        return *this;
    } // update

}; // class Flasher

#endif // _flasher_h_
 
// Pulser -- extends Flasher by gradually increasing brightness then back down again
// initial version using cosine function.

#ifndef _Pulser_h_
#define _Pulser_h_

#include "Flasher.h"

#define PULSER_VERSION "$Id: Pulser.h 9 2018-10-01 07:19:06Z cafelizardo $"

class Pulser : public Flasher
{
    long interval;
    long offset;        // offset to be applied after computing value to force clipping either low or high
    long increment;     // increment on step_angle
    long maxValue;      // max brightness
    int step_angle;

    public:
    // constructor -- creates a Pulser object
    Pulser() : Flasher() {}
    Pulser(int pin, long interval = 0, long maxValue = 255, long initial = 0, long increment = 1, long offset = 0)
        : Flasher(pin, interval, offset)
    {
        this->interval = interval;
        this->offset = offset;
        this->increment = max(1, increment);
        this->maxValue = maxValue;
        this->step_angle = initial;
    } // ctor

    // modifiers
    Pulser& setInterval(long interval) {
        this->interval = interval;
        return *this;
    }
    Pulser& setMaxValue(long maxValue) {
        this->maxValue = maxValue;
        return *this;
    }
    Pulser& setInitial(long initial) {
        this->step_angle = initial;
        return *this;
    }
    Pulser& setIncrement(long increment) {
        this->increment = increment;
        return *this;
    }
    Pulser& setOffset(long offset) {
        this->offset = offset;
        return *this;
    }

    virtual Pulser& output(int value)
    {
        //Serial.print("Pulser.output "); Serial.print(this->ledPin, DEC); Serial.print(" "); Serial.print(value, HEX); Serial.println("");
        if (this->ledState == DISABLED) {
            digitalWrite(this->ledPin, this->inverted ? HIGH : LOW);
        } else {
            analogWrite(this->ledPin, constrain(this->inverted ? 255 - value : value, 0, 255) );
        }
        return *this;
    }
    virtual Pulser& output()
    {
        //Serial.print("Pulser.output "); Serial.print(this->ledPin, DEC); Serial.print(" ledState="); Serial.print(this->ledState, DEC); Serial.println("");
        this->output(this->ledState);
        return *this;
    }

    // update -- uses sin function to slowly ramp up and down
    virtual Pulser& update()
    {
        if (this->ledState == DISABLED) return *this;

        //Serial.print("pulser update "); Serial.print(this->ledPin, DEC); Serial.print(" ledState="); Serial.print(this->ledState, DEC); Serial.println("");
        // get current time
        unsigned long currentMillis = millis();

        if ( currentMillis - this->previousMillis >= this->interval )
        {
            //convert 0-360 angle to radian (needed for cos function)
            float rad = DEG_TO_RAD * this->step_angle + this->offTime;

            //calculate cosine of angle as number between 0 and maxvalue
            this->ledState = constrain((-cos(rad) * this->maxValue + this->maxValue) + this->offset, 0, this->maxValue);

            this->step_angle += this->increment;
            while (this->step_angle > 360) step_angle -= 360;
            this->previousMillis = currentMillis;
        }
        this->output();
        return *this;
    } // update

    virtual int step(const int dir = 1)
    {
        this->ledState += dir;
        if (this->ledState > this->maxValue) this->ledState = this->maxValue;
        if (this->ledState < 0) this->ledState = 0;
        this->output();
        return this->ledState;
    } // step

}; // class Pulser

#endif // _Pulser_h
// RGB_Pulser -- combines 3 Pulser's which extends the basic Flasher interface

#ifndef _RGB_Pulser_h_
#define _RGB_Pulser_h_

#include "Flasher.h"
#include "Pulser.h"

#define RGB_PULSER_VERSION "$Id: RGB_Pulser.h 9 2018-10-01 07:19:06Z cafelizardo $"

class RGB_Pulser : public Pulser
{
    Pulser *red_led, *green_led, *blue_led;

    public:
    // ctor
    RGB_Pulser(int red_pin, int green_pin, int blue_pin, int interval = 0, int maxValue = 255)
    {
        this->red_led = new Pulser(red_pin, interval, maxValue);
        this->green_led = new Pulser(green_pin, interval, maxValue);
        this->blue_led = new Pulser(blue_pin, interval, maxValue);
    }

    // access functions for Pulser objects
    Pulser* red() { return this->red_led; }
    Pulser* green() { return this->green_led; }
    Pulser* blue() { return this->blue_led; }

    virtual RGB_Pulser& output(int red, int green, int blue) {
        this->red_led->output(red);
        this->green_led->output(blue);
        this->blue_led->output(green);
    }
    virtual RGB_Pulser& output(long rgb) {
#if 0
        Serial.print(F("RGB_Pulser.output "));
        Serial.print(rgb, HEX); Serial.print(F(" "));
        Serial.print((rgb & 0xff0000) >> 16, HEX); Serial.print(F(" "));
        Serial.print((rgb & 0x00ff00) >> 8, 16); Serial.print(F(" "));
        Serial.print(rgb & 0x0000ff, 16); 
        Serial.println();
#endif
        this->red_led->output((rgb & 0xff0000) >> 16);
        this->green_led->output((rgb & 0x00ff00) >> 8);
        this->blue_led->output(rgb & 0x0000ff);
        return *this;
    }
    virtual RGB_Pulser& disable() {
        Serial.println("RGB_light.disable ");
        this->red_led->disable();   this->red_led->output(255);
        this->green_led->disable(); this->green_led->output(255);
        this->blue_led->disable();  this->blue_led->output(255);
        return *this;
    }
    virtual RGB_Pulser& enable() {
        Serial.println("RGB_light.enable ");
        this->red_led->enable();
        this->green_led->enable();
        this->blue_led->enable();
        return *this;
    }
    virtual RGB_Pulser& update()
    {
        //Serial.println("RGB_light.update ");
        this->red_led->update();
        this->green_led->update();
        this->blue_led->update();
        return *this;
    }
}; // class RGB_Pulser

#endif // _RGB_Pulser_h

Discussions