Callback function

I am kind of confused in Callback functions. I have seen some examples and read a little but still have doubts.

Are callback functions similar to Interrupt Service Routines(ISRs) in Microcontrollers?

In most examples I have seen, there is nothing for Callbacks in loop() functions. Then how does the it will jump to callback routine?

You simply tell a piece of code what function you want to use when it wants to do something. It just stores that function in a variable. And once the piece of code thinks it needs to use that function it can call it.

It's a bit like ISR on the Arduino with attachInterrupt() but it is not a ISR. That just stores the function name you pass it. And the real ISR calls that function (by reading the variable) when it's fired.

But every function can call a variable other function. And if it's used when a condition is met it's often called a call back :slight_smile:

void (*functionPointer)(void);

void setup() {
  Serial.begin(115200);
  functionPointer = &testFunction;
}

void loop() {
  functionPointer();
}

void testFunction(){
  Serial.println("TEST!!");
}
1 Like

another example using the Timer2 library:

#include <MsTimer2.h>

const byte buttonPin = 5;

void setup()
{
  Serial.begin(9600);
  pinMode(13, OUTPUT);
  pinMode(buttonPin, INPUT_PULLUP);
}

void loop()
{
  if(buttonFivePressed())
  {
   flashLedThirteen(5000);
  }
}

bool buttonFivePressed(void)
{
  static unsigned long lastMillis = 0;
  static byte lastPress = HIGH;
  byte currentPress = digitalRead(buttonPin);
  if(currentPress != lastPress)
  {
    if(millis() - lastMillis < 200) return false;
    lastPress = currentPress;
    if(currentPress == LOW)
    {
      Serial.println(" button press detected!");
      lastMillis = millis();
      return true;
    }
  }
  return false;
}

void flashLedThirteen(int ledTime)
{
  digitalWrite(13, HIGH);
  Serial.println("LED ON");
  // sets a new timer callback function
  MsTimer2::set(ledTime, [] {  // the square brackets define the start of the anonymous callback function which is executed after 5000 milliseconds in this example
    digitalWrite(13, LOW);
    Serial.println("Timer expired...");
    Serial.println("LED OFF");
    MsTimer2::stop();
  }); // the curly brace defines the end of the anonymous callback function
  MsTimer2::start();
}
1 Like

I know this is an old thread, but I'm just about to leap into a new project, and figured I'd expend my brain cells on the application logic... so I'm hoping someone might simplify what I need to do for the example below...

How do/would I setup the case where I create a class with a function that asynchronously receives serial input, and only when the incoming terminator (e.g. CR) is received, it raises a callback in the main program code.

i.e. minimal pseudocode...

myCLASS::tick() {
   // do stuff
   // receive serial chars to build a c-string
   if (CR was received)
     myCallback(data)  // send the comnplete c-string back to the main program
}
main::loop() {
   myclass->tick();  // tap the class frequently to maintin the input functionality
   // do other stuff
}

main::myCallback(data) {  // we get thrown here to process the call from myClass::tick() event
   // process the complete returned c-string 
}

you should have created a new thread

lastchancename:
I know this is an old thread, but I'm just about to leap into a new project, and figured I'd expend my brain cells on the application logic... so I'm hoping someone might simplify what I need to do for the example below...

How do/would I setup the case where I create a class with a function that asynchronously receives serial input, and only when the incoming terminator (e.g. CR) is received, it raises a callback in the main program code.

i.e. minimal pseudocode...

myCLASS::tick() {

// do stuff
  // receive serial chars to build a c-string
  if (CR was received)
    myCallback(data)  // send the comnplete c-string back to the main program
}





main::loop() {
  myclass->tick();  // tap the class frequently to maintin the input functionality
  // do other stuff
}

main::myCallback(data) {  // we get thrown here to process the call from myClass::tick() event
  // process the complete returned c-string
}

Here you go:

class MyClass
{
    using InputEvent = void (*)(const char*); //type aliasing
                                              //C++ version of: typedef void (*InputEvent)(const char*)
public:
    void RegisterCallback(InputEvent InEvent)
    {
        Event = InEvent;
    }
    void Tick()
    {
        //
        //accumuate buffer here
        if (true) {
            //raise event
            Event(buffer);
        }
    }
private:
    InputEvent Event;
    char buffer[10];
};

//global callback
void ProcessData(const char* Data)
{
    Serial.println(Data);
}

MyClass MClass;

void setup() {
  // put your setup code here, to run once:
  MClass.RegisterCallback(ProcessData);
}

void loop() {
  // put your main code here, to run repeatedly:
  MClass.Tick();
}
3 Likes

Thanks _new, I’ll give it a shot.
Sorry about resurrecting the old thread, but it was on topic, and well above the typical user.

If the mods think it would be better on its own, I’m ok with that.