header file: order of definitions - using typdefs / structs in each others

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:

// 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:

...
/**************************************************************************************************/
/** 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:

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

i am Experimenting with Structures and Callback functions.

And ranDom caPitalIzaTion, too, I see.

SwitchPCB_Test_RotaryEncoder_v02.h:42: error: 'tCBF_OnStep' does not name a type

Well, where IS tCBF_OnStep typed?

is there a solution

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

sorry - i commented the definition out..
here comes the full code:
SwitchPCB_Test_RotaryEncoder_v02.h

/**
 ** 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

/**************************************************************************************************
 ** 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:

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

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..

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: 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.

Thanks pYro_65,

it is working great :slight_smile:
[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

/**
  * 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

/**
  * 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

slight_RotaryEncoder_2013_04_24__22_58.zip (4.79 KB)