Go Down

Topic: header file: order of definitions - using typdefs / structs in each others (Read 578 times) previous topic - next topic

s-light


Hello Community,

i am Experimenting with Structures and Callback functions. (build a 'Input Event System')
Now iam at the point where i try to make the calling 'object' (a typdefed struct) available in the called function
in my headerfile i have the following definitions:
Code: [Select]
// Encoder 'Object'
struct sEncoder
{
const byte bNr;

byte pin_A;
byte pin_B;

boolean bA;
boolean bB;
boolean bA_last;
boolean bB_last;

boolean bLastFullStep;

byte bState;

unsigned long ulTimeStamp;

//tCBF_OnStep cbfOnStep;
};
typedef struct sEncoder tEncoder;

// typdef for input state change callback
//typedef void (* tCBF_OnStep) (tEncoder *encObj);


in my ino file iam using it this way:
Code: [Select]
...
/**************************************************************************************************/
/** Include yourselfs header file  (used to define structs and co)                               **/
/**************************************************************************************************/
#include "SwitchPCB_Test_RotaryEncoder_v02.h"
// use "" for files in same directory as .ino

/**************************************************************************************************/
/** definitions                                                                                  **/
/**************************************************************************************************/

tEncoder enc_left = {

111, //const byte bNr;

22, // byte pin_A;
26, // byte pin_B;

0, // boolean bA;
0, // boolean bB;
0, // boolean bA_last;
0, // boolean bB_last;
0, // boolean bLastFullStep;

state_UNDEFINED, // byte bState;

0, // unsigned long ulTimeStamp;

};

tEncoder enc_right = {
....
};

/**************************************************************************************************/
/** functions                                                                                    **/
/**************************************************************************************************/

void callback_Encoder_OnStep (tEncoder *encObj) {
/** Debug Out **/
Serial.print((*encObj).bNr);
Serial.print(":");
Serial.println((*encObj).bState);
/** **/
}

void readEncoder(tEncoder *encObj) {
boolean bTemp_A = digitalRead((*encObj).pin_A);
boolean bTemp_B = digitalRead((*encObj).pin_B);
.....
/**  RUN CALLBACK Function: **/
(*encObj).cbfOnStateChange(encObj);
.....
}

/****************************************************************************************************/
/** Main Loop                                                                                      **/
/****************************************************************************************************/
void loop()
{
readEncoder(&enc_left);
readEncoder(&enc_right);
}


so with this setup the compiler stops and says:
Code: [Select]
In file included from SwitchPCB_Test_RotaryEncoder_v02.ino:42:
SwitchPCB_Test_RotaryEncoder_v02.h:42: error: 'tCBF_OnStep' does not name a type

- ok thats clear - on this point he does not know the definition that comes later  -
but i also cant reorder this - because in the function i use the type tEncoder - and if i write this for the tEncoder definition he also cant find it..

i hope it is clear what iam trying to do -
is there a solution or iam just on the absolute wrong way with this concept?!
i just know a little about this 'callback function' and pointer and reference and co system.
if you know a good tutorial or example how to do such things i am ready to learn new things..
(iam coming from the computer and javascript world - and sometimes its hard to think in limited memory and co..)

sunny greetings stefan

PaulS

Quote
i am Experimenting with Structures and Callback functions.

And ranDom caPitalIzaTion, too, I see.

Code: [Select]
SwitchPCB_Test_RotaryEncoder_v02.h:42: error: 'tCBF_OnStep' does not name a type
Well, where IS tCBF_OnStep typed?

Quote
is there a solution

Of course. It does not involve posting snippets, though. Post your real code. All of it.

s-light

sorry - i commented the definition out..
here comes the full code:
SwitchPCB_Test_RotaryEncoder_v02.h
Code: [Select]
/**
** see SwitchPCB_Test_RotaryEncoder_v02.ino                                                     **
**/

/**
  * Includes Core Arduino functionality
  **/
#if ARDUINO < 100
#include <WProgram.h>
#else
#include <Arduino.h>
#endif

/** type and struct       definition                                                             **/

const byte state_UNDEFINED = 0;
const byte state_CW = 1;
const byte state_CCW = 2;
const byte state_HalfStep = 3;



// Input Event
struct sEncoder
{
const byte bNr;

byte pin_A;
byte pin_B;

boolean bA;
boolean bB;
boolean bA_last;
boolean bB_last;

boolean bLastFullStep;

byte bState;

unsigned long ulTimeStamp;

tCBF_OnStep cbfOnStep;
};
typedef struct sEncoder tEncoder;

// typdef for step callback
typedef void (* tCBF_OnStep) (tEncoder *encObj);

/** END                                        **/


SwitchPCB_Test_RotaryEncoder_v02.ino
Code: [Select]
/**************************************************************************************************
** RotaryEncoder Tester                                                                         **
**  Tests the Rotary Encoders of the SwitchPCBs at 2ChControll Project                          **
** based on infos and ideas from:                                                               **
**  ~ http://www.mikrocontroller.net/articles/Drehgeber                                         **
**  ~ http://www.mikrocontroller.net/topic/drehgeber-auslesen                                   **
**  ~ http://www.mikrocontroller.net/topic/38863                                                **
**  ~ http://hacks.ayars.org/2009/12/using-quadrature-encoder-rotary-switch.html                **
**  ~ http://playground.arduino.cc/Main/RotaryEncoders#Waveform                                 **
**  written by stefan krueger (s-light), stefan@s-light.eu, http://s-light.eu                   **
**  changelog / history                                                                         **
**   17.02.2013 22:25 created                                                                   **
**   23.04.2013 15:06 uncomment code for posting and testing in forum.                          **
**                                                                                              **
**                                                                                              **
**  TO DO:                                                                                      **
**   ~ ask for help with the call back function                                                 **
**   ~ get this into a library or                                                               **
**   ~ a 'addon' file to add to projects (to much customization needed to fit every encoder?!   **
**                                                                                              **
**************************************************************************************************/

/** Include yourselfs header file  (used to define structs and co)                               **/
#include "SwitchPCB_Test_RotaryEncoder_v02.h"
// use "" for files in same directory as .ino


/** definitions                                                                                  **/

tEncoder enc_left = {

111, //const byte bNr;

22, // byte pin_A;
26, // byte pin_B;

0, // boolean bA;
0, // boolean bB;
0, // boolean bA_last;
0, // boolean bB_last;
0, // boolean bLastFullStep;

state_UNDEFINED, // byte bState;

0, // unsigned long ulTimeStamp;

callback_Encoder_OnStep, // tCBF_OnStep cbfOnStep;

};

tEncoder enc_right = {

222, //const byte bNr;

24, // byte pin_A;
28, // byte pin_B;

0, // boolean bA;
0, // boolean bB;
0, // boolean bA_last;
0, // boolean bB_last;
0, // boolean bLastFullStep;

state_UNDEFINED, // byte bState;

0, // unsigned long ulTimeStamp;

callback_Encoder_OnStep, // tCBF_OnStep cbfOnStep;
};



/** functions                                                                                    **/

void callback_Encoder_OnStep (tEncoder *encObj) {
/** Debug Out **/
Serial.print((*encObj).bNr);
Serial.print(":");
Serial.println((*encObj).bState);
/** **/
}

/************************************************/
/** read Encoder and rect                      **/
/************************************************/
void readEncoder(tEncoder *encObj) {

/**
rast punkt immer bei 0,0 oder 1,1
CCW
---- A, B
111: 0, 1
111: 0, 0 !
111: 1, 0
111: 1, 1 !
---- A, B
111: 0, 1
111: 0, 0 !
111: 1, 0
111: 1, 1 !

CW
---- A, B
111: 1, 0
111: 0, 0 !
111: 0, 1
111: 1, 1 !
---- A, B
111: 1, 0
111: 0, 0 !
111: 0, 1
111: 1, 1 !

Error
---- A, B
111: 1, 0
111: 0, 0
111:1
111+1
111: 0, 1
111: 1, 1
111:1
111+1
111: 1, 0
111: 1, 1
111:2 <--Wrong reading!!!
111-1
111: 1, 0
111: 0, 0
111:1
111+1

this reading is wrong.
it is not alowed to have 1,1; 1,0; 1,1

**/

boolean bTemp_A = digitalRead((*encObj).pin_A);
boolean bTemp_B = digitalRead((*encObj).pin_B);
if ( (bTemp_A != (*encObj).bA) || (bTemp_B != (*encObj).bB)) {
//status changed.
(*encObj).bA = bTemp_A;
(*encObj).bB = bTemp_B;

Serial.print((*encObj).bNr);
Serial.print(": ");
Serial.print((*encObj).bA);
Serial.print(", ");
Serial.println((*encObj).bB);

// half step; save state of encoder pins for direction check
if ((*encObj).bA != (*encObj).bB) {
(*encObj).bA_last = (*encObj).bA;
(*encObj).bB_last = (*encObj).bB;
(*encObj).bState = state_HalfStep;
} else {
// full step; one step is finished. --> This is Only true if the last full step was not the same level (high or low)
if ( ((*encObj).bA == (*encObj).bB) && ((*encObj).bA != (*encObj).bLastFullStep) ) {
//remember LastFullStep (error correction)
(*encObj).bLastFullStep = (*encObj).bA;
// Encoder turns CW
if ((*encObj).bA == (*encObj).bB_last)  {
//Do Something
(*encObj).bState = state_CW;
/**  RUN CALLBACK Function: **/
(*encObj).cbfOnStep(encObj);

// direct call for testing..
  //callback_Encoder_OnStep(encObj);
/** **/
Serial.print((*encObj).bNr);
Serial.println(": +1");
/** **/


} else {
// Encoder turns CCW
if ((*encObj).bB == (*encObj).bA_last)  {
//Do Something
(*encObj).bState = state_CCW;
/**  RUN CALLBACK Function: **/
(*encObj).cbfOnStep(encObj);

// direct call for testing..
  //callback_Encoder_OnStep(encObj);
/** **/
Serial.print((*encObj).bNr);
Serial.println(": -1");
/** **/


}
}
}
}
}



}

void setup() {
pinMode(enc_left.pin_A, INPUT_PULLUP);
pinMode(enc_left.pin_B, INPUT_PULLUP);
pinMode(enc_right.pin_A, INPUT_PULLUP);
pinMode(enc_right.pin_B, INPUT_PULLUP);

} // setup


/** Main Loop                                                                                      **/
void loop() {
readEncoder(&enc_left);
readEncoder(&enc_right);
}


and the errors when i try to compile:
Code: [Select]
In file included from SwitchPCB_Test_RotaryEncoder_v02.ino:51:
SwitchPCB_Test_RotaryEncoder_v02.h:46: error: 'tCBF_OnStep' does not name a type
SwitchPCB_Test_RotaryEncoder_v02:89: error: too many initializers for 'tEncoder'
SwitchPCB_Test_RotaryEncoder_v02:109: error: too many initializers for 'tEncoder'
SwitchPCB_Test_RotaryEncoder_v02.ino: In function 'void readEncoder(tEncoder*)':
SwitchPCB_Test_RotaryEncoder_v02:208: error: 'struct sEncoder' has no member named 'cbfOnStep'
SwitchPCB_Test_RotaryEncoder_v02:224: error: 'struct sEncoder' has no member named 'cbfOnStep'


if i put the typedef of the function for the sEncoder struct i have some other / similar errors
Code: [Select]
In file included from SwitchPCB_Test_RotaryEncoder_v02.ino:51:
SwitchPCB_Test_RotaryEncoder_v02.h:27: error: typedef 'tCBF_OnStep' is initialized (use __typeof__ instead)
SwitchPCB_Test_RotaryEncoder_v02.h:27: error: 'tEncoder' was not declared in this scope
SwitchPCB_Test_RotaryEncoder_v02.h:27: error: 'encObj' was not declared in this scope
SwitchPCB_Test_RotaryEncoder_v02.h:48: error: 'tCBF_OnStep' does not name a type
SwitchPCB_Test_RotaryEncoder_v02:89: error: too many initializers for 'tEncoder'
SwitchPCB_Test_RotaryEncoder_v02:109: error: too many initializers for 'tEncoder'
SwitchPCB_Test_RotaryEncoder_v02.ino: In function 'void readEncoder(tEncoder*)':
SwitchPCB_Test_RotaryEncoder_v02:208: error: 'struct sEncoder' has no member named 'cbfOnStep'
SwitchPCB_Test_RotaryEncoder_v02:224: error: 'struct sEncoder' has no member named 'cbfOnStep'


i think i missing some basic understanding of this..

thanks for your help

sunny greetings stefan

PS: how do you prefer such code? in-line or as attached file? i had to remove some formating comments to get under 9500 characters..

pYro_65

The first lot of information you provided was sufficient, I'll explain why:

'tCBF_OnStep' is not found due to it being declared after the class ( which you already assumed ), and as the compiler reads the code linearly, it does not see the function type definition until after the malformed class.

C++ has a feature called "forward declarations" which allows the compiler to use a type it has not seen (not previously declared ) in other declarations. This differs from using ( defining ) the type before it is declared.

To fix your scenario:

  • Remove this line: typedef struct sEncoder tEncoder;
       It is not needed in C++, structs are types by definition. Typedefs are only useful when you change the type, as in:
    Code: [Select]
    typedef sEncoder* tEncoder;

    This provides a pointer type, rather than the original type.

  • Add 'class sEncoder;' above the structure, this is the forward declaration.

  • Underneath the forward declaration but above the struct, add: typedef void (* tCBF_OnStep) (sEncoder*encObj);  

    Once complete, bob should be your uncle.


s-light

Thanks pYro_65,

it is working great :-)
[solved]

my next step is to convert this into a library - maybe this is a bit overkill for this easy thing - but hey - its easy to use and makes a project more structured. (so my opinion)

so in this case - if i have the same schema for the callback function the class would need to give a referenc to herself.
how is this possible ? or is there a other method or construct to make something similar?

i have the library attached as zip but for a fast look i include the header and cpp here:
(in the zip is also an example - my test sketch for developing the lib)

slight_RotaryEncoder.h
Code: [Select]

/**
  * slight_RotaryEncoder.h
  * for Infos  see slight_RotaryEncoder.cpp file
  **/

 
#ifndef slight_RotaryEncoder_h
#define slight_RotaryEncoder_h

/**
  * Includes Core Arduino functionality
  **/
#if ARDUINO < 100
#include <WProgram.h>
#else
#include <Arduino.h>
#endif


/**
  *
  * Definitions
  *
  **/

/*
struct aspecialthing
{
int one;
int two;
int three;
};

typedef struct aspecialthing AspecialThing;
*/

// definitions
// typdef for step callback
//typedef void (* tCBF_OnStep) (slight_RotaryEncoder*encObj);
typedef void (* tCBF_OnStep) (byte bState); 

const byte state_UNDEFINED = 0;
const byte state_CW = 1;
const byte state_CCW = 2;
const byte state_HalfStep = 3;

class slight_RotaryEncoder
{
public:
// public methods
//Constructor
slight_RotaryEncoder(byte bPin_A_NewValue, byte bPin_B_NewValue, byte bPulsPerStep_NewValue, tCBF_OnStep cbfOnStep_NewValue);
// bool inverse_logic = false --- the = false means a standard value / makes it optional?

//Destructor
~slight_RotaryEncoder();

//word xYfuncName(word XXX);

// run every loop to update stee of system.
void update();

// get state
byte getState();



private:
// per object data

byte pin_A;
byte pin_B;

byte bPulsPerStep;

tCBF_OnStep cbfOnStep;


//internal data
byte bNr;

boolean bA;
boolean bB;
boolean bA_last;
boolean bB_last;
boolean bLastFullStep;

byte bPulsCount;
byte bState;

unsigned long ulTimeStamp;



//word _blubber;
//AspecialThing _theValues;

// private methods
void test();

void stateChange();
void printState();
};

#endif



slight_RotaryEncoder.cpp
Code: [Select]
/**
  * slight_RotaryEncoder.cpp
  * library for easy encoder using
  *
  * copyright (c) 2013 by stefan krueger
  *
  * based on infos and ideas from:
  *  ~ http://www.mikrocontroller.net/articles/Drehgeber
  *  ~ http://www.mikrocontroller.net/topic/drehgeber-auslesen
  *  ~ http://www.mikrocontroller.net/topic/38863
  *  ~ http://hacks.ayars.org/2009/12/using-quadrature-encoder-rotary-switch.html
  *  ~ http://playground.arduino.cc/Main/RotaryEncoders#Waveform
  *
  * whit help from:
  *   pYro_65 ( http://arduino.cc/forum/index.php/topic,162249.0.html )
  *
  * changelog / history:
  *   24.04.2013 08:46 created; based on SwitchPCB_Test_RotaryEncoder_v02
  *
  * to do:
  *   
  **/


/**
  * Includes Core Arduino functionality
  **/
#if ARDUINO < 100
#include <WProgram.h>
#else
#include <Arduino.h>
#endif


/**
  * Include yourselfs header file
  **/
#include <slight_RotaryEncoder.h>


/**
  * Constructor
  **/
slight_RotaryEncoder::slight_RotaryEncoder(byte bPin_A_NewValue, byte bPin_B_NewValue, byte bPulsPerStep_NewValue, tCBF_OnStep cbfOnStep_NewValue) {

// initialise variables
pin_A = bPin_A_NewValue;
pin_B = bPin_B_NewValue;
bPulsPerStep = bPulsPerStep_NewValue;
cbfOnStep = cbfOnStep_NewValue;

//debug nr.
bNr = 33;

bA = 0;
bB = 0;
bA_last = 0;
bB_last = 0;
bLastFullStep = 0;
bPulsCount = 0;
bState = state_UNDEFINED;
ulTimeStamp = 0;

// initialise pins
pinMode(pin_A, INPUT_PULLUP);
pinMode(pin_B, INPUT_PULLUP);

}

/**
  * Destructor
  **/
slight_RotaryEncoder::~slight_RotaryEncoder() {
  //
}



/**
  * Public methods
  **/

/*
word slight_RotaryEncoder::xYfuncName(word XXX)
{
  //some code
  result XXX;
}*/


/**
  * Returns State (direction)
  **/
byte slight_RotaryEncoder::getState() {
return bState;
}


/**
  * update
  **/
void slight_RotaryEncoder::update() {

/**
rast punkt immer bei 0,0 oder 1,1
CCW
---- A, B
111: 0, 1
111: 0, 0 !
111: 1, 0
111: 1, 1 !
---- A, B
111: 0, 1
111: 0, 0 !
111: 1, 0
111: 1, 1 !

CW
---- A, B
111: 1, 0
111: 0, 0 !
111: 0, 1
111: 1, 1 !
---- A, B
111: 1, 0
111: 0, 0 !
111: 0, 1
111: 1, 1 !

Error
---- A, B
111: 1, 0
111: 0, 0
111:1
111+1
111: 0, 1
111: 1, 1
111:1
111+1
111: 1, 0
111: 1, 1
111:2 <--Wrong reading!!!
111-1
111: 1, 0
111: 0, 0
111:1
111+1

this reading is wrong.
it is not alowed to have 1,1; 1,0; 1,1

**/

boolean bTemp_A = digitalRead(pin_A);
boolean bTemp_B = digitalRead(pin_B);
if ( (bTemp_A != bA) || (bTemp_B != bB)) {
//status changed.
bA = bTemp_A;
bB = bTemp_B;

/** **
Serial.print(bNr);
Serial.print("?: ");
Serial.print(bA);
Serial.print(", ");
Serial.println(bB);
/** **/

// half step; save state of encoder pins for direction check
if (bA != bB) {
bA_last = bA;
bB_last = bB;
bState = state_HalfStep;
stateChange();
} else {
// full step; one step is finished. --> This is Only true if the last full step was not the same level (high or low)
if ( (bA == bB) && (bA != bLastFullStep) ) {
//remember LastFullStep (error correction)
bLastFullStep = bA;
// Encoder turns CW
if (bA == bB_last)  {
//Do Something
bState = state_CW;
stateChange();
} else {
// Encoder turns CCW
if (bB == bA_last)  {
bState = state_CCW;
stateChange();
}
}
}
}
}



}



/**
  * Private methods
  **/
/*
void slight_RotaryEncoder::test()
{
_blubber = _blubber + 1;
}*/

void slight_RotaryEncoder::stateChange() {

if ( (bState == state_CW) || (bState == state_CCW) ) {
if (bPulsCount >= bPulsPerStep) {
// One Step for the User
bPulsCount = 0;

/** DEBUG OUT **/
Serial.print(bNr);
Serial.print(": ");
printState();
Serial.println();
/** **/

/**  RUN CALLBACK Function: **/
cbfOnStep(bState);
} else {
bPulsCount = bPulsCount + 1;
}
}





}

void slight_RotaryEncoder::printState() {
switch (bState)
{
case state_UNDEFINED: {
Serial.print("state_UNDEFINED");
break;
}
case state_CW: {
Serial.print("state_CW   +1");
break;
}
case state_CCW: {
Serial.print("state_CCW  -1");
break;
}
case state_HalfStep: {
Serial.print("state_HalfStep");
break;
}
default: {
Serial.println("??");
}
}
}

/** THE END **/


Thanks for your help!
sunny greetings stefan

Go Up