RGB.onChange() not working for Electron

Hello,

So I recently ported my code from a Boron device to an electron device. Everything ported nicely and ran well, except my functionality to mirror the on board RGB LED to an external LED.

Now, I know that RGB.mirrorTo() does not work on 3rd gen devices (as of 0.9.0), so I used the RGB.onChange() function as a work around on my Boron. This is all good, but it seems that the RGB.onChange() function crashes my electron device (i.e. blinking red SOS pattern right after flashing), so I reverted back to the RGB.mirrorTo() for my electron. My code is below if anyone is curious but I think this might be easily reproducible. I also scanned the forum and some people seem to be using it fine, or at least there are no reported crashes. Maybe I’m missing something really simple, but I did follow the example on the device API docs pretty closely.

Jokes:I didn’t know the RGB.onChange() and RGB.mirrorTo() function were mutually exclusive to each other!

Environment

  • Boron - DeviceOS 0.9.0
  • Electron - Device 1.0.1

Summary

  • On my Boron I have to use RGB.onChange()
  • On my Electron I have to use RGB.mirrorTo()

Relevant Docs/URLs

Main.ino

#include "LED.h"

// pin_t gRedPin = D8;                             // Un-comment for Boron
// pin_t gGreenPin = D6;
// pin_t gBluePin = D5;
// pin_t gRedPin = B2;                             // Un-comment for Electron
// pin_t gGreenPin = B1;
// pin_t gBluePin = B0;
LED* led

void setup() {
     led = new LED(gRedPin, gGreenPin, gBluePin);
     .....
}

void loop() {
     .....
}

LED.h

#include "application.h"

class LED {
    public:
        LED(pin_t redPin, pin_t greenPin, pin_t bluePin);
        void blinkOrange();
        void blinkRed();
        void blinkGreen();
        void solidGreen();
        void idleColor();
    private:
        void ledHandler(uint8_t r, uint8_t g, uint8_t b);
        LEDStatus* mBlinkOrange;
        LEDStatus* mBlinkRed;
        LEDStatus* mBlinkGreen;
        LEDStatus* mSolidGreen;
        pin_t mRedPin;
        pin_t mGreenPin;
        pin_t mBluePin;
};

LED.cpp

#include "LED.h"

LED::LED(pin_t redPin, pin_t greenPin, pin_t bluePin) {
    this->mBlinkOrange = new LEDStatus(RGB_COLOR_ORANGE, LED_PATTERN_BLINK, LED_PRIORITY_IMPORTANT);
    this->mBlinkRed = new LEDStatus(RGB_COLOR_RED, LED_PATTERN_BLINK, LED_PRIORITY_IMPORTANT);
    this->mBlinkGreen = new LEDStatus(RGB_COLOR_GREEN, LED_PATTERN_BLINK, LED_PRIORITY_IMPORTANT);
    this->mSolidGreen = new LEDStatus(RGB_COLOR_GREEN, LED_PATTERN_SOLID, LED_PRIORITY_IMPORTANT);
    this->mRedPin = redPin;
    this->mGreenPin = greenPin;
    this->mBluePin = bluePin;
    pinMode(mRedPin, OUTPUT);
    pinMode(mGreenPin, OUTPUT);
    pinMode(mBluePin, OUTPUT);
    // RGB.onChange(&LED::ledHandler, this);         // Un-comment for Boron
    // RGB.mirrorTo(redPin, greenPin, bluePin);        // Un-comment for electron
}

void LED::blinkOrange() {
    mBlinkOrange->setActive(true);
    mBlinkRed->setActive(false);
    mBlinkGreen->setActive(false);
    mSolidGreen->setActive(false);
}

void LED::blinkRed() {
    if (mBlinkOrange->isActive()) mBlinkOrange->setActive(false);
    if (!mBlinkRed->isActive()) mBlinkRed->setActive(true);
    if (mBlinkGreen->isActive()) mBlinkGreen->setActive(false);
    if (mSolidGreen->isActive())  mSolidGreen->setActive(false);
}
void LED::blinkGreen() {
    if (mBlinkOrange->isActive()) mBlinkOrange->setActive(false);
    if (mBlinkRed->isActive()) mBlinkRed->setActive(false);
    if (!mBlinkGreen->isActive()) mBlinkGreen->setActive(true);
    if (mSolidGreen->isActive())  mSolidGreen->setActive(false);
}

void LED::solidGreen() {
    if (mBlinkOrange->isActive()) mBlinkOrange->setActive(false);
    if (mBlinkRed->isActive()) mBlinkRed->setActive(false);
    if (mBlinkGreen->isActive()) mBlinkGreen->setActive(false);
    if (!mSolidGreen->isActive())  mSolidGreen->setActive(true);
}

void LED::idleColor() {
    if (mBlinkOrange->isActive()) mBlinkOrange->setActive(false);
    if (mBlinkRed->isActive()) mBlinkRed->setActive(false);
    if (mBlinkGreen->isActive()) mBlinkGreen->setActive(false);
    if (mSolidGreen->isActive())  mSolidGreen->setActive(false);
}

void LED::ledHandler(uint8_t r, uint8_t g, uint8_t b) {
    analogWrite(mRedPin, r);
    analogWrite(mGreenPin, g);
    analogWrite(mBluePin, b);
}

With SOS panic paterns it's always important to also mention how many slow blinks you see between two SOS patterns.

The RGB.onChange() method works on all platforms but you may encounter a problem with non-deterministic order of execution for object constructors distributed over multiple modules.
Your LED::LED() constructor should not contain pin manipulation (e.g. pinMode()) or other functions that rely on the finished construction of any other object (e.g. RGB.xxx() calls).

The common practice is to only initialise fields owned by the class and place other (external) initialisation tasks in a someClass::begin() method.

Having said that, I'm surprised you run into that situation when using an object pointer and the new operator in setup() (by which time all other constructors are expected to have finished) but for clean design the "begin()" pattern should be applied across the board.

With SOS panic paterns it’s always important to also mention how many slow blinks you see between two SOS patterns.

@ScruffR I'll keep a look out for the pattern the next time I flash the Boron unit.

Your LED::LED() constructor should not contain pin manipulation (e.g. pinMode() ) or other functions that rely on the finished construction of any other object (e.g. RGB.xxx() calls).

The common practice is to only initialise fields owned by the class and place other (external) initialisation tasks in a someClass::begin() method.

Good point. I'll move these initialization to a a LED::begin() method. This might solve the problem overall.

Having said that, I’m surprised you run into that situation when using an object pointer and the new operator in setup() (by which time all other constructors are expected to have finished) but for clean design the “begin()” pattern should be applied across the board.

I initially did run into this problem where I tried to initialize everything before the void setup() step thing things did not crash but instead did not initialize properly. Another option was to use STARTUP() but I wanted to bundle this all into a class for code management and modularity.

1 Like