0

I am trying to get my head around how to instantiate objects from existing libaries inside a class object. Specifically I am trying to get the bounce2.h debouncer working.

At the moment my code compiles but i think the debounce function is not working as the button is filling my array with lots of random numbers (even from setup) whereas it works fine on a non-class based sketch, so I think that I am probably not instantiating the bounce object correctly. If anyone can help I would be very grateful!

My header:

#ifndef CandleRack_h
#define CandleRack_h

class CandleRack
{
  public:

    CandleRack(byte candleRackStatus);
    void begin();
    void pushButton();
    void selectRandomCandle();
    void turnOnCandle(byte candlePosition);
    void burnCandlesForMs();
    void fadeOutCandle(byte candlePosition);

  private:
    Bounce _debouncePushButton;
    byte _pushButtonPin; 
    byte _candleRackStatus;    
    byte _candlePosition;
    byte _candleStatus[];
    elapsedMillis _candleTimeElapsed;
    unsigned int _candleOnForMs;
};
#endif

My cpp

#include "Arduino.h"
#include <FastLED.h>
#include <elapsedMillis.h>
#include <Bounce2.h>
#include "CandleRack.h"

#define NUM_LEDS 50

CandleRack::CandleRack(byte candleRackStatus)
{
  _candleRackStatus = candleRackStatus;
}

void CandleRack::begin()
{
  randomSeed(analogRead(0));

  _debouncePushButton = Bounce();
  _pushButtonPin = 3;
  _candleOnForMs = 20000;
  pinMode(_pushButtonPin, INPUT_PULLUP);
  _debouncePushButton.attach(_pushButtonPin);
  _debouncePushButton.interval(10);

  for (byte b = 0; b < NUM_LEDS; b++)
  {
    _candleStatus[_candlePosition] = 0;
  }

  Serial.println("Candle Array: ");       // Debug ************************
  for (byte b = 0; b < NUM_LEDS; b++)     // Debug ************************
  {                                       // Debug ************************
    Serial.print(_candleStatus[b]);       // Debug ************************
    Serial.print(", ");                   // Debug ************************
  }                                       // Debug ************************
  Serial.println("");                     // Debug ************************
  Serial.println("Begin Complete");       // Debug ************************
}


void CandleRack::pushButton()
{
  // Look for a button push
  if (_debouncePushButton.update())
  {
    if (_debouncePushButton.fell())
    {
      selectRandomCandle();
    }
  }
}


void CandleRack::selectRandomCandle()
{
  byte randomCandlePosition;
  do
  {
    randomCandlePosition = random(0, NUM_LEDS);
  }
  while (_candleStatus[randomCandlePosition] != 0);

  turnOnCandle(randomCandlePosition);
}


void CandleRack::turnOnCandle(byte _candlePosition)
{
  // leds[_candlePosition] = CRGB::Red;
  _candleStatus[_candlePosition] = 1;

  Serial.println("Button Pushed: ");      // Debug ************************
  Serial.println(_candlePosition);        // Debug ************************
  Serial.println("Candle Array: ");       // Debug ************************
  for (byte b = 0; b < NUM_LEDS; b++)     // Debug ************************
  {                                       // Debug ************************
    Serial.print(_candleStatus[b]);       // Debug ************************
    Serial.print(", ");                   // Debug ************************
  }                                       // Debug ************************
  Serial.println("");                     // Debug ************************
}


void CandleRack::burnCandlesForMs()
{
  // See if it is time to turn the candle off
  for (byte b = 0; b < NUM_LEDS; b++)
  {
    if (_candleTimeElapsed > _candleOnForMs && _candleStatus[b] == 1)
    {
      CandleRack::fadeOutCandle(b);
      _candleTimeElapsed = 0;
    }
  }
}


void CandleRack::fadeOutCandle(byte _candlePosition)
{
  _candleStatus[_candlePosition] = 0;
}

My sketch:

#include "Arduino.h"
#include <FastLED.h>
#include <elapsedMillis.h>
#include <Bounce2.h>
#include "CandleRack.h"

CandleRack MyCandleRack(0);

void setup() {
  delay(3000); // sanity delay

  Serial.begin(9600);
  while (!Serial); 

  Serial.println("Setup Starts");  // Debug ************************

  MyCandleRack.begin();

  Serial.println("Setup Complate");  // Debug ************************
}

void loop() {

  MyCandleRack.pushButton();
 // MyCandleRack.burnCandlesForMs();

  FastLED.show();
}

edit - added the code from the original debounce example for a single sketch

// Detect the falling edge

// Include the Bounce2 library found here :
// https://github.com/thomasfredericks/Bounce-Arduino-Wiring
#include <Bounce2.h>


#define BUTTON_PIN 2
#define LED_PIN 13

int ledState = LOW;


// Instantiate a Bounce object :
Bounce debouncer = Bounce(); 

void setup() {

  // Setup the button with an internal pull-up :
  pinMode(BUTTON_PIN,INPUT_PULLUP);

  // After setting up the button, setup the Bounce instance :
  debouncer.attach(BUTTON_PIN);
  debouncer.interval(500);

  // Setup the LED :
  pinMode(LED_PIN,OUTPUT);
  digitalWrite(LED_PIN,ledState);


}

void loop() {

  // Update the Bounce instance :
   debouncer.update();

   // Call code if Bounce fell (transition from HIGH to LOW) :
   if ( debouncer.fell() ) {

     // Toggle LED state :
     ledState = !ledState;
     digitalWrite(LED_PIN,ledState);

   }
}

1 Answer 1

1

Your main issue (the cause of your symptoms) is the fact that you have no size for your _candleStatus array. Without specifying a size you end up with no memory allocated to the array, and hence no valid content.

In your class definition the line:

byte _candleStatus[];

should be modified to:

byte _candleStatus[NUM_LEDS];

and NUM_LEDS should be defined in the header, not in the CPP file. (By specifying it in the header file, since the header file is included in the CPP file, the definition ends up in both files).

Aside from that this line doesn't do what you think it does:

_debouncePushButton = Bounce();

You are copying the content of an anonymous bounce object created just for the occasion into a pre-existing Bounce object. You don't want that line to exist since the Bounce object has already been instantiated by the construction of your CandleRack object.

However there is something you need to be aware of known as the Static Initialization Order Fiasco which can bite you on the rear end if you are not aware of it. This is basically when you use objects in other objects and those objects haven't yet been properly created since you cannot predict the order in which objects are initialized. This may or may not be an issue in your system - it all depends on how the other classes you use behave.

One way around the issue, if you find it does become a problem (or if you just want to avoid the problem altogether) is to use pointers and what is known as Construct On First Use. This is where you don't instantiate any objects in your constructor or the class definition - instead you just have pointers to them and use the new keyword to construct the object from within a member function (such as begin()).

For this you change your class definition object entries to be pointers, such as:

Bounce *_debouncePushButton = NULL;

(Note that not all versions of the compiler will support that assignment in the class definition. It's a modern addition to C++ and depending on your version of the Arduino software may or may not work. If it doesn't you can move the assignment into the constructor by removing the = NULL from the definition and adding _debouncePushButton = NULL; into the constructor.)

Then construct a new object in the begin() function (if it hasn't already been created):

if (_debouncePushButton == NULL) {
    _debouncePushButton = new Bounce();
}

Now you have to access the _debouncePushButton as a pointer to an object not an object, so change all your accesses to use the pointer dereference operator ->:

_debouncePushButton->attach(_pushButtonPin);
... etc ...

In case the class should ever go out of scope or be used in a dynamic way you really should create a destructor to delete any objects that have been created:

CandleRack::~CandleRack() {
    if (_debouncePushButton != NULL) {
        delete _debouncePushButton;
    }
}

Note that you should only ever try and delete objects that have actually been created - hence the test to see if it's not null.

Excessive use of new and delete though is discouraged, so it is best to use objects in a static context if possible. Too much new and delete or malloc and free can cause heap fragmentation which can lead to you running out of memory and all the associated stability problems that come with it.

Of course, all this is optional and chances are it won't be needed if the other classes behave well.

16
  • Thanks. It is still giving me the same issue though, it is adding lots of random numbers to the array. I can post the code for using the debounce object in a single sketch if it helps?
    – Rob Hilken
    Mar 17, 2016 at 22:55
  • See my edit. You have a second completely unrelated problem with your code.
    – Majenko
    Mar 17, 2016 at 23:03
  • Agh, OK. one issue is that #define NUM_LEDS comes from my FastLED array and I need that in the main sketch I think. I'm not sure how to reference/point to a #define though (which it needs to be).
    – Rob Hilken
    Mar 17, 2016 at 23:31
  • If it is in CandleRack.h then it is also in CandleRack.cpp since CandleRack.h is included in CandleRack.cpp.
    – Majenko
    Mar 17, 2016 at 23:37
  • 1
    @frarugi87 yes, I wrote it just before bed. It's not a wonderful answer.
    – Majenko
    Mar 18, 2016 at 10:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.