6 years, 7 months ago.

access to members from within a callback

I wrote an encapsulation over a Serial() instance to build a clean interface with an external device. Something like this :

class ConnectIt {
    private:
        Serial link;
        void handler(int event); //callback of the Serial.read()
   public:
        ConnectIt(PinName tx, PinName rx); // constructor
        int info[16]; // parsed info stored here
}

handler is a callback function passed to the Serial.read() call. handler should be able to parse the buffer and fill the info array. But callback functions have no access to properties or members (this is not defined), and std::bind is not available on the mbed environment, even with <functional>.

How to bind the callback to the object instance ?

1 Answer

6 years, 7 months ago.

Hello yota,

I think callback functions have access to class members. The code below is using RawSerial that doesn't use streams, thus making it safe to use in interrupt handlers with the RTOS. It worked for me on an LPC1768 board.

ConnectIt.h

#ifndef CONNECT_IT_H
#define CONNECT_IT_H

#define RX_MAXLEN       10
#define INFO_MAXLEN     16
#define DELIMITER       ','

#include "mbed.h"

class ConnectIt
{
    RawSerial link;
    void  handler();
    int   rxLen;
    char  rxBuf[RX_MAXLEN];
    int   infoLen;
    int   info[INFO_MAXLEN];
public:
    ConnectIt(PinName tx, PinName rx);
};
#endif

ConnectIt.cpp

#include "ConnectIt.h"

ConnectIt::ConnectIt(PinName tx, PinName rx) : link(tx, rx), rxLen(0), infoLen(0) {
    link.attach(callback(this, &ConnectIt::handler), Serial::RxIrq);
}

void ConnectIt::handler() {
    while (link.readable()) {
        if (rxLen < RX_MAXLEN) {
            rxBuf[rxLen] = link.getc();
            if (rxBuf[rxLen] == DELIMITER) {
                rxBuf[rxLen] = '\0';
                rxLen = 0;
                if (infoLen >= INFO_MAXLEN)
                    infoLen = 0;
                info[infoLen] = atoi(rxBuf);
                printf("info[%d] = %d\r\n", infoLen, info[infoLen]);
                infoLen++;
            }
            else
                rxLen++;
        }
        else
            rxLen = 0;
    }
}

main.cpp

#include "mbed.h"
#include "ConnectIt.h"

DigitalOut led1(LED1);
ConnectIt  connectIt(USBTX, USBRX);

int main() {
    while (true) {
        led1 = !led1;
        wait(0.5);
    }
}

NOTE:

  • Since I was not able to figure out how to pass member function arguments to the callback function the handler function takes no arguments.
  • DELIMITER char shall be applied also after the last number sent in a serial message.

Interesting... up to now, I used Serial.read(), The main problem I see with your while( link.readable() ) is that the loop is always active, instead of using events (like SERIAL_EVENT_RX_COMPLETE or SERIAL_EVENT_RX_CHARACTER_MATCH). Nonetheless, thank you for your example, I'll test it... and accept it after that :)

posted by yota news 18 Sep 2017

The while(link.readable()) loop isn't normally active. It's triggered only on serial receive interrupt. The main purpose for looping here is to improve the overall performance when the serial message is longer than one byte (The program will work also after removing the looping). Also remember, there is no magic done by hardware even when using events. The relevant code is in that case included by MBED OS, not by you. Anyway, in both cases it is possible to link the callback to the object's instance as shown above in the constructor of ConnectIt class.

posted by Zoltan Hudak 27 Sep 2017