Arduino IDE suddenly asking for function prototypes

Hi guys,

Lately in one of my project, Arduino IDE complained that a function was not in the scope of the main file. It required the function prototype. Ok...

Now some of my students are required to add the function prototypes, but not all of them. I'm trying to understand why.

Has there been a change lately in the IDE?

Thanks

Update : I'm currently on version 2.2.1 on Windows on my Office computer and it's not complaining about function prototypes.

Do the functions requiring function prototypes have optional parameters ? I ask because I have experience of that causing function prototypes to be required

Generating the function prototypes depends on preproc.macros.flags in platform.txt file of the current arduino board package. Each arduino board has its own independent prototypes settings.
Therefore, if your students uses a different boards, it could explain why some of them experienced this issue and some not.

I've had to (or been lead to) add prototypes when there was some unrelated syntax error somewhere in the file.

Has a student with a working sketch who thought she needed prototypes tried removing them? Like if it was one of seven things that had to change before a success verify or upload, ppl would not typically go back and try removing one of the things that was on the path.

a7

I've seen this when using multiple .ino files within one project - are some of your students getting inventive? The placement of loop() and setup() anywhere but in the first file seemed to be problematic, though there may have been other underlying causes.

Pretty sure I've also seen it if I included a .cpp file in the project. The prototypes were not automatically generated in that case.

Every arduino project includes a lot of .cpp files - inside the arduino core.

Oh! I think you nailed it. I've tested with void test(int testParam = 100){....} and it gave this error:

C:\temp\labo_04\labo_04.ino: In function 'void gyroTask(long unsigned int)':
C:\temp\labo_04\labo_04.ino:136:3: error: 'test' was not declared in this scope
   test();
   ^~~~

exit status 1

Compilation error: 'test' was not declared in this scope

What I understand, the prototypes are the equivalent of the .h files. You declared your optional values in the header file. You don't do that in the implementation file (.cpp). Otherwise, it won't compile.

In brief, you need to declared a function prototype with the optional parameters and not in the function implementation.

void test (int testParam = 100);

void someFunc() {
  // ... code
  test();
  // ... code
}

void test (int testParam) {
  // ... code
}

Thanks!

Yes, but those have prototypes in the .h file.

Yes, the function prototypes have only been moved.

This prototype stuff is really getting crazy. I'm seeing this even on functions with no parameters, and other inconsistent behavior.

Recent experiences with 2.2.1 include

  • Code that compiled fine until recently on both 1.x and 2.x IDE versions is demanding prototypes for every function that's not defined before it is called.
  • A student with a fresh install of 2.2.1 and a fresh download of known working code can't compile without adding prototypes.
  • I can compile the code the student is using on my desktop with 2.2.1 with no problems. This is the same desktop where other code is demanding prototypes for simple functions with no arguments.

The program the student found this with is just a real time clock setter with two *.ino files and a README.md. There are no optional arguments anywhere, and no other files. Again, these are known working sketches used without trouble in several research groups and with no changes in over a year for the simplest sketch and 9 months for the other.

Now I'm afraid to reinstall on my desktop to try to replicate the problem the student is having because of yet another problem. A fresh install on a laptop of mine (fresh 2.x, it did have a 1.18.x version installed) refuses to even load the sketch, saying that it's not in a directory with a matching name. That is absolutely not true. I can't risk getting into that state on my main computer!

I want to update our GitHub repository with prototypes, but now I have two computers, one of which won't even load the sketch, and the other won't reproduce the problem.

Background: my compiles are on Windows 11. No sure about the student, but it's Windows. The code which is behaving differently on different installations of the same IDE version is at GitHub - VeloSteve/CBASS-Arduino-Sketch: Arduino code for a CBASS-R based CBASS with Bluetooth
the simpler of the two sketches is here
https://github.com/VeloSteve/CBASS-Arduino-Sketch/tree/main/CBASS_Clocksetter

@el_pablo Was this meant to be a reply to my post? Perhaps it's a coincidence that it followed mine rapidly when the previous post was 16 days ago, but please note that I'm seeing this unfortunate new behavior with no optional arguments.

I haven’t replied to your post.

Could the non loading examples have .pde file extensions? It happens when loading examples from outdated or old libraries.

Thanks for replying. Sorry I guessed wrong.

The RTC code has two *.ino files and a README.md. That's all.

The sketch is just one of the built-in examples extended to output to a display as well as the serial port. I apologize for not stripping this down to a minimal example, but it's still pretty simple one you get past the verbose approach and preprocessor directives to support more than one display type.

Here is the first file, CBASS_ClockSetter.ino:

// It is useful to have the sketch confirm success or failure.  This should always be visible on the Serial monitor, but
// also try to have it output on the most-used display types.
// Supported displays (do not edit without changing affected code):
#define LCD 1    // The original 2-line monochrome LCD
#define TFT 2    // TFT LCD, typically 320 x 240 color pixels.
#define NODISP 3

// The supported display to compile for at this time (edit as needed):
#define DISP TFT

#include <RTClib.h>
#if DISP == LCD
#include <Adafruit_RGBLCDShield.h>
  Adafruit_RGBLCDShield disp = Adafruit_RGBLCDShield();
#elif DISP == TFT
  // Library for the Adafruit TFT LCD Display
  #include <Adafruit_ILI9341.h>
  #define TFT_CS   7
  #define TFT_DC   6
  Adafruit_ILI9341 disp = Adafruit_ILI9341(TFT_CS, TFT_DC);  // The CS and DC pins.
#elif DISP == NODISP
#endif

RTC_DS1307 rtc;  // This works with the DS3231 as well.

char daysOfTheWeek[7][12] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};

int uploadLag = 8; // About how long it takes to upload the sketch and actually set the time, in seconds.

void setup() {
  Serial.begin(9600);
  delay(1000);
  Serial.println("ClockSetter");
  // If there is a physical display, start it and print a greeting there as well.
  startAnyDisplay();

  if (!rtc.begin()) {
    // This code may not be what I thought.  It returns true even if there is
    // no RTC!  The isrunning call will catch that case.
    showText("Couldn't start RTC");
    while (1);
  }
  if (!rtc.isrunning()) {
    showText("Failed to communicate with RTC.  Is one connected?");
    while (1);
  }
  showText("Found RTC"); // New 2/8/19
  // following line sets the RTC to the date & time this sketch was compiled
  rtc.adjust(DateTime(F(__DATE__), F(__TIME__)) + TimeSpan(0, 0, 0, uploadLag));
  Serial.print("Set RTC to ");
  Serial.print(F(__DATE__)); Serial.print("  ");Serial.println(F(__TIME__));
  Serial.println("Install another sketch now.");
  Serial.println("Do not reset the Arduino with ClockSetter installed, or the time will be wrong.");
}

DateTime now, oldNow;
void loop() {
  now = rtc.now();
  // Reduce flickering by only displaying when the seconds digit has changed.  Note that
  // DateTime only has seconds.
  if (now.second() != oldNow.second()) showTime(now);
  oldNow = now;
  delay(50);  // Check again after 50 ms.  Overkill, but too long a delay looks choppy.
}

and the second, Display.ino:

#if DISP == TFT
  #define TFT_WIDTH 320
  #define TFT_HEIGHT 240
  #define TFT_LAND 3  // Rotation for normal text  1 = SPI pins at top, 3 = SPI at bottom.
  #define TFT_PORT 2  // Rotation for y axis label, typically 1 less than TFT_LAND
  #define LINEHEIGHT 19 // Pixel height of size 2 text is 14.  Add 1 or more for legibility.
  #define BLACK   ILI9341_BLACK
  #define WHITE   ILI9341_WHITE
  
  void startTFTDisplay() {
      disp.begin();
      disp.setRotation(TFT_LAND); 
      disp.fillScreen(BLACK);
      // A start point for text size and color.  If changed elsewhere, change it back.
      disp.setTextSize(2);  // A reasonable size. 1-3 (maybe more) are available.
      disp.setTextColor(WHITE);
  }
#endif

/**
 * Start the display type expected at compile time.  It would be ideal to check for success and
 * give a warning if needed, but these functions return void.
 */
void startAnyDisplay() {
  #if DISP == LCD
    disp.begin(16, 2);              // Start the library.  Returns void
    disp.setCursor(0, 0);
    disp.print("ClockSetter LCD");
  #elif DISP == TFT
    startTFTDisplay();

    // The TFT display is big enough to put this message at the bottom and still use
    // top lines for other things.
    disp.setTextSize(1);
    disp.setCursor(0, TFT_HEIGHT - 2*LINEHEIGHT);
    disp.print("Now install the CBASS sketch.");
    disp.setCursor(0, TFT_HEIGHT - LINEHEIGHT);
    disp.print("Do not restart ClockSetter.");
    disp.setTextSize(2);

    disp.setCursor(0, 0);
    disp.print("ClockSetter TFT LCD");
  #elif DISP == NODISP
    startTFTDisplay();
    Serial.println("No supported display is expected on the device.");
    return;
  #endif
  Serial.println("If the intalled display matches compilation settings, you should see messages there.");
}

/**
 * This simply prints the given text on one line on the display.  The complexity
 * comes from the fact that different displays have different sizes, behaviors, and command names.
 */
void showText(const char *t) {
  // Always print to the serial monitor.
  Serial.println(t);
  #if DISP == NODISP
    return;
  #elif DISP == LCD
    disp.setCursor(0, 1);
    disp.print(t);
  #elif DISP == TFT
    // Determine the current display line and move to the start of the next, wrapping to the top
    // if needed.  This crudely assumes there are no headers or other special positions.
    int16_t yNow = disp.getCursorY();
    if (TFT_HEIGHT - yNow - LINEHEIGHT > 0) {
      disp.setCursor(0, yNow + LINEHEIGHT);
    } else {
      disp.setCursor(0, 0);
    }
    disp.print(t);
  #endif
}


/** Print the time to the display.  For now this assumes that successive prints with no cursor change
 *  pick up from the most recent x location, but always at the same y (no automatic line feed).
 *  Place the time on a fixed text line, regardless of what else is on the screen.  For LCD use the 2nd of 2 lines for time and the first
 *  for the date.
 *  For TFT use the 4th line for time and the 3rd for date.
 *  These are printed in reverse order because prompt time updates take priority over the date.
 */
void showTime(const DateTime now) {
  #if DISP == NODISP
    return;
  #elif DISP == LCD
    disp.setCursor(0, 1);
    disp.print("          ");
    disp.setCursor(0, 1);
  #elif DISP == TFT    
    disp.fillRect(0, 3*LINEHEIGHT, TFT_WIDTH, LINEHEIGHT, BLACK);
    disp.setCursor(0, 3*LINEHEIGHT); 
  #endif

  // Time
  disp.print(now.hour());
  disp.print(':');
  if (now.minute() < 10) disp.print("0");
  disp.print(now.minute());
  disp.print(':');
  if (now.second() < 10) disp.print("0");
  disp.print(now.second());
  disp.print("   ");

  // Date
  #if DISP == LCD
    disp.setCursor(0, 0);
  #elif DISP == TFT
    disp.setCursor(0, 2*LINEHEIGHT);
    disp.print("                ");
    disp.setCursor(0, 2*LINEHEIGHT);
  #endif
  disp.print(daysOfTheWeek[now.dayOfTheWeek()]);
  disp.print(", ");
  disp.print(now.day());
  disp.print('/');
  disp.print(now.month());
  disp.print('/');
  disp.print(now.year());
}

If you always supply your own function prototypes, there will never be a problem. I don't see this as a burden as you must do it when writing C++ in any other development environment.

It's true that creating headers is normal in C or C++, but it is still an unfortunate burden for beginners with Arduino. It is also an added task for experienced Arduino programmers who have been writing sketches for the last 10 years without this requirement, and now they have to rework old code.

When users ask for advanced functionality in the Arduino IDE the counterargument is often "This is a tool for beginners. Use command line tools or your own build process if you want more." Now when something comes along that hurts beginners, the answer is "You are a C++ developer, right? Just do what you do elsewhere."

Even the first explanation of how to use functions at arduino.cc does not include a prototype.

The entire sketch would then look like this:

void setup(){
  Serial.begin(9600);
}

void loop() {
  int i = 2;
  int j = 3;
  int k;

  k = myMultiplyFunction(i, j); // k now contains 6
  Serial.println(k);
  delay(500);
}

int myMultiplyFunction(int x, int y){
  int result;
  result = x * y;
  return result;
}

Why would it when the title of the page is

Using Functions in a Sketch

Learn how to define and use functions in a Sketch.

Note : my emphasis of Sketch in both places

I thought "sketch" was short for "any code written specifically for an Arduino in the variant of C++ it uses".

Reflecting, I've never seen a definition of the word and just picked it up from context. Looking now at the web site I see

A sketch is the name that Arduino uses for a program. It's the unit of code that is uploaded to and run on an Arduino board.

Does that somehow not include a "unit of code" that happens to have a few functions defined after setup() and loop()?

Unrelated to the problem with function prototypes.
The github file has the directory CBASS_Clocksetter which contains the sketch CBASS_ClockSetter.ino. On a case-sensitive operating system the IDE wants to place the sketch in its own folder because of the difference in capitalization.

Wow! Thanks for spotting that. Oddly, the copy on my computer has matched capitalization but GitHub desktop shows the problem you spotted. I suppose that directory capitalization isn't included in the diffs. I'll fix the repository so new downloads are correct.

Thanks again!