I'm trying to write a program to upload a file to an arduino. The program can open a serial port and receive data from the arduino. The problem comes when I try to use a callback to a function in a statechart state, the program crashes.
I have never used boost before and decided that this would be a good opportunity to try it out.
I have a statechart object with the basic transitions as so: Idle; -> PortOpening; -> PortOpen; -> WaitForCurveChoiceConfirmation; -> ChooseFile; -> WaitForFileReceive; -> CompareFiles; -> CloseProgram;
Theres a port closing state too but that works fine.
My main program is only as follows
int main(int argc, char* argv[]){
if(argc!=4){
cerr<<"Usage: serialPort baudRate file"<<endl;
return 1;
}
try {
myInterface deviceInterface;
stateMachine fsm(deviceInterface);
fsm.initiate();
} catch (std::exception& e) {
cerr<<"Exception: "<<e.what()<<endl;
}
}
I can open the serial port and receive data perfectly. The problem comes when I try to use a callback to a function in my WaitForCurveChoiceConfirmation state. The arduino sends a message every few seconds, I use the call back when a full message is received by the program. As soon as the message is received and the callback is called, my program crashes.
The WaitForCurveChoiceConfirmationState is as follows
class WaitForCurveChoiceConfirmation: public sc::state< WaitForCurveChoiceConfirmation, Active > {
public:
WaitForCurveChoiceConfirmation(my_context ctx);
typedef boost::mpl::list<
sc::custom_reaction< EvSensorChoiceConfirm > ,
sc::custom_reaction< EvSensorChoiceTimeout >
> reactions;
sc::result react( const EvSensorChoiceConfirm & );
sc::result react( const EvSensorChoiceTimeout & );
void curveChoiceConfirmed(myMessage & msg);
};
WaitForCurveChoiceConfirmation::WaitForCurveChoiceConfirmation(my_context ctx): my_base( ctx ){
context< stateMachine >().getInterface().setfullMsgReceivedCallback(boost::bind(&WaitForCurveChoiceConfirmation::curveChoiceConfirmed, this, _1));
}
void WaitForCurveChoiceConfirmation::curveChoiceConfirmed(my1100Message & msg){
std::cout << "curveChoiceConfirmed callback " << std::endl;
if(msg.isExpectingTemperatureCurve()){
post_event( EvSensorChoiceConfirm() );
}
}
sc::result WaitForCurveChoiceConfirmation::react( const EvSensorChoiceConfirm & ){
std::cout<<"EvSensorChoiceConfirm"<<std::endl;
return transit<ChooseFile>();
}
sc::result WaitForCurveChoiceConfirmation::react( const EvSensorChoiceTimeout & ){
return transit<PortClosing>();
}
The relevant parts of the myInterface class is as follows
class myInterface: public CallbackAsyncSerial
{
/** Default constructor */
myInterface();
/** Default destructor */
virtual ~myInterface();
void processReceivedData(const char *data, unsigned int len);
void setfullMsgReceivedCallback( boost::function<void(myMessage &msg )>);
void clearfullMsgReceivedCallback( );
private:
boost::circular_buffer<char> * incomingMsg;
static const int MESSAGE_DATA_LENGTH = 73; //length of data, not including flags or checksum
static const uint8_t PROTOCOL_OUTGOING_LENGTH = 22; // number of characters in received message
uint8_t receive_buffer[MESSAGE_DATA_LENGTH + 2]; // plus 2 for checksum
char outgoingMsg[PROTOCOL_OUTGOING_LENGTH + 1];
uint8_t statusByte;
uint8_t statusByte2;
uint8_t userByte;
uint8_t userByte2;
uint8_t notificationByte;
uint8_t errorByte;
uint8_t actionByte;
int msgIndex ;
int flagIndex ;
int byteCount;
int checkSum ;
int dispPreambleCount ;
int rcvCalculatedCheckSum ;
char rcvdFlag;
dispMsgState_t dispMsgState ;
static const int FLAG_COUNT = 17;
static const char flags[FLAG_COUNT] ;
boost::function<void(myMessage & msg )> fullMsgReceivedCallback;
};
// this is used as a callback in CallBackAsyncSerial. It takes the data received by serial and processes it
void myInterface::processReceivedData(const char *data, unsigned int len)
{
for (unsigned int i = 0; i < len; i++)
{
incomingMsg->push_back(data[i] );
switch (dispMsgState){
case DISP_PREAMBLE: {//msg start flags
//std::cout << "DISP_PREAMBLE " <<std::endl;
if(incomingMsg->back() == START_FLAG){
dispPreambleCount++;
if (dispPreambleCount == 5){
dispMsgState = DISP_BYTE;
msgIndex = 0;
flagIndex = 0;
rcvCalculatedCheckSum = 5 * START_FLAG;
dispPreambleCount = 0;
rcvdFlag = 0;
}
}
else{
dispPreambleCount = 0; //reset counter if a different character was found
}
incomingMsg->pop_back();
}
break;
case DISP_BYTE:{ //status, user, notification, error bytes
rcvCalculatedCheckSum += incomingMsg->back();
receive_buffer[msgIndex] = incomingMsg->back();
msgIndex++;
incomingMsg->pop_back();
if (msgIndex == 7){
dispMsgState = DISP_INTEGER_FLAG;
}
}
break;
case DISP_INTEGER_FLAG:{ //integer flag
rcvCalculatedCheckSum += incomingMsg->back();
rcvdFlag = incomingMsg->back();
incomingMsg->pop_back();
dispMsgState = DISP_INTEGER;
}
break;
case DISP_INTEGER:{ // integers
rcvCalculatedCheckSum += incomingMsg->back();
if(rcvdFlag == flags[flagIndex]){
receive_buffer[msgIndex] = incomingMsg->back();
}
incomingMsg->pop_back();
msgIndex++;
byteCount++;
if (byteCount >= 2){
if(msgIndex < 21){
dispMsgState = DISP_INTEGER_FLAG;
}
else{
dispMsgState = DISP_FLOAT_FLAG;
}
byteCount = 0;
flagIndex++;
}
}
break;
case DISP_FLOAT_FLAG:{ // float flag
rcvCalculatedCheckSum += incomingMsg->back();
rcvdFlag = incomingMsg->back();
incomingMsg->pop_back();
dispMsgState = DISP_FLOAT;
}
break;
case DISP_FLOAT:{ // floats
rcvCalculatedCheckSum += incomingMsg->back();
if(rcvdFlag == flags[flagIndex]){
receive_buffer[msgIndex] = incomingMsg->back();
}
incomingMsg->pop_back();
msgIndex++;
byteCount++;
if (byteCount >= 4){
if(msgIndex < 49){
dispMsgState = DISP_FLOAT_FLAG;
}
else{
dispMsgState = DISP_STRING_FLAG;
}
byteCount = 0;
flagIndex++;
}
}
break;
case DISP_STRING_FLAG:{ // pressure flag
rcvCalculatedCheckSum += incomingMsg->back();
rcvdFlag = incomingMsg->back();
incomingMsg->pop_back();
dispMsgState = DISP_STRING;
}
break;
case DISP_STRING:{ // pressure string
rcvCalculatedCheckSum += incomingMsg->back();
if(rcvdFlag == flags[flagIndex]){
receive_buffer[msgIndex] = incomingMsg->back();
}
incomingMsg->pop_back();
msgIndex++;
byteCount++;
if (byteCount >= 8){
if(msgIndex < 73){
dispMsgState = DISP_STRING_FLAG;
}
else{
dispMsgState = DISP_CHECKSUM;
}
byteCount = 0;
flagIndex++;
}
}
break;
case DISP_CHECKSUM:{ // rcv checksum
if (byteCount == 0){
receive_buffer[msgIndex ] = incomingMsg->back() ;
byteCount ++;
}
else{
receive_buffer[msgIndex ] = incomingMsg->back();
if (rcvCalculatedCheckSum == ((receive_buffer[msgIndex - 1 ] << 8) | receive_buffer[msgIndex ])) {
std::cout<<"FULL MSG CONFIRMED "<<std::endl;
statusByte = receive_buffer[0];
statusByte2 = receive_buffer[1];
userByte = receive_buffer[2];
userByte2 = receive_buffer[3];
notificationByte = receive_buffer[4];
errorByte = receive_buffer[5];
actionByte = receive_buffer[6];
myMessage * msg = new myMessage();
msg->initialise(statusByte,statusByte2,userByte,userByte2,notificationByte,errorByte,actionByte) ;
std::cout<<"made new msg"<<std::endl;
fullMsgReceivedCallback(*msg); //THIS IS WHERE IT CRASHES
std::cout<<"callback returned"<<std::endl;
delete msg;
std::cout<<"msg deleted"<<std::endl;
/* to convert string to float
#include <sstream>
using namespace std;
string s = "1.60000000000000000000000000000000000e+01";
istringstream os(s);
double d;
os >> d;
cout << d << endl;
*/
}
else{
std::cout<<"FULL MSG NOT CONFIRMED "<<std::endl;
std::cout << std::hex << rcvCalculatedCheckSum <<" " << std::hex<< int((receive_buffer[msgIndex - 1 ] ) )<<" "<< std::hex << int(receive_buffer[msgIndex ] )<<std::endl;
}
dispMsgState = DISP_PREAMBLE;
rcvCalculatedCheckSum = 0;
msgIndex = 0;
byteCount = 0;
}
msgIndex++;
incomingMsg->pop_back();
}
break;
}
}
//incomingMsg->insert( incomingMsg->end(), data, data + len );
//for( boost::circular_buffer<char>::const_iterator i = incomingMsg->begin(); i != incomingMsg->end(); ++i)
// std::cout << *i ;
for( int i = 0; i < MESSAGE_DATA_LENGTH + 1; i++){
//std::cout << std::hex<< (uint8_t)*i << ' ' ;
std::cout << std::hex << receive_buffer[i] << ' ';
}
std::cout <<endl;
}
void myInterface::setfullMsgReceivedCallback(boost::function<void(my0Message & msg)> cb){
fullMsgReceivedCallback = cb;
}
void myInterface::clearfullMsgReceivedCallback( ){
fullMsgReceivedCallback = NULL;
}
The crash occurs at the line "fullMsgReceivedCallback(*msg);" in processReceivedData. I'm sure I'm just binding the function incorrectly or declaring the function pointer object incorrectly.
Can anyone see where I am going wrong?
thanks for your help
I've managed to solve it. It wasn't anything to do with statechart, bind or function.
It was my mistake with calling the callback. I was calling the "full message received" callback even if it didn't have a callback assigned to it and was just NULL. I fixed this by adding and if clause:
if (fullMsgReceivedCallback!= NULL){
Message * msg = Message ;
std::cout<<"made new msg"<<std::endl;
msg->initialise(statusByte,statusByte2,userByte,userByte2,notificationByte,errorByte,actionByte);
std::cout<<" msg initialised"<<std::endl;
fullMsgReceivedCallback(*msg);
delete msg;
std::cout<<"msg deleted"<<std::endl;
}