"does not name a type" error when I have multiple *.ino files and ALL of the code is moved into one

Hello,
From what I know it is allowed to have multiple *.ino files in a project and while compiling Arduino builder will concatenate all the files starting from the *.ino file named the same as parent directory and then all the rest files in alphabetical order.
Such information can be found for example here.

This pattern was working for me until I wanted to try one library from GitHub. I was getting a compilation error saying:

error: 'WiFiEventStationModeGotIP' does not name a type

when building the project so I experimented a bit but nothing helped.

So I finally copy-pasted the example without touching it into main file and removed everything else and it compiled just fine.

But when I created another *.ino file moved all the code into and and left "main" file empty the same error appears when compiling.

What's wrong here?

The process you describe is not everything that happens, I think the IDE is also generating stubs and trying to be clever when merging everything...

But it's probably trying too hard to be clever and fails including the proper definitions before using them

I'm pretty sure that if you were to keep the main part of the sketch in the main .ino file, such as:

#include <ESP8266WiFi.h>
#include <Ticker.h>
#include <AsyncMqttClient.h>

#define WIFI_SSID "My_Wi-Fi"
#define WIFI_PASSWORD "my-awesome-password"

#define MQTT_HOST IPAddress(192, 168, 1, 10)
#define MQTT_PORT 1883

AsyncMqttClient mqttClient;
Ticker mqttReconnectTimer;

WiFiEventHandler wifiConnectHandler;
WiFiEventHandler wifiDisconnectHandler;
Ticker wifiReconnectTimer;

void setup() {
  Serial.begin(115200);
  Serial.println();
  Serial.println();

  wifiConnectHandler = WiFi.onStationModeGotIP(onWifiConnect);
  wifiDisconnectHandler = WiFi.onStationModeDisconnected(onWifiDisconnect);

  mqttClient.onConnect(onMqttConnect);
  mqttClient.onDisconnect(onMqttDisconnect);
  mqttClient.onSubscribe(onMqttSubscribe);
  mqttClient.onUnsubscribe(onMqttUnsubscribe);
  mqttClient.onMessage(onMqttMessage);
  mqttClient.onPublish(onMqttPublish);
  mqttClient.setServer(MQTT_HOST, MQTT_PORT);

  connectToWifi();
}

void loop() {}

and move all the functions in a secondary .ino file then everything will work (that would correspond to the expected simple view of code structure the IDE seems to expect)


My view is to not rely on multiple .ino to structure code, use .h, .hpp or .cpp files and rely (as much as possible) on standard compilation rules (ie declare something before using it etc)

Create as many .ino's (tabs) as needed, remembering the main 1st tab must have the setup() and loop(). While this normally works, sometimes automatic function protyping has a hiccup.
When you move libraries (.h & .cpp files) remember to change the #includes:
#include <thislib.h>
becomes
#include "./thislib.h"

I have some projects with 10+ tabs. Nice side effect is you can Zip one folder and have a backup including working library code.

This isn't required. In fact, I've had better results and far less compilation errors when putting setup() and loop() in the last tab.

Sure but OP's example is proving that the last tab won't work all the time...

it all black magic voodoo thing the IDE is doing there :scream: :cold_face: and I think it is to best stay away from the non standard capability. There are solid best practices for having separate files when you compile and link, that's what should be used in my view.

@J-M-L moving the code as you suggested works.

Previously I was having loop and setup in my last tab/file as well and it was working just fine.

I experimented a lot more with this example and this is what is required to have in main file:

#include <ESP8266WiFi.h>
#include <Ticker.h>
#include <AsyncMqttClient.h>

WiFiEventHandler wifiConnectHandler;
WiFiEventHandler wifiDisconnectHandler;

void setup() {
  st();
}

void loop() {}

I put rest of the stuff into 2nd file and i put st() method into 3rd file, which has everything that previously was in setup().

I couldn't compile it with any possible combination of code split when setup wasn't in main file so I guess in some circumstances is a must.

I will try to learn working with .cpp + .h files, it looks tedious a bit, because I'm basically repeating myself in the header files and I need to update two places when I update my a function or something. Working with *.ino files I was able to define a class inside single file and modify it when I want to change something and use it in other files.

This applies only to local libraries, right? Not those installed from Arduino IDE, which are kind of "global".

Same here until I bumped into this.

Actually I may also consider (at least for some time) some stupid hack like manually concatenating my files with the rules described in the first post and building from CLI. I don't edit my code within Arduino IDE anyway. I just set board/port and compile/upload the code from it.

You can find out what the IDE exactly does by enabling verbose output during compilation in file -> preferences.

You will see lots of information; the relevant part to search for is your sketch name, e.g. Sweep.ino. One of the lines that contains this information is e.g. C:\Users\yourUserName\AppData\Local\Temp\arduino_build_629284\sketch\Sweep.ino.cpp.o. Navigate to the indicated (sketch) directory and you will find the file yourSketch.ino.cpp which is the file that is created by the Arduino builder and is the actual source file that goes through the 'compile' process.

One thing you need to know is that the compiler needs to be aware of any variable or function before it is used or called in the code. The Arduino builder sometimes drops a stitch and e.g. does not place a function prototype in the correct place (or at all) in the final CPP.

This simple sketch demonstrates the failure.

main sketch (MultipleIno.ino)

void setup()
{
  calc(3);
}


void loop()
{
  
}

tabOne.ino


int calc(int y)
{
  return x+y;
}

tabTwo.ino

int x = 0;

The error is

C:\Users\sterretje\Documents\Arduino\DemoCode\MultipleIno\tabOne.ino: In function 'int calc(int)':

tabOne:3: error: 'x' was not declared in this scope

   return x+y;

The resulting MultipleIno.ino.cpp

#include <Arduino.h>
#line 1 "C:\\Users\\sterretje\\Documents\\Arduino\\DemoCode\\MultipleIno\\MultipleIno.ino"
#line 1 "C:\\Users\\sterretje\\Documents\\Arduino\\DemoCode\\MultipleIno\\MultipleIno.ino"
#line 1 "C:\\Users\\sterretje\\Documents\\Arduino\\DemoCode\\MultipleIno\\MultipleIno.ino"
void setup();
#line 7 "C:\\Users\\sterretje\\Documents\\Arduino\\DemoCode\\MultipleIno\\MultipleIno.ino"
void loop();
#line 2 "C:\\Users\\sterretje\\Documents\\Arduino\\DemoCode\\MultipleIno\\tabOne.ino"
int calc(int y);
#line 1 "C:\\Users\\sterretje\\Documents\\Arduino\\DemoCode\\MultipleIno\\MultipleIno.ino"
void setup()
{
  calc(3);
}


void loop()
{
  
}


#line 1 "C:\\Users\\sterretje\\Documents\\Arduino\\DemoCode\\MultipleIno\\tabOne.ino"

int calc(int y)
{
  return x+y;
}


#line 1 "C:\\Users\\sterretje\\Documents\\Arduino\\DemoCode\\MultipleIno\\tabTwo.ino"
int x = 0;

You can see where the variable x is declared (after the calc function where it is used) and hence it's unknown in the calc function. Swapping the content of tabOne.ino and tabTwo.ino solves the problem in this case.

The most interesting lines are probably

#line 2 "C:\\Users\\sterretje\\Documents\\Arduino\\DemoCode\\MultipleIno\\tabOne.ino"
int calc(int y);

int calc(int y); is a so-called function prototype that tells the compiler what the function 'looks like'; if you e.g. omit the argument in the function call, the compiler can (and will) tell you that something is wrong. Sometimes the Arduino builder does not include that prototype.