ESP32 project for unknown wifi network

Hi all,

I have a bit of a problem and i hope some of you can help me out, maby send me in the right direction.

I have a working project (chicken coop automation) on an ESP32, The ESP does all the work and communicates over my Wifi network with an Android app i have written with MIT App Inventor. So far so good, works like a charm.

The ESP has a static IP address and sends data to the Android app through the webserver (i post some data in a big string and the Android app cuts that up and deals with the data).
The Android app sends it's data through HTTP as well (http://ipadress/keyword with or without data).

Everything works as i would like but i would like to add 1 more addition to my project.
I would like to add the ability to use this on any wifi network.
For my working prototype i have the ESP make contact with my router and collect the IP, Gateway and Subnet. I hardcode this in the sketch, i can also hardcode the IP address in the Android app. So this works perfectly, but this will be a problem if i want to build one of these for someone else. I can't get their IP, Gateway and Subnet so i can't hardcode this in the sketch.

I have tried to write a sketch myself and that works..... allmost. It's probably way too complicated (over750 lines of code). The ESP starts from "factory default"in AP mode and waits for a reply from the Android app, on the Android app i can enter the SSID and password for my wifi network. When the ESP receives this information and stores this on an SD card, it restarts and uses the SSID and password from the SD to make contact with the wifi network and get's the IP, Gateway and Subnet and stores this on the SD card. ESP then restarts again in AP mode and sends the static IP to the Android app. After confirmation of receiving the IP, the ESP restarts again, makes contact with the local wifi and should be ready to go. The Android app had the IP it needs to contact the ESP, so everybody happy.... But this doesn't work.

I think i did find out why.
The IP, Gateway and Subnet should be declared before Setup().
And there seems to be no way to get the data from the SD card before Setup().
Soi'm kind of stuck on this one.

Then i have tried to use the Wifimanager library (Tzapu).
This should do everything i need, but i can't get that to work just yet.
It seems to work somewhat the same as my idea. It set's up a website where you can input the SSID and password. The ESP then restarts and is connected to the wifi network.
But there seems to be no way to get the IP address to the Android app. and it sets up a mqtt server and i have no knowlage of this. At all. Also, you still have to give this library a static IP hardcoded in the sketch, this is a problem because i need to be in the IP range of the enduser.

As far as i can google around and search forums, i think there is no solution to my problem.
THere seems to be no way to set the IP parameters after Setup() is started, and before Setup() starts, there seems to be no way to get stored data from the SD card (or the EEPROM).

I assume that most of you out there are way smarter then me with coding and networking.
So maby i'm overlooking some way to solve one of the two options i need. Or maby there could be a completely different solution i haven't though of.

Thank you for your time.

Below the code from my attempt to solve this problem (this doesn't work by the way)..
it is a bit lengthy and there is still alot of junk in there, sorry for that...

//First Contact
//
//Setup and check SD card for information.
//SSID and WW found, then start normal  mode (or send message to Serial Monitor).
//Else...
//Set up own Wifi AP mode.
//Reiceive local SSID and WW from Android.
//Connect to local Wifi and receive IP (to know the range).
//Create static IP based on received IP.
//Set up onw Wifi againin AP mode and send the static IP to website (Android).
//Wait for conformation from Android that the static IP is received.
//Connect to local IP and start normal operation (or send message to Serial Monitor).
//


//Libraries for SD card communication
#include "FS.h"
#include "SD.h"
#include "SPI.h"

//Libraries for Wifi
#include <WiFi.h>
#include <WebServer.h>

//Variables for SSID and Password for local Wifi
String WiName = "";
String WiPass = "";
String RecSSID;
String RecPass;
const char* RecSSID2;
const char* RecPass2;
int locIP1;
int locIP2;
int locIP3;
int locIP4;
int locSub1;
int locSub2;
int locSub3;
int locSub4;
int locGat1;
int locGat2;
int locGat3;
int locGat4;

//Constants for SSID and Password for AP mode
const char* ap_ssid = "ESPP32";  // Enter SSID here
const char* ap_password = "adminadmin";  //Enter Password here
// IP Address details for AP mode
IPAddress ap_ip(192,168,50,1);
IPAddress ap_gateway(192,168,50,1);
IPAddress ap_subnet(255,255,255,0);
WiFiServer server(80);
bool serverOnAP = false;

//Variable for status of local Wifi information
int wifiStatus = 0;

//Variables for Website
String reply = "Connected";
String req = "";
String reqType = "";
int comma;


//Variables for button and led
// set pin numbers
const int buttonPin = 4;  // the number of the pushbutton pin
const int ledPin =  17;    // the number of the LED pin
int blinkTime = 500;      // Blinktime Delay Time
unsigned long previousMillis = 0;  //variables for timer
unsigned long currentMillis = 0;
int buttonState = 0;    // variable for storing the pushbutton status 
long buttonTimer = 0;     //Records time the button was pressed
long longPressTime = 5000; //Time for long time press (5000 = 5 sec)
long wifiConnectionFail = 30000;  //time (30 sec) for trying to connect to wifi first time
boolean buttonActive = false;   //bools for long press button detection
boolean longPressActive = false;

//Variables for wifiStatus2
String fullSet = "";
String statIp1 = "";
String statIp2 = "";
String statIp3 = "";
String statIp4 = "";

//Variables for SD card
//String message = "";


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

  pinMode(buttonPin, INPUT);    // initialize the pushbutton pin as an input
  pinMode(ledPin, OUTPUT);      // initialize the LED pin as an output
  
  // Check if SD card is mounted
  if(!SD.begin()){
    Serial.println("Card Mount Failed");
    return;
  }

  //Check if SD card is attached
  uint8_t cardType = SD.cardType();
  if(cardType == CARD_NONE){
    Serial.println("No SD card attached");
    return;
  }

//writeFile(SD, "/LocalWifi.txt", "SSID\n");
//appendFile(SD, "/LocalWifi.txt", "Pass\n");
readFileWifi(SD, "/LocalWifi.txt");   // Read the SD for LocalWifi SSID and Password information
readFileStatus(SD, "/wifiStatus.txt"); // Read the SD for wifiStatus
//deleteFile(SD, "/LocalWifi.txt");

//  writeFile(SD, "/hello.txt", "Hello ");
//  appendFile(SD, "/hello.txt", "World!\n");
//  readFile(SD, "/hello.txt");
//  deleteFile(SD, "/foo.txt");
//  renameFile(SD, "/hello.txt", "/foo.txt");


//Check Wifi information status
  if (wifiStatus == 0){   // No Wifi info on SD card, lets get some.
    if (serverOnAP == false){   // only start webserver once
       //Setup Wifi details
       WiFi.softAP(ap_ssid, ap_password);
       WiFi.softAPConfig(ap_ip, ap_gateway, ap_subnet);
       delay(100);

       //Start server
       server.begin();
       Serial.println("HTTP server started"); 
       serverOnAP = true;
    }
  }
  
  if (wifiStatus == 1){   //We have SSID and password, lets get the local ip range and set our static ip

    readFileFirst(SD, "/firstWifi.txt");
    
    WebServer server(80);
    Serial.print("Try connecting to ");
    Serial.println(WiName);
    
    WiFi.begin(WiName.c_str(), WiPass.c_str());
    
      // Check wi-fi is connected to wi-fi network
      previousMillis = millis();    //set timer before trying to connect to local wifi
      while (WiFi.status() != WL_CONNECTED) {
      delay(1000);
      Serial.print(".");
      currentMillis = millis();   //after 30 sec blink led 10 times and hard reset because wifi info is probablly wrong
        if (currentMillis - previousMillis >= wifiConnectionFail){ 
          blinkyBlinky(10, blinkTime); // 10 is number of blinks
          deleteFile(SD, "/LocalWifi.txt");   
          deleteFile(SD, "/wifiStatus.txt"); 
          deleteFile(SD, "/firstWifi.txt");
          Serial.println("Resetting ESP to factory default in 1 second!!");
          delay(1000);  
          ESP.restart();
        }
      }
    Serial.println("");
    Serial.println("WiFi connected successfully");
    Serial.print("Got IP: ");
    Serial.println(WiFi.localIP());  //Show local IP on serial

    IPAddress broadCast =WiFi.localIP();    // Cut up ip adress in seperate parts
     int ip1 = broadCast[0];
    Serial.println(ip1);
     int ip2 = broadCast[1];
    Serial.println(ip2);
     int ip3 = broadCast[2];
    Serial.println(ip3);
     int ip4 = broadCast[3];
    Serial.println(ip4);

    Serial.println("");
    Serial.print("Got Subnet: ");
    Serial.println(WiFi.subnetMask());  //Show local Subnet on serial
    
    IPAddress broadCastSubnet = WiFi.subnetMask();
    int sub1 = broadCastSubnet[0];
    Serial.println(sub1);
    int sub2 = broadCastSubnet[1];
    Serial.println(sub2);
    int sub3 = broadCastSubnet[2];
    Serial.println(sub3);
    int sub4 = broadCastSubnet[3];
    Serial.println(sub4);

    Serial.println("");
    Serial.print("Got Gateway: ");
    Serial.println(WiFi.gatewayIP());  //Show local Gateway on serial

    IPAddress broadCastGateway = WiFi.gatewayIP();
    int gat1 = broadCastGateway[0];
    Serial.println(gat1);
    int gat2 = broadCastGateway[1];
    Serial.println(gat2);
    int gat3 = broadCastGateway[2];
    Serial.println(gat3);
    int gat4 = broadCastGateway[3];
    Serial.println(gat4);

    //Change int to String to char and write to SD file
    appendFile(SD, "/firstWifi.txt", ",");    //Write Ip adress to SD
    char _ip1[sizeof(ip1)];
    String __ip1 = String(ip1);
    __ip1.toCharArray(_ip1, sizeof(_ip1));
    appendFile(SD, "/firstWifi.txt",_ip1);
    appendFile(SD, "/firstWifi.txt", ",");
    char _ip2[sizeof(ip2)];
    String __ip2 = String(ip2);
    __ip2.toCharArray(_ip2, sizeof(_ip2));
    appendFile(SD, "/firstWifi.txt",_ip2);
    appendFile(SD, "/firstWifi.txt", ",");
    char _ip3[sizeof(ip3)];
    String __ip3 = String(ip3);
    __ip3.toCharArray(_ip3, sizeof(_ip3));
    appendFile(SD, "/firstWifi.txt",_ip3);
    appendFile(SD, "/firstWifi.txt", ",");
    char _ip4[sizeof(ip4)];
    String __ip4 = String(ip4);
    __ip4.toCharArray(_ip4, sizeof(_ip4));
    appendFile(SD, "/firstWifi.txt",_ip4);

    appendFile(SD, "/firstWifi.txt", ",");    // Write Subnetmask to SD
    char _sub1[sizeof(sub1)];
    String __sub1 = String(sub1);
    __sub1.toCharArray(_sub1, sizeof(_sub1));
    appendFile(SD, "/firstWifi.txt",_sub1);
    appendFile(SD, "/firstWifi.txt", ",");
    char _sub2[sizeof(sub2)];
    String __sub2 = String(sub2);
    __sub2.toCharArray(_sub2, sizeof(_sub2));
    appendFile(SD, "/firstWifi.txt",_sub2);
    appendFile(SD, "/firstWifi.txt", ",");
    char _sub3[sizeof(sub3)];
    String __sub3 = String(sub3);
    __sub3.toCharArray(_sub3, sizeof(_sub3));
    appendFile(SD, "/firstWifi.txt",_sub3);
    appendFile(SD, "/firstWifi.txt", ",");
    char _sub4[sizeof(sub4)];
    String __sub4 = String(sub4);
    __sub4.toCharArray(_sub4, sizeof(_sub4));
    appendFile(SD, "/firstWifi.txt",_sub4);

    appendFile(SD, "/firstWifi.txt", ",");    //Write Gateway to SD
    char _gat1[sizeof(gat1)];
    String __gat1 = String(gat1);
    __gat1.toCharArray(_gat1, sizeof(_gat1));
    appendFile(SD, "/firstWifi.txt",_gat1);
    appendFile(SD, "/firstWifi.txt", ",");
    char _gat2[sizeof(gat2)];
    String __gat2 = String(gat2);
    __gat2.toCharArray(_gat2, sizeof(_gat2));
    appendFile(SD, "/firstWifi.txt",_gat2);
    appendFile(SD, "/firstWifi.txt", ",");
    char _gat3[sizeof(gat3)];
    String __gat3 = String(gat3);
    __gat3.toCharArray(_gat3, sizeof(_gat3));
    appendFile(SD, "/firstWifi.txt",_gat3);
    appendFile(SD, "/firstWifi.txt", ",");
    char _gat4[sizeof(gat4)];
    String __gat4 = String(gat4);
    __gat4.toCharArray(_gat4, sizeof(_gat4));
    appendFile(SD, "/firstWifi.txt",_gat4);

    //Set wifiStatus to 2 and write to file
    deleteFile(SD, "/wifiStatus.txt");
    writeFile(SD, "/wifiStatus.txt", "2");
    
    //Restart ESP
  Serial.println("Restarting ESP in 5 seconds !!!");
  delay(5000);
  ESP.restart();
   
  }
  if (wifiStatus == 2){   //We have info, but need to sync with Android. Almost done.
    
    readFileIP(SD, "/firstWifi.txt");
    
    if (serverOnAP == false){   // only start webserver once
       //Setup Wifi details
       WiFi.softAP(ap_ssid, ap_password);
       WiFi.softAPConfig(ap_ip, ap_gateway, ap_subnet);
       delay(100);

       //Start server
       server.begin();
       Serial.println("HTTP server started"); 
       serverOnAP = true;
    }
  }
  if (wifiStatus == 3){   // We have info and Android is ready, lets rock and roll.
      
      readFileWifi(SD, "/LocalWifi.txt");

      // Setting Static IP.
         IPAddress local_IP(locIP1, locIP2, locIP3, locIP4);
         IPAddress gateway(locGat1, locGat2, locGat3, locGat4);
         IPAddress subnet(locSub1, locSub2, locSub3, locSub4); 
        
      WebServer server(80);
      // Setting Static IP.
        if (!WiFi.config(local_IP, gateway, subnet)) {
          Serial.println("Error in configuration.");
        }
      Serial.print("Try connecting to ");
      Serial.println(WiName);
    
      WiFi.begin(WiName.c_str(), WiPass.c_str());
    
      // Check wi-fi is connected to wi-fi network
      previousMillis = millis();    //set timer before trying to connect to local wifi
      while (WiFi.status() != WL_CONNECTED) {
      delay(1000);
      Serial.print(".");
      currentMillis = millis();   //after 30 sec ...... Just give up, need to reset with button
        if (currentMillis - previousMillis >= wifiConnectionFail){ 
          break;
        }
      }

      Serial.println("");
      Serial.println("WiFi connected successfully");
      Serial.print("Got IP: ");
      Serial.println(WiFi.localIP());  //Show local IP on serial
        
  }

}





void loop(){

  checkButton();    //check if button ispressed for reset or factory default

  if (wifiStatus == 0){   // No Wifi info on SD card, lets get some from android
    checkReply();  
  }
  
  //if (wifiStatus == 1){   // Make contact to local wifi and use ip to create own static ip adress
  // 
  //}
  
  if (wifiStatus == 2){   // Give the new static IP to Android and wait for reply
    checkReply();
  }
  if (wifiStatus == 3)    //We have all we need and should be connected to local wifi with static IP
    checkReply();
    //blinkyBlinky(5, 100); // 2 is number of blinks 
    //delay(2000);
}



void checkButton(){

  //Check button for short or long press
  if (digitalRead(buttonPin) == HIGH) {

    if (buttonActive == false) {
      buttonActive = true;
      buttonTimer = millis();
    }

    if ((millis() - buttonTimer > longPressTime) && (longPressActive == false)) {
      longPressActive = true;
      //Long button press action
       blinkyBlinky(5, blinkTime); // 5 is number of blinks
       deleteFile(SD, "/LocalWifi.txt");   
       deleteFile(SD, "/wifiStatus.txt"); 
       deleteFile(SD, "/firstWifi.txt");
       Serial.println("Resetting ESP to factory default in 1 second!!");
       delay(1000);  
       ESP.restart();
    }

  } else {
    if (buttonActive == true) {
      if (longPressActive == true) {
        longPressActive = false;

      } else {
        //Short button press action
         blinkyBlinky(2, blinkTime); // 2 is number of blinks 
         Serial.println("Resetting ESP in 1 second!!");
         delay(1000);  
         ESP.restart();
      }
      
      buttonActive = false;

    }
  }
  
}



void blinkyBlinky(int repeats, int time)
{
  for (int i = 0; i < repeats; i++)
  {
    digitalWrite(ledPin, HIGH);
    delay(blinkTime);
    digitalWrite(ledPin, LOW);
    delay(blinkTime);
  }
}




void readFileIP(fs::FS &fs, const char * path){
  Serial.printf("Reading file: %s\n", path);

  File file = fs.open(path);
  if(!file){
    Serial.println("Failed to open file for reading");    //No file exists so we need info from Android
    wifiStatus = 0;
    return;
  }

  Serial.println("Read from file: ");     //Read the Static IP from SD card
  while(file.available()){
    String temp = "";                     // String to hold the data we don't need
    temp = file.readStringUntil(',');
    Serial.println(temp);
    temp = file.readStringUntil(',');
    Serial.println(temp);
    statIp1 = file.readStringUntil(',');  // this should be the location for the local ip adress
    statIp2 = file.readStringUntil(',');
    statIp3 = file.readStringUntil(',');
    statIp4 = file.readStringUntil(',');
    temp = file.readStringUntil('\n');
    reply = "Static IP = " + statIp1 + "." + statIp2 + "." + statIp3 + "." + statIp4;
    Serial.print("");
    Serial.print("Static IP Taken from SD card in wifiStatus 2 : ");
    Serial.println(reply);
  }
  file.close();
}



void readFileWifi(fs::FS &fs, const char * path){
  Serial.printf("Reading file: %s\n", path);

  File file = fs.open(path);
  if(!file){
    Serial.println("Failed to open file for reading");    //No file exists so we need info from Android
    wifiStatus = 0;
    return;
  }

  Serial.println("Read from file: ");     //Read the SSID and Password from SD card
  String temp;
  while(file.available()){
      WiName = file.readStringUntil(',');
      Serial.println(WiName);
      WiPass = file.readStringUntil(',');
      Serial.println(WiPass); 
      
      temp = file.readStringUntil(',');
      locIP1 = temp.toInt();
      Serial.print("IP = ");
      Serial.print(locIP1);
      Serial.print(".");
      temp = file.readStringUntil(',');
      locIP2 = temp.toInt();
      Serial.print(locIP2);
      Serial.print(".");
      temp = file.readStringUntil(',');
      locIP3 = temp.toInt();
      Serial.print(locIP3);
      Serial.print(".");
      temp = file.readStringUntil(',');
      locIP4 = temp.toInt();
      Serial.println(locIP4);
      
      temp = file.readStringUntil(',');
      locSub1 = temp.toInt();
      Serial.print("Subnet = ");
      Serial.print(locSub1);
      Serial.print(".");
      temp = file.readStringUntil(',');
      locSub2 = temp.toInt();
      Serial.print(locSub2);
      Serial.print(".");
      temp = file.readStringUntil(',');
      locSub3 = temp.toInt();
      Serial.print(locSub3);
      Serial.print(".");
      temp = file.readStringUntil(',');
      locSub4 = temp.toInt();
      Serial.println(locSub4);

      temp = file.readStringUntil(',');
      locGat1 = temp.toInt();
      Serial.print("Gateway = ");
      Serial.print(locGat1);
      Serial.print(".");
      temp = file.readStringUntil(',');
      locGat2 = temp.toInt();
      Serial.print(locGat2);
      Serial.print(".");
      temp = file.readStringUntil(',');
      locGat3 = temp.toInt();
      Serial.print(locGat3);
      Serial.print(".");
      temp = file.readStringUntil(',');
      locGat4 = temp.toInt();
      Serial.println(locGat4);
      
      wifiStatus = 3;
  }
  file.close();
}


void readFileStatus(fs::FS &fs, const char * path){
  Serial.printf("Reading file: %s\n", path);

  File file = fs.open(path);
  if(!file){
    Serial.println("Failed to open file for reading");    //No file exists so we need info from Android
    wifiStatus = 0;
    return;
  }

  Serial.println("Read from file: ");     //Read the wifiStatus from SD card
  while(file.available()){
      String forNow = file.readStringUntil('\n');
      wifiStatus = forNow.toInt();
      Serial.println("wifiStatus = ");
      Serial.println(wifiStatus);
  }
  file.close();
}



void readFileFirst(fs::FS &fs, const char * path){
  Serial.printf("Reading file: %s\n", path);

  File file = fs.open(path);
  if(!file){
    Serial.println("Failed to open file for reading");    //No file exists so we need info from Android
    wifiStatus = 0;
    return;
  }

  Serial.println("Read from file: ");     //Read the SSID and Password from SD card
  while(file.available()){
      String forNow2 = file.readStringUntil('\n');
      Serial.print("forNow2 = ");
      Serial.println(forNow2);
        int i1 = forNow2.indexOf(',');
        Serial.print("i1 = ");
        Serial.println(i1);
        WiName = forNow2.substring(0, i1);
        WiPass = forNow2.substring(i1 + 1);
        Serial.println(WiName);
        Serial.println(WiPass);     
  }
  file.close();
}




void writeFile(fs::FS &fs, const char * path, const char * message){
  Serial.printf("Writing file: %s\n", path);

  File file = fs.open(path, FILE_WRITE);
  if(!file){
    Serial.println("Failed to open file for writing");
    return;
  }
  if(file.print(message)){
    Serial.println("File written");
  } else {
    Serial.println("Write failed");
  }
  file.close();
}



void appendFile(fs::FS &fs, const char * path, const char * message){
  Serial.printf("Appending to file: %s\n", path);

  File file = fs.open(path, FILE_APPEND);
  if(!file){
    Serial.println("Failed to open file for appending");
    return;
  }
  if(file.print(message)){
      Serial.println("Message appended");
  } else {
    Serial.println("Append failed");
  }
  file.close();
}



void deleteFile(fs::FS &fs, const char * path){
  Serial.printf("Deleting file: %s\n", path);
  if(fs.remove(path)){
    Serial.println("File deleted");
  } else {
    Serial.println("Delete failed");
  }
}



void renameFile(fs::FS &fs, const char * path1, const char * path2){
  Serial.printf("Renaming file %s to %s\n", path1, path2);
  if (fs.rename(path1, path2)) {
    Serial.println("File renamed");
  } else {
    Serial.println("Rename failed");
  }
}




void checkReply(){

  // Check if a client has connected..
  WiFiClient client = server.available();
  if (!client) {
    delay(100);
    return;
  }
   
  Serial.print("New client: ");
  Serial.println(client.remoteIP());
   
  
/////////////////////////////////////////////////////
  // Read information sends by client.
  req = client.readStringUntil('\r');
  Serial.println(req);
  req.replace("+", " ");          //Remove not used parts
  req.replace(" HTTP/1.1", "");   
  req.replace("GET /", "");       

  Serial.print("Reply from app: ");
  Serial.println(req);

 // Make the client's request.

   if(req.startsWith("NamePass")){    //SSID and Password received?
    funcNamePass();
   }
   
   if(req.startsWith("IPReceived")){  //Static IP received by Android?
    renameFile(SD, "/firstWifi.txt", "/LocalWifi.txt");
    //Set wifiStatus to 3 and write to file
      deleteFile(SD, "/wifiStatus.txt");
      writeFile(SD, "/wifiStatus.txt", "3");
    
    //Restart ESP
      Serial.println("Restarting ESP in 5 seconds !!!");
      delay(5000);
      ESP.restart();
   }

   if(req.startsWith("blink")){   // only for testing connection
    blinkyBlinky(3, blinkTime); // 3 is number of blinks 
    req = "";  //Empty the String req to make shure we only handle the request on time 
   }
       
           
  //////////////////////////////////////////////
  // Página WEB. ////////////////////////////
  client.println("HTTP/1.1 200 OK");
  client.println("Content-Type: text/html");
  client.println(""); //  We seem to need this
  client.println(reply); //  Return status.               
 

  client.flush();
  client.stop();
  Serial.println("Client disconnected.");

  
}



void funcNamePass(){

  //Cut String req up in pieces
  int i1 = req.indexOf(',');
  int i2 = req.indexOf(',',i1+1);
    //first string = req.substring(0, i1)
    RecSSID = req.substring(i1 + 1, i2);
    RecPass = req.substring(i2 + 1);
    WiName = RecSSID;
    WiPass = RecPass;
      Serial.print("Received SSID: ");
      Serial.println(RecSSID);
      Serial.print("Received Password: ");
      Serial.println(RecPass);

  //Change String to char and write to SD file
    char __RecSSID[sizeof(RecSSID)];
    RecSSID.toCharArray(__RecSSID, sizeof(__RecSSID));
    writeFile(SD, "/firstWifi.txt", __RecSSID);
    appendFile(SD, "/firstWifi.txt", ",");
    char __RecPass[sizeof(RecPass)];
    RecPass.toCharArray(__RecPass, sizeof(__RecPass));
    appendFile(SD, "/firstWifi.txt", __RecPass);

  //update serial monitor and android
  Serial.println("SSID and Pass saved to file");
  reply = "SSID and Pass saved to file";
  //checkReply();
  WiFiClient client = server.available();
  // Página WEB. ////////////////////////////
  client.println("HTTP/1.1 200 OK");
  client.println("Content-Type: text/html");
  client.println(""); //  Comillas importantes.
  client.println(reply); //  Return status.               
  client.flush();
  client.stop();
  
  //We have SSID and password, next step is getting the local ip range
  wifiStatus = 1;
  writeFile(SD, "/wifiStatus.txt", "1");
  
  req = "";  //Empty the String req to make shure we only handle the request on time 

  //Restart ESP
  Serial.println("Restarting ESP in 5 seconds !!!");
  delay(5000);
  ESP.restart();
}

Why? DHCP not working?

Using freeRTOS, the WiFi operations can be taken out of setup and placed in a task. Thing is, if freeRTOS is used, loop() needs to be empty. So the rest of the code will need to be ported to tasks as well.

If the SD card task is started before the WiFi task, the WiFI task can be told to wait till the SD card task clears the WiFi task to continue. During the wait, the SD card gets the data, puts the data in a queue and when the WIFI task detects data in the queue, the WiFi task continues on its merry way.

An example of using freeRTOS, the built in OS on a ESP32.

/*
   10/17/2020 Ambrose Campbell
   Project to display MQTT data on a OLED screen
*/
#include <WiFi.h>
#include <PubSubClient.h>
#include "certs.h"
#include "sdkconfig.h"
#include "esp_system.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/timers.h"
#include "freertos/event_groups.h"
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1351.h>
#include "time.h"
//
hw_timer_t * timer = NULL;
EventGroupHandle_t eg;
#define evtDoDisplay             ( 1 << 1 )
#define evtCollectHistory        ( 1 << 2 )
#define evtParseMQTT             ( 1 << 3 )
// the events in this event groups tasks have been given a delay to avoid a possible race condition.
#define evtGroupBits ( evtCollectHistory | evtDoDisplay )
//                                                 CS GPIO_NUM_27, DC GPIO_NUM_12, DIN GPIO_NUM_13, CLK GPIO_NUM_14, RST GPIO_NUM_26
Adafruit_SSD1351 tft = Adafruit_SSD1351( 128, 128, GPIO_NUM_27,    GPIO_NUM_12,    GPIO_NUM_13,     GPIO_NUM_14,     GPIO_NUM_26 );
////
WiFiClient   wifiClient;
PubSubClient MQTTclient( mqtt_server, mqtt_port, wifiClient );
//////
// black, blue, red, green, cyan, magenta, yellow, white, lightyellow, brown
const int DataCells = 60;
int    ColorPalette[10] = { 0x0000, 0x001F, 0xF800, 0x07E0, 0x07FF, 0xF81F, 0xFFE0, 0xFFFF, 0xFFFFD8, 0xFF8040 };
int    arrayCellPtr = 0;
float  oPressureHistory[DataCells] = {0.0f};
String str_eTopic;
char   strPayload [300] = {NULL};
float  oTemperature = 0.0f;
float  oHumidity = 0.0f;
float  oIAQ = 0.0f; // Indexed Air Quality
float  oPressure = 0.0f;
byte mac[6];
int mqttOK = 0;
////
////
SemaphoreHandle_t sema_HistoryCompleted;
SemaphoreHandle_t sema_MQTT_Parser;;
SemaphoreHandle_t sema_MQTT_KeepAlive;
SemaphoreHandle_t sema_mqttOK;
////
volatile int iDoTheThing = 0;
////
void IRAM_ATTR mqttCallback(char* topic, byte * payload, unsigned int length)
{
  xSemaphoreTake( sema_MQTT_Parser, portMAX_DELAY);
  str_eTopic = topic;
  int i = 0;
  for ( i; i < length; i++)
  {
    strPayload[i] = ((char)payload[i]);
  }
  strPayload[i] = NULL;
  //log_i( "topic %s payload %s" ,str_eTopicPtr, strPayloadPtr );
  xSemaphoreGive ( sema_MQTT_Parser );
  xEventGroupSetBits( eg, evtParseMQTT ); // trigger tasks
} // void mqttCallback(char* topic, byte* payload, unsigned int length)
////
void IRAM_ATTR WiFiEvent(WiFiEvent_t event)
{
  switch (event) {
    case SYSTEM_EVENT_STA_CONNECTED:
      log_i("Connected to access point");
      break;
    case SYSTEM_EVENT_STA_DISCONNECTED:
      log_i("Disconnected from WiFi access point");
      break;
    case SYSTEM_EVENT_AP_STADISCONNECTED:
      log_i("WiFi client disconnected");
      break;
    default: break;
  }
}
////
void IRAM_ATTR onTimer()
{
  BaseType_t xHigherPriorityTaskWoken;
  iDoTheThing++;
  if ( iDoTheThing == 60000 )
  {
    xEventGroupSetBitsFromISR( eg, evtGroupBits, &xHigherPriorityTaskWoken );
    iDoTheThing = 0;
  }
}
////
void setup()
{
  /* Use 4th timer of 4.
    1 tick 1/(80MHZ/80) = 1us set divider 80 and count up.
    Attach onTimer function to timer
    Set alarm to call timer ISR, every 1000uS and repeat / reset ISR (true) after each alarm
    Start an timer alarm
  */
  timer = timerBegin( 3, 80, true );
  timerAttachInterrupt( timer, &onTimer, true );
  timerAlarmWrite(timer, 1000, true);
  timerAlarmEnable(timer);
  //
  str_eTopic.reserve(300);
  //
  eg = xEventGroupCreate();
  //
  tft.begin();
  tft.setRotation( 1 );
  tft.fillScreen(0x0000);
  //
  sema_mqttOK    =  xSemaphoreCreateBinary();
  sema_MQTT_Parser      = xSemaphoreCreateBinary();
  sema_HistoryCompleted = xSemaphoreCreateBinary();
  sema_MQTT_KeepAlive   = xSemaphoreCreateBinary();
  xSemaphoreGive( sema_HistoryCompleted );
  xSemaphoreGive( sema_MQTT_KeepAlive ); // found keep alive can mess with a publish, stop keep alive during publish
  xSemaphoreGive( sema_mqttOK );
  ////
  xTaskCreatePinnedToCore( fparseMQTT,      "fparseMQTT",      7000,  NULL, 5, NULL, 1 ); // assign all to core 1, WiFi in use.
  xTaskCreatePinnedToCore( fCollectHistory, "fCollectHistory", 10000, NULL, 4, NULL, 1 );
  xTaskCreatePinnedToCore( MQTTkeepalive,   "MQTTkeepalive",   7000,  NULL, 2, NULL, 1 ); //this task makes a WiFi and MQTT connection.
  xTaskCreatePinnedToCore( fUpdateDisplay,  "fUpdateDisplay",  50000, NULL, 5, NULL, 1 );
  xTaskCreatePinnedToCore( fmqttWatchDog,   "fmqttWatchDog", 3000, NULL, 3, NULL, 1 ); // assign all to core 1
  ////
} //setup() END
////
////
void fmqttWatchDog( void * paramater )
{
  int maxNonMQTTresponse = 5;
  //wait for a mqtt connection
  while ( !MQTTclient.connected() )
  {
    vTaskDelay( 250 );
  }
  TickType_t xLastWakeTime = xTaskGetTickCount();
  const TickType_t xFrequency = 1000; //delay for mS
  for (;;)
  {
    xLastWakeTime = xTaskGetTickCount();
    vTaskDelayUntil( &xLastWakeTime, xFrequency );
    if ( mqttOK >= maxNonMQTTresponse )
    {
      ESP.restart();
    }
  }
  vTaskDelete( NULL );
} //void fmqttWatchDog( void * paramater )
////
void fCollectHistory( void * parameter )
{
  int StorageTriggerCount = 119;
  bool Tick = true;
  int maxStorageTriggerCount = 120;
  for (;;)
  {
    xEventGroupWaitBits (eg, evtCollectHistory, pdTRUE, pdTRUE, portMAX_DELAY );
    xSemaphoreTake( sema_HistoryCompleted, portMAX_DELAY );
    oPressureHistory[arrayCellPtr] = oPressure;
    StorageTriggerCount++; //triggered by the timer isr once a minute
    log_i( "storage trigger count %d", StorageTriggerCount );
    if ( StorageTriggerCount == maxStorageTriggerCount )
    {
      arrayCellPtr++;
      log_i( "arrayCellPtr %d", arrayCellPtr );
      if ( arrayCellPtr == DataCells )
      {
        arrayCellPtr = 0;
      }
      StorageTriggerCount = 0;
    }
    xSemaphoreGive( sema_HistoryCompleted );
    // log_i( " high watermark %d",  uxTaskGetStackHighWaterMark( NULL ) );
  }
  vTaskDelete( NULL );
} //void fCollectHistory( void * parameter )
////
// Using a semaphore to protect the PSRAM from multiple tasks access the same PSRM locations
////
void fUpdateDisplay( void * parameter )
{
  // Color definitions
  // http://www.barth-dev.de/online/rgb565-color-picker/
  /*
    ColorPalette[0] = 0x0000; //BLACK
    ColorPalette[1] = 0x001F; //BLUE
    ColorPalette[2] = 0xF800; //RED
    ColorPalette[3] = 0x07E0; //GREEN
    ColorPalette[4] = 0x07FF; //CYAN
    ColorPalette[5] = 0xF81F; //MAGENTA
    ColorPalette[6] = 0xFFE0; //YELLOW
    ColorPalette[7] = 0xFFFF; //WHITE
    ColorPalette[8] = 0xFFFFD8; //LIGHTYELLOW
    ColorPalette[9] = 0xFF8040; //BROWN
  */
  int rowRef = 80;
  int hRef = int( oPressureHistory[0] );
  const int nextPoint = 2;
  int nextCol = 0;
  int16_t aqColor = 216; //minimum value
  for (;;)
  {
    xEventGroupWaitBits (eg, evtDoDisplay, pdTRUE, pdTRUE, portMAX_DELAY ); //
    xSemaphoreTake( sema_mqttOK, portMAX_DELAY );
    mqttOK ++;
    xSemaphoreGive( sema_mqttOK );
    vTaskDelay( 1 );
    tft.fillScreen( ColorPalette[0] );
    tft.setTextSize( 2 );
    log_i( "oTemperature %f", oTemperature );
    if ( (oTemperature >= 73.0f) && (oTemperature <= 100.0f) )
    {
      log_i( "if ( (oTemperature >= 73.0f) && (oTemperature <= 100.0f) )" );
      // green to decrease and red to increase no blue
      int tempDiff = int( 100.0f - oTemperature );
      log_i( "oTemperature %f tempDiff %d", oTemperature, tempDiff );
      int NewRed = tempDiff << 10;
      int NewGreen =  0x07E0 - (tempDiff << 5 );
      //int NewRed = ColorPalette[3] - (tempDiff << 5 ) ; // load in green decreased by temperature
      log_i( "NewRed %d NewGreen %d", NewRed, NewGreen );
      NewRed |= NewGreen ;
      log_i( "NewRed | NewGreen %d ", NewRed );
      tft.setTextColor( NewRed );
    }
    if ( oTemperature <= 0.0f )
    {
      log_i("white");
      tft.setTextColor( ColorPalette[7] ); // white
    }
    if ((oTemperature >= 32.0f) && (oTemperature < 72.0f) )
    {
      //log_i("if ((oTemperature >= 32.0f) && (oTemperature < 72.0f) )");
      //blue decreases, no red, green increases
      int tempDiff = int( oTemperature - 32.0f );
      if( tempDiff > 31 ) tempDiff = 31;
      log_i(" oTemperature %f tempDiff %d", oTemperature, tempDiff );
      int NewBlue =  ColorPalette[1] - tempDiff; // decrease blue
      log_i( "NewBlue %d", NewBlue );
      int NewGreen =  (tempDiff) << 5 ;// increase green
      log_i( "NewGreen %d", NewGreen );
      NewGreen |= NewBlue; // combine blue into newgreen
      log_i( "NewGreen or'ed %d", NewGreen );
      tft.setTextColor( NewGreen );
    }
    if ((oTemperature >= 72.0f) && (oTemperature < 73.0f) ) tft.setTextColor( ColorPalette[3] ); //green
    if ( (oTemperature < 32.0f) && (oTemperature >= 0.0f) )
    {
      log_i( "if ( (oTemperature < 32.0f) && (oTemperature >= 0.0f) )" );
      int NewBlue = ColorPalette[1]; // blue
      //blue is max for each step below 32 red and green increase by one
      //get steps below 32
      int tempDiff = int( 32.0f - oTemperature );
      NewBlue = NewBlue + ( (tempDiff) << 5); //add green
      NewBlue =  NewBlue | (xOff << 10);//add red
      tft.setTextColor( NewBlue ); // red
    } //if( (oTemperature <= 32.0f) %% (oTemperature >= 0.0f) )
    if ( oTemperature >= 100.0f ) tft.setTextColor( ColorPalette[2] ); // red
    tft.setCursor( 0, 0 );
    if ( oTemperature < 100.0f )
    {
      tft.print( "Tmp " + String(oTemperature) );
    } else
    {
      tft.print( "Tmp " + String(int(oTemperature)) );
    }
    tft.setCursor( 0, 20 );
    tft.setTextSize( 2 );
    //set humidity color
    // log_i( "%f", oHumidity );
//    if ( oHumidity < 33.0f )
//    {
//      tft.setTextColor( ColorPalette[6] ); //yellow
//    }
//    if ( (oHumidity >= 33.0f) && ( oTemperature <= 60.0f) )
//    {
//      tft.setTextColor( ColorPalette[3] ); //green
//    }
//    if ( (oTemperature > 60.0f) )
//    {
//      tft.setTextColor( ColorPalette[2] ); //red
//    }
    //below 50%
    if ( oHumidity < 49.0f )
    {
      //reduce green component, increase blue component, red @0
      //get difference from 50%
      int xOff = 2 * ( abs(50 - int(oHumidity)) ); // used to make adjustments to green and blue
      //shift xOff to the left by 5 places reduce green by xOff
      newGreen = ColorPalette[3] - ( (xOff) << 5); //green falls off by xOff
      // increase blue by xOff
      newGreen = newGreen | (xOff);
      //log_i( "ColorPalette[3] %d newGreen %d", ColorPalette[3], newGreen );
      tft.setTextColor( newGreen );
    }
    //@50%
    if ( (oHumidity <= 51.0f) && (oHumidity >= 49.0f) )
    {
      tft.setTextColor( ColorPalette[3] ); //green
    }
    // above



    
    if ( oHumidity > 51.0f )
    {
      //log_i( "humidity %f", oHumidity );
      //reduce green component, increase red component, blue @0
      //get difference from 50%
      int tempDiff = (int)oHumidity - 50;
      if( tempDiff > 31 ) tempDiff = 31;
      int newGreen = ColorPalette[3] - ( tempDiff << 5 ); //green falls off by xOff
      int newRed = tempDiff << 10;
      //log_i( "newGreen %d", newGreen );
      // increase red by xOff
      newGreen =  newGreen | tempDiff;
      //log_i( "newGreen %d", newGreen );
      tft.setTextColor( newGreen );
    }



    
    tft.print( "Hum " + String(oHumidity) );
    // set AQI text color
    if ( (oIAQ * 216) >= (90.0f * 216))
    {
      int redColor = (int)oIAQ - 90;
      redColor *= 216;
      int green = ColorPalette[3] - (redColor << 5);
      int blue = ColorPalette[1] - redColor;
      aqColor &= 0x7FF; //set green to and blue to max
      redColor = redColor << 10;
      aqColor |= redColor;
      tft.setTextColor( aqColor );
    }
    if ( oIAQ * 216 > 60.0f * 216 && oIAQ * 216 < 90.0f * 216 )
    {
      int blueColor = (int)oIAQ - 60;
      blueColor *= 216;
      aqColor &= (0x7E0 - (blueColor << 5) ); //set green reduce green by blue
      aqColor |= blueColor;
      tft.setTextColor( aqColor );
    }
    if ( oIAQ * 216 <= 60.0f * 216 )
    {
      int greenColor = oIAQ * 216;
      greenColor = greenColor << 5;
      tft.setTextColor( greenColor );
    }
    // display AQI
    tft.setCursor( 0, 40 );
    tft.print( "AQI " + String(oIAQ) );
    //
    tft.setTextSize( 1 );
    tft.setTextColor( ColorPalette[1] ); //set graph line color
    tft.setCursor( 0, 118 );
    tft.print( "  Pres: " + String(oPressure) + "mmHg" );
    hRef = int( oPressureHistory[0] );
    nextCol = 0;
    xSemaphoreTake( sema_HistoryCompleted, portMAX_DELAY );
    for (int i = 0; i <= DataCells; i++)
    {
      int hDisplacement = hRef - int( oPressureHistory[i] ); // cell height displacement from baseline
      tft.setCursor( nextCol , (rowRef + hDisplacement) );
      tft.print( "_" );
      nextCol += nextPoint;
      //place vertical bar representing a 24 hour time period.
      if ( (nextCol % 24) == 0 )
      {
        //tft.drawLine( col, row, width, height, color );
        tft.drawLine( uint16_t(nextCol), uint16_t(rowRef + 5), uint16_t(nextCol), uint16_t(rowRef + 11), ColorPalette[7] );
      }
    }
    // advance cursor to place a begin of graph marker. each letter is 5 spaces wide but graph position marker only advances 2 spaces
    // to keep cursor at begining place cursor +2 the nextCol position.
    tft.setCursor( (arrayCellPtr * nextPoint), (rowRef + 3) );
    tft.print( "I" );
    xSemaphoreGive( sema_HistoryCompleted );
    //     log_i( "fUpdateDisplay MEMORY WATERMARK %d", uxTaskGetStackHighWaterMark(NULL) );
  }
  vTaskDelete( NULL );
} // void fUpdateDisplay( void * parameter )
/*
    Important to not set vTaskDelay to less then 10. Errors begin to develop with the MQTT and network connection.
    makes the initial wifi/mqtt connection and works to keeps those connections open.
*/
void MQTTkeepalive( void *pvParameters )
{
  // setting must be set before a mqtt connection is made
  MQTTclient.setKeepAlive( 90 ); // setting keep alive to 90 seconds makes for a very reliable connection, must be set before the 1st connection is made.
  TickType_t xLastWakeTime = xTaskGetTickCount();
  const TickType_t xFrequency = 250; // 250mS
  for (;;)
  {
    //check for a is connected and if the WiFi thinks its connected, found checking on both is more realible than just a single check
    if ( (wifiClient.connected()) && (WiFi.status() == WL_CONNECTED) )
    {
      xSemaphoreTake( sema_MQTT_KeepAlive, portMAX_DELAY ); // whiles loop() is running no other mqtt operations should be in process
      MQTTclient.loop();
      xSemaphoreGive( sema_MQTT_KeepAlive );
    }
    else {
      log_i( "MQTT keep alive found MQTT status %s WiFi status %s", String(wifiClient.connected()), String(WiFi.status()) );
      if ( !(WiFi.status() == WL_CONNECTED) )
      {
        connectToWiFi();
      }
      connectToMQTT();
    }
    xLastWakeTime = xTaskGetTickCount();
    vTaskDelayUntil( &xLastWakeTime, xFrequency );
  }
  vTaskDelete ( NULL );
}
////
void connectToMQTT()
{
  // create client ID from mac address
  byte mac[5];
  WiFi.macAddress(mac); // get mac address
  String clientID = String(mac[0]) + String(mac[4]) ; // use mac address to create clientID
  log_i( "connect to mqtt as client %s", clientID );
  while ( !MQTTclient.connected() )
  {
    MQTTclient.connect( clientID.c_str(), mqtt_username, mqtt_password );
    log_i( "connecting to MQTT" );
    vTaskDelay( 250 );
  }
  log_i("MQTT Connected");
  MQTTclient.setCallback( mqttCallback );
  MQTTclient.subscribe( mqtt_topic );
}
//
void connectToWiFi()
{
  log_i( "connect to wifi" );
  while ( WiFi.status() != WL_CONNECTED )
  {
    WiFi.disconnect();
    WiFi.begin( SSID, PASSWORD );
    log_i(" waiting on wifi connection" );
    vTaskDelay( 4000 );
  }
  log_i( "Connected to WiFi" );
  WiFi.onEvent( WiFiEvent );
}
////
void fparseMQTT( void *pvParameters )
{
  xSemaphoreGive ( sema_MQTT_Parser );
  for (;;)
  {
    xEventGroupWaitBits (eg, evtParseMQTT, pdTRUE, pdTRUE, portMAX_DELAY ); //
    xSemaphoreTake( sema_mqttOK, portMAX_DELAY );
    mqttOK = 0;
    xSemaphoreGive( sema_mqttOK );
    xSemaphoreTake( sema_MQTT_Parser, portMAX_DELAY );
    if ( str_eTopic == topicOutsideTemperature )
    {
      oTemperature = String(strPayload).toFloat();
    }
    if ( (String)str_eTopic == topicOutsideHumidity )
    {
      oHumidity = String(strPayload).toFloat();
    }
    if ( (String)str_eTopic == topicAQIndex )
    {
      oIAQ = String(strPayload).toFloat();
    }
    if ( String(str_eTopic) == topicOutsidePressure )
    {
      oPressure = String(strPayload).toFloat();
    }
    // clear pointer locations
    memset( strPayload, '\0', 300 );
    str_eTopic = ""; //clear string buffer
    xSemaphoreGive( sema_MQTT_Parser );
  }
} // void fparseMQTT( void *pvParameters )
////
void loop() { }

This looks like doing it the hard way.
Use MQTT and a public MQTT broker. Done.

Thank you for your replies everyone. This will keep me busy for a while.
I will check out your ideas and see if these will get we what I need.
It will take me some time. Busy life and not a lot of spare time. But I will get there.

Thanks.

I use mDNS so a browser client can connect to the ESP32 web server using http://esp32mdns.local. No need for the client to know the IP address. Look through the ESP32 examples for mDNS.

If you need to specify the MQTT broker address to the ESP32, WiFiManager supports adding input fields to the SSID and password. For example, see this.

Thank you icebuster for this information, I'll check this out when I get the time.