Linking custom classes with Blynk API

This topic was opened before in some old posts like here and there and many others with no proper closure, I tried to find a proper answer to this, and the way found worth sharing.

Problem#1: you can’t simply use Blynk object like Blynk.virtualWrite() inside a custom class’s definition, because Blynk API declaration is done inside the included blynk header; like BlynkSimpleEsp32.h, which is in main scope. trying to use Blynk functions will lead to compilation error: ‘Blynk’ was not declared in this scope.

Problem#2: you can’t reinclude Blynk header file inside custom class, this will lead to another compilation error: multiple definition of Blynk as “Blynk” should be defined only once in your project.

As @vshymanskyy advised here:

Blynk functions shall be implemented in main scope and then making a link between each class or class instance and Blynk functions.

Approach#1: to implement a function in main that checks for changes in each instance variables and report any change to Blynk using Blynk.virtualWrite(), and the other direction; to implement a BLYNK_WRITE(Vp) callback for each object using its corresponding virtual pin, or to use BLYNK_WRITE_DEFAULT() callback and within; search for the corresponding object that matches the passed virtual pin and update object’s variable. this approach is not quite neat and is no proper implementation for OOP.

Approach#2: to implement a function callback, as this is exactly what function callabacks are made for as explained here. by defining the function in main scope, the Blynk.virtualWrite() will be properly implemented, then passing this function to custom class’s static member, and since each object will hold its own virtual pin value stored in a private field, shall each object/ instance of this class be able to interface with Blynk API by calling this member and passing its virtual pin and corresponding variable to it, then the callback function will be executed in main scope with no problem. the other direction my be quite similar to approach#1 but by defining a static array of pointers that points to all class’s instances, then iterating over this array inside BLYNK_WRITE_DEFAULT() calling a custom write() function; each object shall match the passed virtual pin to its local stored one and only the corresponding instance shall perform the requested write().

Solution: the next code example implements Approach#2 by a callback function
with a function pointer, as syntaxes can get quite ugly, I used the most neat form I could found by using typedef.

CustomClass.h

#ifndef CUSTOM_CLASS_INCLUDED
#define CUSTOM_CLASS_INCLUDED

//create a typedef for callback function's signature where pushCallback can be used later as a function pointer to any function that has the same signature : void functionName(byte, int)
typedef void (*pushCallback)(byte, int);

class CustomClass{
// static members
static pushCallback pushValueF; 
//pushValueF is a static member variable that will hold a pointer to function of type pushCallback 's signature

public:
CustomClass();
// pass a pointer to the callback function defined at main scope and has the pushCallback's function signature
void static setPushFunc(pushCallback);
/*All other setters
-
-
*/

private:
void pushVP2Cloud();
unsigned long last_push_time;
byte virtualPin;
int localVariable;
/*All other member variables and functions prototypes
-
-
*/
}
#endif

CustomClass.cpp

#include "CustomClass.h"

//static members initialization MUST be here, Not inside the class definition not at main{} and type specifier has to be used
pushCallback CustomClass::pushValueF = 0; 
//pushValueF  points to nothing for now, this pointer's function has to be implemented in main scope

//class default constructor
CustomClass::CustomClass(){
/*local variables initialization
-
-
*/
}

//setters
void CustomClass::setPushFunc(pushCallback pushF)
{
    CustomClass::pushValueF = pushF;
}
/*All other setters
-
-
*/

//to be called when variable changes or periodically
void CustomClass::pushVP2Cloud() {
  last_push_time = millis();
  CustomClass::pushValueF(virtualPin, localVariable);
}

project.ino:

/*All Blynk defines
-
*/

#include <BlynkSimpleEsp32.h> 
//#include "BlynkEdgent.h"
#include "CustomClass.h"

//callback function defined at main scope
void pushValue(byte Vpin, int val) {
	Blynk.virtualWrite(Vpin, val);
}

void setup(){
  //passing callback function to CustomClass
 CustomClass::setPushFunc(pushValue);

 // creating objects using class default constructor all this objects will have Blynk functionality
CustomClass firstInstance = CustomClass();
CustomClass secondInstance= CustomClass();
}

linking BLYNK_WRITE_DEFAULT() may be amended later.

Hope that helps somebody

Links to some Older posts:

1 Like