Divide et impera — Divide and rule
Ancient Wisdom

Software quickly get complex. You have to structure your software, otherwise you will lose track of your project and create more problems than solving them. One of the simplest ways to structure software is by divide it into logical components. You encapsulate the complexity of a certain part of your project and provide few well documented interface methods.

The LEDController class is an example for this approach. The sole responsibility of this class is to control the dual color LED to indicate the status of the board.

The Interface

The class starts with two constants which define the pins for the LEDs. Then there ate two enums: The first declares some LED colors, the second one different “display” states.

/// This class controls the Signal LED
///
class LEDController
{
private:
    /// The pin where the red part of the LED is connected.
    ///
    const int RED_STATUS_PIN = 6;
    
    /// The pin where the green part of the LED is connected.
    ///
    const int GREEN_STATUS_PIN = 7;
    
public:
    /// The color for the LED
    ///
    enum Color : uint8_t {
        Red, ///< Red
        Green, ///< Green
        Orange ///< Orange (both)
    };
    
    /// The states for the LED
    ///
    enum State : uint8_t {
        Off, ///< Off
        On, ///< On
        BlinkSlow, ///< Blink with 500ms
        BlinkFast, ///< Blink with 250ms
        FlashVerySlow, ///< Short flash to indicate the device is running.
    };

There is a setup() method, which has to be called from the setup() method of your sketch. The loop() method has to be called from the loop() method of the sketch.

With the setState() method, you can set a color and state for the LED.

public:
    /// Call this method in the setup() method.
    ///
    void setup();

    /// Call this method in each loop.
    ///
    /// @param The current time to the loop.
    ///
    void loop(unsigned long currentTime);

    /// Set the LED state.
    ///
    /// @param color The color for the LED
    /// @param state The state for the LED
    ///
    void setState(Color color, State state);

The Implementation

The setup just sets the chosen pins to output and puts these outputs into the low state.

void LEDController::setup()
{
    // Set the status LED outputs
    pinMode(RED_STATUS_PIN, OUTPUT);
    pinMode(GREEN_STATUS_PIN, OUTPUT);
    digitalWrite(RED_STATUS_PIN, LOW);
    digitalWrite(GREEN_STATUS_PIN, LOW);
}

First thing in the loop is to check the timer. If the check method returns true, the onTimer method is called. Depending on the state of the controller, this will blink the LED.

The second part handles the case for the orange color of the LED. This color is actually generated by rapidly switch from red to green color. If this switch is fast enough, the color of the LED seems to be orange.

void LEDController::loop(unsigned long currentTime)
{
    if (_blinkTimer.check(currentTime)) {
        onTimer();
    }
    if (_color == Orange && _enabled) {
        if ((currentTime & 0x07) < 4) {
            digitalWrite(RED_STATUS_PIN, HIGH);
            digitalWrite(GREEN_STATUS_PIN, LOW);
        } else {
            digitalWrite(RED_STATUS_PIN, LOW);
            digitalWrite(GREEN_STATUS_PIN, HIGH);
        }
    }
}

The state of the LED is changed with the setState method. This method compares the current state with the new state, if there is a change, the timer and state of the LED is initialized for this state.

void LEDController::setState(Color color, State state)
{
    if (_color != color || _state != state) {
        _color = color;
        _state = state;
        switch(state) {
        case Off:
            disable();
            _blinkTimer.stop();
            break;
        case On:
            enable();
            _blinkTimer.stop();
            break;
        case BlinkSlow:
            enable();
            _blinkTimer.start(500, millis());               
            break;
        case BlinkFast:
            enable();
            _blinkTimer.start(250, millis());               
            break;
        case FlashVerySlow:
            disable();
            _blinkTimer.start(10000, millis());
        }
    }       
}

If the timer is enabled, the method onTimer() is called from the loop() method. It will enable or disable the LED in the chosen interval.

void LEDController::onTimer()
{
    if (_state != FlashVerySlow) {
        if (_enabled) {
            disable();
        } else {
            enable();
        }
    } else {
        if (_enabled) {
            disable();
            _blinkTimer.start(10000, millis());
        } else {
            enable();
            _blinkTimer.start(25, millis());
        }
    }
}

The enable() method just enables the LED in the chosen color. Nothing is done for the orange color, because this color is handled in the loop() method.

void LEDController::enable()
{
    _enabled = true;
    switch (_color) {
    case Red:
        digitalWrite(RED_STATUS_PIN, HIGH);
        digitalWrite(GREEN_STATUS_PIN, LOW);
        break;
    case Green:
        digitalWrite(RED_STATUS_PIN, LOW);
        digitalWrite(GREEN_STATUS_PIN, HIGH);
        break;
    case Orange:
        // This is controlled in the Loop
        break;
    default:
        break;
    }
}

The disable() method disables the LED.

void LEDController::disable()
{
    _enabled = false;
    digitalWrite(RED_STATUS_PIN, LOW);
    digitalWrite(GREEN_STATUS_PIN, LOW);
}

Conclusion

The LEDController class wraps the complexity of controlling the dual color LED, and hides all the details from the other parts of the software. Now you just have to choose a color and state and everything else is automatically done by this controller.

Continue here: Accessing the Motion Sensor