Introduction: Christmas Music Cheer Light

About: Howdy, we are application engineers in Seeed. Sharing projects with the maker community is awesome. Hope you like it XD Seeed is the IoT hardware enabler providing services that empower IoT developers to swift…

Merry Christmas! Do you want to have a Christmas tree that can interact with you?

Step 1: Things Used in This Project

Step 2: Hardware Connection

Connect PIR Sensor, Loudness Sensor and LED strip to Base Shield's port D2, A0 and D6 separately. Plug Base Shield to Seeduino, all done.

Step 3: Software Programming

The libraries following down need to install before programming, please download and import them to your Arduino IDE manually:

In order to make the code more concise, we have packaged it. CheerLight class is the application class of this project.

   class application::CheerLight
     : public application::interface::IApplication {
   public:
     void setup(void);
     void loop(void);    
     void setPIRSensorPin(uint8_t pin);
     void setLoudnessSensorPin(uint8_t pin);
     void measureSensors(void);
    
     void changeAnimation(void * args);
     void changeSpeed(void * args);
     void changeColor(void * args);
    
     static application::CheerLight * getInstance(void);    
   protected:
     driver::LEDStrip        _ledStrip;
     driver::PIRSensor       _pirSensor;
     driver::LoudnessSensor  _loudnessSensor;    
     uint8_t _animation;    
     middleware::Delegate<application::CheerLight> _detectedDelegate;
     middleware::Delegate<application::CheerLight> _absoluteLoudnessDelegate;
     middleware::Delegate<application::CheerLight> _relativeLoudnessDelegate;    
     CheerLight(void);
     static application::CheerLight _instance;
   };

CheerLight class was designed by Singleton Patterns, which means there is only one instance for it, you can call CheerLight::getInstance() to that instance. If your Sensors' connection are difference to Hardware Connection, you can change them by calling setPIRSensorPin() and setLoudnessSensorPin() methods.

We recommand calling measureSensors() method in timer interrupt to make sensors measured timely, but calling changeAnimation(), changeSpeed() or changeColor() methods manually are not necessary. They will be called via Delegates when sensors measured.

What is a Delegate?

As we all know, we can declare a function pointer and make it point to a function in C:

   void func1(void);
   void (*pFunc)(void) = func1;

and use it to call the function it pointed to

   pFunc();

But there are differences in C++, if you try to compile the code following down:

   class A {
   public:
       void func1(void);
   };
   void (*pFunc)(void) = &A::func1;

the compiler will report a type conversion error, here is the right example:

   void (A::*pFunc)(void) = &A::func1;

When we try to use it to call that method, error again. The reason for that error is that an object-method must be called by an object. So we create an object to call it:

   A a;
   a.*pFunc();

This time no problem. So there is Delegate class in Delegate.h.

   template <typename T>
   class middleware::Delegate
     : public middleware::interface::IDelegate {
   public:
     Delegate(T * object, void (T::*method)(void *));
     void invoke(void * args);
    
   protected:
     T * _object;
     void (T::*_method)(void *);
   };
   template <typename T>
   inline middleware::Delegate<T>::Delegate(T * object, void (T::*method)(void *))
     : _object(object), _method(method) { }
   template <typename T>
   inline void middleware::Delegate<T>::invoke(void * args) {
     (_object->*_method)(args);
   }

Because Delegate class is a template class, which means Delegate<A> is difference to Delegate<B>, how to make them be pointed by pointer have same type? The answer is interface, so there is IDelegate interface in IDelegate.h.

   class middleware::interface::IDelegate {
   public:
     virtual void invoke(void * args) = 0;
   };

In PIR Sensor and Loudness Sensor's class, there has a variable named _delegates used to store pointer of Delegates, and there has a method named invokeAllDelegates() used to invoke all Delegates in _delegates, it will be called in measure() method.

NOTE: Delegate methods, such as changeAnimation(), changeSpeed() and changeColor() will be called in timer2 interrupt, so do NOT use delay() or other interrupt-based function in it.

Step 4: Operation