Let's Make Robots!

Struggling with creating libraries for Arduino

UPDATE

Documentation of my efforts will be continued in my blog: Building a new controller - fun and games

Thanks to the help of my fellow LMR members I now have a working library. I have attached the initial library created as a result of this forum. A big thanks to cr0c0, Maus and Mogule who put a lot of effort into helping me with my code. The attached library is not the final product, I need to add more functions and examples yet (I may also need more help).

Thanks also goes to RobotFreak for his tutorial Arduino 101: Timers and Interrupts.

 

 


 

Currently I am trying to create a Library for use with Arduino. Unfortunately programming is not my specialty. I have been studying the library tutorials but unfortunately their attempts to "keep it simple" means they are often too simple to answer my questions. I've tried looking at the code of other libraries but it is not helping much.

My main question for now is a general C++ question.

I am creating a function in my library that generates 4 intergers, how do I pass these 4 integers back to the main routine without using global variables. The tutorials I have found indicate that a function can only return 1 variable which seems utterly pointless and mostly useless.

Do I need to create a data structure and if so, how? Links to tutorials much appreciated. I am trying to learn more about C++ programming structure as well as using interrupts, timers etc.

RobotFreak has written a good tutorial on Timers here: http://letsmakerobots.com/node/28278
However I am still struggling with using timer 2 to call an interrupt to my Library every 1mS to update sensor readings.

All help appreciated.

 


 

Update: 24th January 2012

After looking at my options, Rogue's suggestion as demonstrated by Mogul seemed the easiest to implement. My "Impact" funtion now looks like this:

 

volatile byte interruptflag;

void setup()
{
  // initialize timer2
  noInterrupts();                                      // disable all interrupts
  TCCR2A=0;
  TCCR2B=0;
  TCNT2 =0;

  OCR2A=31;                                            // compare match register 8MHz/256/1000Hz
  TCCR2B |= (1 << WGM22);                              // CTC mode
  TCCR2B |= (1 << CS22);                               // 256 prescaler
  TIMSK2 |= (1 << OCIE2A);                             // enable timer compare interrupt
  interrupts();                                        // enable all interrupts
  Serial.begin(57600);
}

void loop()
{
  int deltx,delty,deltz,magnitude;
  if (interruptflag==1)
  {
    interruptflag=0;
    magnitude=Impact(deltx,delty,deltz);
    if(magnitude>0)
    {
      Serial.print("\tMagnitude:");
      Serial.print(magnitude);
      Serial.print("\tDelta X:");
      Serial.print(deltx);
      Serial.print("\tDelta Y:");
      Serial.print(delty);
      Serial.print("\tDelta Z:");
      Serial.println(deltz);
      Serial.println("");     
    }
  }
}


ISR(TIMER2_COMPA_vect)                                 // timer compare interrupt service routine
{
  interruptflag=1;
}


int c;                                                  // counts program loops since last impact (1 loop / mS)
int x,y,z;                                              // x,y and z axis readings from accelerometer
#define sens 30                                         // sensitivity of impact detection
byte Impact(int &deltx, int &delty, int &deltz)         // Impact function header
{
  int oldx=x;                                           // local variables store previous axis readings
  int oldy=y;
  int oldz=z;

  x=analogRead(0);                                      // new axis readings are taken
  y=analogRead(1);
  z=analogRead(2);

  deltx=x-oldx;                                         // delta between old and new axis readings
  delty=y-oldy;
  deltz=z-oldz;
  int magnitude=sqrt(sq(deltx)+sq(delty)+sq(deltz));    // magnitude of delta x,y,z using pythagorus

  if (magnitude>sens && c<1)                            // has a new impact occured
  {
    c=500;                                              // reset loop counter
    return magnitude;                                   // return impact magnitude
  }
  else
  {
    c--;                                                // loop counter prevents false triggering 
    if(c<0) c=0;                                        // as robot vibrates due to impact
    return 0;
  }
}


I have now included a timer interrupt based on information posted by RobotFreak. Unfortunately the tutorial did not explain things well enought for me to fully understand the process but it was good enough that I could modify an example to use timer 2 to call my function roughly once every millisecond. I tested this originally by having it print micros() everytime the interrupt occured.

My only issue (not a big one) is that I had to use a global variable for the interrupt flag. I am trying to avoid global variables in my library. Is there a way around this?

 


 

Update: 25th of January

I have now tidied up my code a bit more and eliminated the interruptflag by calling the impact function directly from the ISR. This has given me a new problem as now magnitude, deltx, delty and deltz need to be global for my main routine to read them. My new code now looks like this:

 

volatile int magnitude,deltx,delty,deltz;

void setup()
{
  // initialize timer2
  noInterrupts();                                      // disable all interrupts
  TCCR2A=0;
  TCCR2B=0;
  TCNT2 =0;

  OCR2A=31;                                            // compare match register 8MHz/256/1000Hz
  TCCR2B |= (1 << WGM22);                              // CTC mode
  TCCR2B |= (1 << CS22);                               // 256 prescaler
  TIMSK2 |= (1 << OCIE2A);                             // enable timer compare interrupt
  interrupts();                                        // enable all interrupts
  Serial.begin(57600);
}

void loop()
{
  
  if(magnitude>0)
  {
    Serial.print("\tMagnitude:");
    Serial.print(magnitude);
    Serial.print("\tDelta X:");
    Serial.print(deltx);
    Serial.print("\tDelta Y:");
    Serial.print(delty);
    Serial.print("\tDelta Z:");
    Serial.println(deltz);
    Serial.println("");     
  }
  
}


ISR(TIMER2_COMPA_vect)                                 // timer compare interrupt service routine
{
  Impact(magnitude,deltx,delty,deltz);
}



void Impact(volatile int &magnitude, volatile int &deltx, volatile int &delty, volatile int &deltz)          // Impact function header
{
  volatile static int vibration;                      // counts program loops since last impact (1 loop / mS)
  volatile static int xaxis,yaxis,zaxis;              // x,y and z axis readings from accelerometer
#define SENSITIVITY 30                                // sensitivity of impact detection

  volatile int oldx=xaxis;                            // local variables store previous axis readings
  volatile int oldy=yaxis;
  volatile int oldz=zaxis;

  xaxis=analogRead(0);                                // new axis readings are taken
  yaxis=analogRead(1);
  zaxis=analogRead(2);
  
  if(vibration<500) magnitude=0;
  vibration--;                                        // loop counter prevents false triggering 
  if(vibration<0) vibration=0;                        // as robot vibrates due to impact
  if(vibration>0) return;                             // until vibration has subsided no further calculations required

  deltx=xaxis-oldx;                                   // delta between old and new axis readings
  delty=yaxis-oldy;
  deltz=zaxis-oldz;
  magnitude=sqrt(sq(deltx)+sq(delty)+sq(deltz));      // magnitude of delta x,y,z using pythagorus

  if (magnitude>SENSITIVITY)                           // has a new impact occured
  {
    vibration=500;                                      // reset loop counter for 500mS
    return;
  }
  else
  {
    magnitude=0;
  }
}

Once this is converted to a library, the "Impact" function will be called once every mS by the timer interrupt. This is all good except that now I seem to need to make my magnitude and axis deltas into global variables so that the main program can access them.

After thinking more about how I want this Library to work, I decided that as the impact function is being called by the timer then it is best to simply have it's results as global variables that can be accessed anytime by any other function.

I've now attempted to create my MicroM.h and MicroM.cpp files based on two tutorials I found on the Arduino website.

/******************************************
 *               MicroM.h                 *
 *                                        *
 *  a library to simplify the use of the  *
 *     DAGU Micro Magician Controller     *
 *                                        *
 *              written by                *
 *            Russell Cameron             *
 ******************************************/

#ifndef MicroM_h
#define MicroM_h

#include "Wprogram.h"

class MicroM
{
public:
  volatile int sensitivity,magnitude,deltx,delty,deltz;

private:
  volatile static int _vibration,_xaxis,_yaxis,_zaxis;
  volatile int _oldx,_oldy,_oldz;

  void _Setup();
  void _Impact(int sensitivity, volatile int &magnitude, volatile int &deltx, volatile int &delty, volatile int) ;
};

#endif /* MicroM_h */





/******************************************
 *              MicroM.cpp                *
 *                                        *
 *  a library to simplify the use of the  *
 *     DAGU Micro Magician Controller     *
 *                                        *
 *              written by                *
 *            Russell Cameron             *
 ******************************************/


#include "Wprogram.h"
#include "MicroM.h"



MicroM::_Setup()
{ 
  noInterrupts();                                      // disable all interrupts
  TCCR2A=0;                                            // initialize timer2
  TCCR2B=0;
  TCNT2 =0;

  OCR2A=31;                                            // compare match register 8MHz/256/1000Hz
  TCCR2B |= (1 << WGM22);                              // CTC mode
  TCCR2B |= (1 << CS22);                               // 256 prescaler
  TIMSK2 |= (1 << OCIE2A);                             // enable timer compare interrupt
  interrupts();                                        // enable all interrupts
  sensitivity=35;				       // sets a default sensitivity
}



MicroM::ISR(TIMER2_COMPA_vect)                         // timer compare interrupt service routine
{
  Impact(sensitivity,magnitude,deltx,delty,deltz);
}



MicroM::_Impact(int sensitivity, volatile int &magnitude, volatile int &deltx, volatile int &delty, volatile int &deltz)
{
  volatile static int _vibration;                     // counts program loops since last impact (1 loop / mS)
  volatile static int _xaxis,_yaxis,_zaxis;           // x,y and z axis readings from accelerometer

  volatile int _oldx=_xaxis;                          // local variables store previous axis readings
  volatile int _oldy=_yaxis;
  volatile int _oldz=_zaxis;

  _xaxis=analogRead(0);                               // new axis readings are taken
  _yaxis=analogRead(1);
  _zaxis=analogRead(2);

  _vibration--;                                       // loop counter prevents false triggering 
  if(_vibration<0) _vibration=0;                      // as robot vibrates due to impact
  if(_vibration>0) return;                            // until vibration has subsided no further calculations required

  deltx=_xaxis-_oldx;                                 // delta between old and new axis readings
  delty=_yaxis-_oldy;
  deltz=_zaxis-_oldz;

  magnitude=sqrt(sq(deltx)+sq(delty)+sq(deltz));        // magnitude of delta x,y,z using pythagorus

  if (magnitude>sensitivity)                          // has a new impact occured
  {
    _vibration=500;                                   // reset loop counter for 500mS
    return;
  }
  else
  {
    magnitude=0;
  }
}


keywords.txt
MicroM  KEYWORD1


As I was having trouble following the tutorials I would apreciate it if you can point out my mistakes. The tutorials I refer to are:

http://arduino.cc/it/Hacking/LibraryTutorial and http://arduino.cc/playground/Code/Library#API

 

AttachmentSize
MicroM.zip2.89 KB

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

From what I understand of the tutorials, this is my next step, to convert my sketch into a class.

Once it is a library the impact function will be invisible to the user except that their code can at any time see the resulting values Magnitude, Delta X, Delta Y and Delta Z.

I like it, but one detail slipped your eye.

The heaviest computation is done twice just after each other on impact, on the same data.

Also I wonder how much more space, if any, the compiled program consumes on the chip.

But newer the less, encapsulation is the way to go when writing larger programs. If larger programs should be on the Arduino is an other question.

It would be simple to change the code to perform the calculations a fewer number of times I think.  We could probably compute the magnatude once on sensor read for example.

If I remember correctly, C++ does have some overhead, so in that reguard it might not be the best solution for Atmel programming in general.  I've read tricks to do some "object oriented"-like programming with just straight C, which might be better overall in terms of programming size.

I'll have to try both programs in the Arduino IDE and see the overall program size as an exercise when I have time.

30K could contain quite a number of lines of code I suppose! :)

Maus

Thanks for the reply. Classes is another C trick I must learn. From what I have understood of the library tutorials this will be my next step once I have the code tidied up and working well. I will be trying this today and post an update tonight.

You could avoid the need for the interrupt flag global by letting the interrupt function call a ISR function in your class.

And you could move c, x, y and z inside the impact function and having them keep their values by making them static:

static int c, x, y, z;

Your code to handle magnitude and delay could also be done this way: (untested)

if (c) { c--; return 0; }

if (magnitude < sens)  return 0;

c=500;
return magnitude;

Not that its any better that your code, but it has a little different logic. Maybe you like it.

Thanks for the tip on static variables. I wish I'd known this sooner as it would have saved some debugging. As someone who grew up with Basic, C language can be confusing because little details like this are often overlooked in explanations.

Passing by reference may be a simple way to work it, it will require you call the function with the four integer variables, all you need to do is place an & in front of each variable in the fumction header. For example: void myFunction(int &var)

My program converted:

int myDeltaRand(int &value, int &delta)
{
  delta = random(5) - 2; // -2 to +2
 
  value += delta;

  if (abs(value) < 10)
    return 0;
  Serial.println("RESET");  
  delta = 0;
  value = 0;
  return 1; 
}

void loop()
{
  static int v;
  int d;
  int status = myDeltaRand(v, d);
  Serial.print("status: ");
  Serial.print(status);
  Serial.print(", v: ");
  Serial.print(v);
  Serial.print(", d: ");
  Serial.println(d);
  delay(100);
}

Its basically excactly the same, and they compile into excactly the same hex file.

I have used this method (see the update at the top) but it would be good if someone can explain how this and other methods work. What is the advantage of one method over another.

Looking at the code I have posted and my need for a global variable as an interrupt flag, is there a better way that will eliminate the need for a global variable?

 

Looks good OddBot.

One of the main differences between passing a parameter by reference versus via pointer is :

If you use the pointer you can pass NULL or no object, references do not allow this... they "want" to be strongly typed.  Pointers are just thingys which point to a memory location and can point to "nothing" as well (very loose).  So if you want your program more structured I would recommend passing by reference.  Usually, more structure means you can find bugs easier.  There are a few other nuances, which can be effected by ref vs pointer... operator overloading, etc.. but I'm not sure if Arduino C supports this anyway.

Your code look clear OB1..  One recommendation would be to go back and find anything that is named with a simple letter... and rename this to something more meaningful (refactoring).  For example, "c" might be renamed to magnitude_comparator..   Also, a convention in C is to have constant defines in all caps .. e.g. #define SENSE_MIN 30

I feel the naming of variables is very important.. when you leave the code for a while then come back to it because it is not working correctly, why go through the extra effort of trying to correlate single letters with their correct meaning?