Using millis() in my own library

Hi all, I am fairly new to Arduino and C/C++, although a veteran of embedded system from the days when assembler was the only option.

I am trying to create my own library function for non-blocking timing, using micros() and millis(). My function for checking elapsed time works perfectly when it’s inline code. But I just can’t manage to separate it out into a library. Whatever I try I get the error message:-

E:\Programs on 1TB\Arduino\libraries\My_non_blocking\src\Timer_NB.cpp:31:14: error: 'millis' was not declared in this scope

I have googled this thing to death, tried every #include (etc) I can think of, tried to reverse engineer every existing library file, slept on it, wept on it, and googled some more, and still not managed to resolve it. I can’t for the life of me work out why the millis() function is accessible from an .ino file but not from an included .cpp.

I’ve placed my wannabe library alongside the official libraries. The compile time diagnostics I get suggest my files are being found and processed. My timing code works fine if it’s just inline as part of my .ino file.

Here’s my .cpp timer file:

/************************************************************
    Call with:
      IntervalMicros  The IntervalMicros elapsed time in uS
       NextMicro  Pointer to an unsigned long, which is the caller's private time marker,

    Return true if IntervalMicros uS have passed, alter NextMicro to the new point in time.
*/

int TestElapsedMicros_NB(unsigned long IntervalMicros, unsigned long *NextMicro)
{
  if (micros() - *NextMicro >= IntervalMicros) {
    *NextMicro = *NextMicro + IntervalMicros;
    return true ;
  }
  else {
    return false;
  }
}

And my .h file

#ifndef Timer_NB_h
#define Timer_NB_h
#include <math.h>
#if (ARDUINO >= 100)
 #include <Arduino.h>
#else
 #include <WProgram.h>
 #include <pins_arduino.h>
#endif

//#include  "Arduino.h" 

#include "Timer_NB.cpp"

#endif

For completeness the rough program I am using as a test bed (dual stepper drive):

//=================== Rough stepper tests =============================
/******************************************************
    Template for superloop
*******************************************************/
//
// =====  Includes ====================
// e.g. /#include <Servo.h>

#include <Timer_NB.h>

// ===== Global decarations =============
// --- Objects from libraries ------
// e.g. Servo MyServo;

//--------- Global variables -----------
// e.g. float gfInitialTemperature;



// --- Constants -----------
// e.g. const float fPi = 3.1415926 ;
const float fFilter = 0.2;
const float fFilterDash = 1 - fFilter ;


// MOtor speed
const unsigned long MotorStepInterval_A_ = 3.7e3;  //  uS
const unsigned long MotorStepInterval_B_ = 2.7e3;  //  uS

// ---- I/O pins ------------------ 
// e.g. const int oRedLED = 2;
 const int oDebug = 6;
  const int oMotor_A_1 = A0;
  const int oMotor_A_2 = A1;
  const int oMotor_A_3 = A2;
  const int oMotor_A_4 = A3;

  const int oMotor_B_1 = 5;
  const int oMotor_B_2 = 4;
  const int oMotor_B_3 = 3;
  const int oMotor_B_4 = 2;


//// ---- Definitions for inline classes --------------
//// This section must be before anything is referenced. In a library it's the .h file

//============== the setup function runs once when you press reset or power the board ============
void setup() {
  Serial.begin(256000);
  Serial.println("hellooo World");
  // initialize I/O pins
  pinMode(oMotor_A_1, OUTPUT);
  pinMode(oMotor_A_2, OUTPUT);
  pinMode(oMotor_A_3, OUTPUT);
  pinMode(oMotor_A_4, OUTPUT);

  pinMode(oMotor_B_1, OUTPUT);
  pinMode(oMotor_B_2, OUTPUT);
  pinMode(oMotor_B_3, OUTPUT);
  pinMode(oMotor_B_4, OUTPUT);

  pinMode(oDebug, OUTPUT);
}

//================  the loop function runs over and over again forever ==================
void loop() {
 digitalWrite(oDebug, 1); 
  MotorStep_A_();
 digitalWrite(oDebug, 0); 
  MotorStep_B_();
}
// ----------------  Rough and ready, duplicate copies of the motor driver. Eventually this will be a proper class with 2 instances

// The stepper is a very simple state machine. It relies on the non-blocking TestElapsedMicros()
void MotorStep_A_() {
  static unsigned long Timer;
  static byte State = 0 ;
  if (TestElapsedMicros_NB(MotorStepInterval_A_, &Timer)) {
    switch (State) {
      case 0:
        motorWrite_A_(0, 1, 0, 0);
        State = 1;
        break;
      case 1:
        motorWrite_A_(0, 0, 1, 0);
        State = 2;
        break;
      case 2:
        motorWrite_A_(0, 0, 0, 1);
        State = 3;
        break;
      case 3:
        motorWrite_A_(1, 0, 0, 0);
        State = 0;
        break;

    }
  }
}

void motorWrite_A_(boolean w1, boolean w2, boolean w3, boolean w4 ) {
  digitalWrite(oMotor_A_1, w1); digitalWrite(oMotor_A_2, w2); digitalWrite(oMotor_A_3, w3); digitalWrite(oMotor_A_4, w4);
}




void MotorStep_B_() {
  static unsigned long Timer;
  static byte State = 0 ;
  if (TestElapsedMicros_NB(MotorStepInterval_B_, &Timer)) {
    switch (State) {
      case 0:
        motorWrite_B_(0, 1, 0, 0);
        State = 1;
        break;
      case 1:
        motorWrite_B_(0, 0, 1, 0);
        State = 2;
        break;
      case 2:
        motorWrite_B_(0, 0, 0, 1);
        State = 3;
        break;
      case 3:
        motorWrite_B_(1, 0, 0, 0);
        State = 0;
        break;

    }
  }
}

void motorWrite_B_(boolean w1, boolean w2, boolean w3, boolean w4 ) {
//  digitalWrite(oMotor_B_1, 0); digitalWrite(oMotor_B_2, 0); digitalWrite(oMotor_B_3, 0); digitalWrite(oMotor_B_4, 0);
  digitalWrite(oMotor_B_1, w1); digitalWrite(oMotor_B_2, w2); digitalWrite(oMotor_B_3, w3); digitalWrite(oMotor_B_4, w4);
}





//
///* General purpose elapsed time function, microseconds
//************************************************************
//    Call with:
//      IntervalMicros  The IntervalMicros elapsed time in uS
//       NextMicro  Pointer to an unsigned long, which is the caller's private time marker,
//
//    Return true if IntervalMicros uS have passed, alter NextMicro to the new point in time.
//
//  My first version of this did not advance the IntervalMicros, so it had cumulative error.
//  In a 20kHz square wave test program that one produced 16.6kHz when the correct frequency was 20kHz
//  (measured 19.998kHz on 'scope)
//*/
//boolean TestElapsedMicros(unsigned long IntervalMicros, unsigned long *NextMicro)
//{
//  if (micros() - *NextMicro >= IntervalMicros) {
//    *NextMicro = *NextMicro + IntervalMicros;
//    return true ;
//  }
//  else {
//    return false;
//  }
//}

Just to re-iterate: If I enable the built in timing function that works perfectly.

So where am I going wrong?

Your .cpp file needs to include the header. include Timer_NB.h in Timer_NB.cpp and it will get better. You may also need some prototypes for the functions you've defined in the .cpp in the .h. That will fix the next problem.

Does your cpp file include your .h file? I don't think it happens automatically.

BTW: I don't know that there's any reason to not include Arduino.h and simply leave it at that.

PaulMurrayCbr:
Does your cpp file include your .h file? I don't think it happens automatically.

BTW: I don't know that there's any reason to not include Arduino.h and simply leave it at that.

It does not.

The header file construct is to support pre-1.0 versions of the IDE. Personally, I think it is long past time to drop support for them.

Thank you guys for your inputs. I’ve tried everything you suggested, and more, and nothing works.

Here’s where I am up to: I discovered at Arduino - Libraries under Manual Installation that user (my own) libraries should be in the default sketch path. For me that is

E[color=blue]:\Documents\Arduino[/color]

So I moved my files there (from the Arduino library folder) and made sure the setting in the IDE matches (I had never bothered with that because I have most of my sketches in a DropBox folder elsewhere.

Then I made the include line (early in my ino file) this:

[color=blue]#include "E:\Documents\Arduino\libraries\My_non_blocking\src\Timer_NB.h" //Tell preprocessor  exactly where it is[/color]

That works; the test program compiles and executes as intended.

Then I trimmed it back to:

[color=blue]#include "Timer_NB.h" //let preprocessor find it[/color]

or

[color=blue]#include <Timer_NB.h>[/color]

Now I get the error output

[color=red]E:\Documents\Arduino\libraries\My_non_blocking\src\Timer_NB.cpp: In function 'bool TestElapsedMicros_NB(long unsigned int, long unsigned int*)':

E:\Documents\Arduino\libraries\My_non_blocking\src\Timer_NB.cpp:14:12: error: 'micros' was not declared in this scope

if (micros() - *NextMicro >= IntervalMicros) {[/color]

What that error report tells me is that it is finding my .h and presumably .cpp files in the very same folder as before, but suddenly spitting the dummy. Makes no sense to me.

So where am I going wrong? I am suspecting a bug or quirk in the IDE (1.8.0)

Not a bug.

Put them in E:/Documents/Arduino/Libraries/Timer_NB

Or put them directly in the folder with the sketch and use the “” include style instead of <>.

C/C++ not my forte, but the following compiles, whether it is correct or works is unknown.

One key point is that Arduino sketches are not valid C++ programs, because the setup and loop functions are not declared before they are used. In a "normal" C++ file, you have to
declare the function/function signature before you use it. C++ files in an Arduino library have to be valid C++ programs.

Small correction made to above.

mohangupta i thought your post would of set of some bells for sure.

arduino is not a language, the IDE can use C, C++ and ASM if you know what youre doing.

Im pretty sure both Setup() and Loop() are declared in "Arduino.h"

One key point is that Arduino sketches are not valid C++ programs

Nonsense. The IDE adds the necessary function prototypes. Or, you can add them.

Hey DavidSG
i started by getting a simple library, deleting all the code .h and .cpp and pasting all my code in its place

renamed the #ifndef blahblah_h at the top of the .h file to #ifndef my_new _name_h, my_new _name should be consistent with the file name. and the #include in the .cpp

declarations go in the header file

int TestElapsedMicros_NB();

definitions go in the cpp file

int TestElapsedMicros_NB(unsigned long IntervalMicros, unsigned long *NextMicro)
{
  if (micros() - *NextMicro >= IntervalMicros) {
    *NextMicro = *NextMicro + IntervalMicros;
    return true ;
  }
  else {
    return false;
  }
}

if anything is assigned a value you might want to try assigning it in the .h and then (not at the same time) the .cpp and see what happens

anything that is given a value in your .h file is included in all files that use the words #incude"mylibrary"
including #defines

put all your code into the copied/deleted library then comment it all out. uncomment one at a time and see what works.

you can have a declaration in a header file and no definition in the .cpp but not the othe way around.

although a library may compile (because it not getting used) doesnt mean it works, as you uncomment out your routines try and use them in other parts of code and see if you can break it, if you cant carry on and uncomment the next part