Register forum user name Search FAQ

Gammon Forum

Notice: Any messages purporting to come from this site telling you that your password has expired, or that you need to "verify" your details, making threats, or asking for money, are spam. We do not email users with any such messages. If you have lost your password you can obtain a new one by using the password reset link.
 Entire forum ➜ Electronics ➜ Microprocessors ➜ How to avoid the quirks of the IDE sketch file pre-preprocessing

How to avoid the quirks of the IDE sketch file pre-preprocessing

Postings by administrators only.

Refresh page


Posted by Nick Gammon   Australia  (23,046 posts)  Bio   Forum Administrator
Date Sun 12 Oct 2014 08:21 PM (UTC)

Amended on Wed 21 Jan 2015 05:42 AM (UTC) by Nick Gammon

Message
Pre-processor problems


On the Arduino forum we get a steady stream of complaints that "valid C" (or "valid C++") does not compile under the Arduino IDE (Integrated Development Environment).

Example 1:


typedef void (*GeneralMessageFunction) ();
void checkPin (const int pin, GeneralMessageFunction response)
  {
  } 

void setup () { }
void loop () { }


Error:


sketch_oct11a:2: error: ‘GeneralMessageFunction’ has not been declared


Example 2:


template <typename T> T minimum (T a, T b) 
  {
  if (a < b)
    return a;
  return b;
  }  // end of minimum

void setup () 
  { 
  int foo = minimum (6, 7);
  }
void loop () { }


Error:


sketch_oct11a:2: error: ‘T’ does not name a type



Example 3:


void setup() { }
void loop() { }

#if 0
foo bar ()
{
}
#endif 


Error:


sketch_oct11a:4: error: ‘foo’ does not name a type





In each case the error is caused by the IDE attempting to generate function prototypes for you, and failing to do it properly. In the case of Example 3 it generated:


#line 1 "sketch_oct10d.ino"
#include "Arduino.h"
void setup();
void loop();
foo bar ();     // <-------------- problem line (line 4)
#line 1
void setup() { }
void loop() { }

#if 0
foo bar ()
{
}
#endif


However it should not have done so because the function "bar" is inside an #if 0 ... #endif block.

How to avoid this problem?


The solution is simple: Leave your main .ino file empty! Yes, that's it.

Make your "sketch" file empty, like this:

foo.ino:





Then use the IDE to make a new "tab" (down-pointing arrow in the top RH side of the window). Name this something sensible (like sketch.cpp). It has to be a .cpp file, eg.

sketch.cpp:


#include <Arduino.h>

// put your sketch here ...

void setup ()
  {

  }  // end of setup

void loop ()
  {

  }  // end of loop



Now, all of the above examples compile fine! Any .cpp files in the project are not subject to this "function prototype generation" procedure, and thus do not fail under certain circumstances.

Four important details


First, you need to have this line at the start of your .cpp file:


#include <Arduino.h>


That gives you prototypes for all of the Arduino functions (eg. digitalWrite etc.).

Second, if you use libraries (eg. Wire, SPI, EEPROM) then the #include lines for those libraries must go into the main sketch (in addition to any place you would normally put them), or you will get linker errors. This is because the IDE scans your main sketch file (and not the other files) to see which library files need to be included in the compilation. So you can regard your .ino file as a sort-of "Project" file which simply lists what libraries you are using.

So an example .ino file would be:

foo.ino:


#include <Wire.h>
#include <SPI.h>
#include <EEPROM.h>


Now the IDE knows to copy (and compile) those libraries as part of your project. Note that you can still omit any code from the .ino file, just list the libraries you are using.

Third, you can't name the .ino file with exactly the same name as the .cpp file or the IDE will complain. Make them have different names. In other words, you can't have foo.ino and foo.cpp.

Fourth, don't name your main file "main.cpp" as there already is a main.cpp file used by the IDE. (This implements the "main" function which calls setup and loop).


You can get rid of setup() and loop()


Also, if you don't like the setup / loop paradigm, you can go back to just using "main" like this:


#include <Arduino.h>

int main ()
  {
  init ();  // initialize timers
  Serial.begin (115200);
  Serial.println ("Hello, world");
  Serial.flush (); // let serial printing finish
  }  // end of main


A couple of traps there:


  • Call init() if you want to use things like millis(), micros(), and delay(), because that is where the hardware timers get initialized. This will also affect analogRead(), analogWrite(), and a few other things. Unless you know what you are doing, it is best to call init().

  • Call Serial.flush() when you are done (as shown) if you have been doing serial printing, otherwise interrupts will be turned off and the most recent serial prints may not be shown.


Also, on boards like the Arduino Leonardo / Micro / Esplora / Robot (boards based on the ATmega32u4 chip) you need to initialize the USB interface after calling init():


#if defined(USBCON)
	USBDevice.attach();
#endif

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

The dates and times for posts above are shown in Universal Co-ordinated Time (UTC).

To show them in your local time you can join the forum, and then set the 'time correction' field in your profile to the number of hours difference between your location and UTC time.


30,725 views.

Postings by administrators only.

Refresh page

Go to topic:           Search the forum


[Go to top] top

Information and images on this site are licensed under the Creative Commons Attribution 3.0 Australia License unless stated otherwise.