Arduino stuck at setup

Hi everyone. I'm trying to build an arduino controlled irrigation system. I've 4 valves controlled by 8 relays (I've bistable solenoids in the valves). Also there is a humidity/temperature sensor to keep track of my greenhouse temperature. For the past months the code was working, but now I after I made some changes the Arduino won't even start. It is blocked in the setup function. When I run this in the serial monitor I can see that it is stuck after the "Starting connection to server...
" which is in the connectToWiFi function (taken from Arduino official website guide here https://docs.arduino.cc/tutorials/uno-r4-wifi/rtc/#network-time-protocol-ntp). Any idea of what's going on?
Also there is another weird thing happening here. If i comment all the code that connects to wifi to set the UTC time, the Arduino get's stuck after "Setup DISPLAY".

/*
  Sketch generated by the Arduino IoT Cloud Thing "Untitled"
  https://create.arduino.cc/cloud/things/b829ddcf-4b2d-4d12-8f54-25a97c42403f

  Arduino IoT Cloud Variables description

  The following variables are automatically generated and updated when changes are made to the Thing

  String lineeAccese;
  String lineeAcceseManualmente;
  String orario;
  float temperatura;
  float umidita;

  Variables which are marked as READ/WRITE in the Cloud Thing will also have functions
  which are called when their values are changed from the Dashboard.
  These functions are generated with the Thing and added at the end of this sketch.
*/

#include "thingProperties.h"

//#define DEBUG

#ifdef DEBUG
#define DPRINT(...) Serial.print(__VA_ARGS__)
#define DPRINTLN(...) Serial.println(__VA_ARGS__)
#define DRINTF(...) Serial.print(F(__VA_ARGS__))
#define DPRINTLNF(...) Serial.println(F(__VA_ARGS__))
#else
#define DPRINT(...)     //blank line
#define DPRINTLN(...)   //blank line
#define DPRINTF(...)    //blank line
#define DPRINTLNF(...)  //blank line
#endif

// Real time clock integrato Arduino
#include "RTC.h"

//Include the NTP library
#include <NTPClient.h>

#if defined(ARDUINO_PORTENTA_C33)
#include <WiFiC3.h>
#elif defined(ARDUINO_UNOWIFIR4)
#include <WiFiS3.h>
#endif
#include <WiFiUdp.h>

// Display oled
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

// Sonda temperatura
#include <Wire.h>
#include <Adafruit_SHT31.h>

// Wifi
int wifiStatus = WL_IDLE_STATUS;
WiFiUDP Udp; // A UDP instance to let us send and receive packets over UDP
NTPClient timeClient(Udp);

// Programmi
const int nPrograms = 4;
const int nSchedules = 4;
const int relays[4][2] = { { 5, 6 }, { 7, 8 }, { 9, 10 }, { 11, 12 } };
bool valveOpen[4] = { false, false, false, false };
int activePrograms[4];
int activeManualPrograms[4];
int activeProgramsTotal;
bool on;
bool off;
const int schedule_timetables[nPrograms * nSchedules][2][2] = {
  // Programma 1
  { { 5, 30 }, { 6, 00 } },
  { { 0, 0 }, { 0, 0 } },
  { { 0, 0 }, { 0, 0 } },
  { { 0, 0 }, { 0, 0 } },
  // Programma 2
  { { 7, 00 }, { 7, 30 } },
  { { 0, 0 }, { 0, 0 } },
  { { 0, 0 }, { 0, 0 } },
  { { 0, 0 }, { 0, 0 } },
  // Programma 3
  { { 6, 0 }, { 6, 30 } },
  { { 0, 0 }, { 0, 0 } },
  { { 0, 0 }, { 0, 0 } },
  { { 0, 0 }, { 0, 0 } },
  // Programma 4
  { { 0, 0 }, { 0, 0 } },
  { { 0, 0 }, { 0, 0 } },
  { { 0, 0 }, { 0, 0 } },
  { { 0, 0 }, { 0, 0 } },
};
const bool schedule_days[nPrograms * nSchedules][14] = {
  // Programma 1
  { true, true, true, true, true, true, true, /**/ true, true, true, true, true, true, true },
  { true, true, true, true, true, true, true, /**/ true, true, true, true, true, true, true },
  { true, true, true, true, true, true, true, /**/ true, true, true, true, true, true, true },
  { true, true, true, true, true, true, true, /**/ true, true, true, true, true, true, true },
  // Programma 2
  { true, false, true, false, true, false, true, /**/ false, true, false, true, false, true, false },
  { true, true, true, true, true, true, true, /**/ true, true, true, true, true, true, true },
  { true, true, true, true, true, true, true, /**/ true, true, true, true, true, true, true },
  { true, true, true, true, true, true, true, /**/ true, true, true, true, true, true, true },
  // Programma 3
  { true, false, false, true, false, false, true, /**/ false, false, true, false, false, true, false },
  { true, true, true, true, true, true, true, /**/ true, true, true, true, true, true, true },
  { true, true, true, true, true, true, true, /**/ true, true, true, true, true, true, true },
  { true, true, true, true, true, true, true, /**/ true, true, true, true, true, true, true },
  // Programma 4
  { true, true, true, true, true, true, true, /**/ true, true, true, true, true, true, true },
  { true, true, true, true, true, true, true, /**/ true, true, true, true, true, true, true },
  { true, true, true, true, true, true, true, /**/ true, true, true, true, true, true, true },
  { true, true, true, true, true, true, true, /**/ true, true, true, true, true, true, true }
};

/*
   Definisco le dimensioni del display, serviranno per creare l'istanza del display
   e magari riutilizzarle nel codice qualora dovessero servirmi queste informazioni
*/
#define SCREEN_WIDTH 128  // OLED display width, in pixels
#define SCREEN_HEIGHT 64  // OLED display height, in pixels

// Creo istanza del display
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT);

// Sonda temperatura
Adafruit_SHT31 sht31 = Adafruit_SHT31();

void setup() {
  // Initialize serial and wait for port to open:
  Serial.begin(9600);
  // This delay gives the chance to wait for a Serial Monitor without blocking if none is found
  delay(1500);
  Serial.println("setup()...");
  // Defined in thingProperties.h
  initProperties();
  
  // Connect to Arduino IoT Cloud
  ArduinoCloud.begin(ArduinoIoTPreferredConnection);

  /*
     The following function allows you to obtain more information
     related to the state of network and IoT Cloud connection and errors
     the higher number the more granular information you’ll get.
     The default is 0 (only errors).
     Maximum is 4
  */
  setDebugMessageLevel(2);
  ArduinoCloud.printDebugInfo();

  // https://docs.arduino.cc/tutorials/uno-r4-wifi/rtc/
  Serial.println("Starting connection to server...");
  connectToWiFi();
  RTC.begin();
  timeClient.begin();
  timeClient.update();

  auto timeZoneOffsetHours = 2;
  auto unixTime = timeClient.getEpochTime() + (timeZoneOffsetHours * 3600);
  Serial.print("Unix time = ");
  Serial.println(unixTime);
  RTCTime timeToSet = RTCTime(unixTime);
  RTC.setTime(timeToSet);
  // Retrieve the date and time from the RTC and print them
  RTCTime currentTime;
  RTC.getTime(currentTime);
  Serial.println("The RTC was just set to: " + String(currentTime));

  // Pin relè dei solenoidi
  Serial.println("Setup pin solenoidi");
  for (int r = 0; r < nPrograms; r++) {
    pinMode(relays[r][0], OUTPUT);
    pinMode(relays[r][1], OUTPUT);
  }

  // Lcd/oled
  Serial.println("Setup DISPLAY");
  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println("Couldn't find DISPLAY");
  }
  display.clearDisplay();
  display.display();

  Serial.println("Setup SHT31");
  // Sonda temperatura
  // if (!sht31.begin(0x44)) {
  //   Serial.println("Couldn't find SHT31");
  // }

  Serial.println("Chiusura valvole iniziale");
  // Chiusura preventiva di tutte le valvole
  delay(5000);
  for (int r = 0; r < nPrograms; r++) {
    openOrCloseValve(r, false);
  }
  Serial.println("Fine setup()");
}

void loop() {
  ArduinoCloud.update();
  // Your code here

  // Orario
  RTCTime currentTime;
  RTC.getTime(currentTime);
  DPRINTLN(currentTime.toString());
  orario = currentTime.toString();

  // Temperatura e umidità
  // float t = sht31.readTemperature();
  // float h = sht31.readHumidity();
  // DPRINTLN("T: " + String(t, 1) + ", %: " + String(h, 1));
  // temperatura = t;
  // umidita = h;

  // Se è l'ora di attivare il programma e la valvola è spenta > apre
  // Se è l'ora di attivare il programma e la valvola è aperta > niente
  // Se NON è l'ora di attivare il programma e la valvola è spenta > niente
  // Se NON è l'ora di attivare il programma e la valvola è aperta > chiude
  activeProgramsTotal = 0;

  // Controllo programmi
  for (int p = 0; p < nPrograms; p++) {
    activePrograms[p] = 0;
    if(on){
      for (int s = 0; s < nSchedules; s++) {
        if (checkProgramDayAndTime(currentTime, p, s)) {
          activePrograms[p]++;
          activeProgramsTotal++;
        }
      }
    }
  }

  // Controllo programmi forzati a mano
  for (int p = 0; p < nPrograms; p++) {
    if (activeManualPrograms[p] > 0) 
      activeProgramsTotal++;
  }
  
  // // Se scrivo off spegne tutto, devo mettere on per farlo andare
  // on = lineeAcceseManualmente.indexOf("on") != -1;
  // off = lineeAcceseManualmente.indexOf("off") != -1 || !on;
  // if (off) {
  //   for (int p = 0; p < nPrograms; p++) {
  //     activePrograms[p] = 0;
  //     activeManualPrograms[p] = 0;
  //   }
  //   activeProgramsTotal = 0;
  // }

  // // Controllo programmi forzati a mano
  // for (int p = 0; p < nPrograms; p++) {
  //   activeManualPrograms[p] = 0;
  //   if (lineeAcceseManualmente.indexOf(displayProgram(p)) != -1) {
  //     activeManualPrograms[p]++;
  //     activeProgramsTotal++;
  //   }
  // }

  // Accende o spegne le valvole in base ai programmi attivi
  for (int p = 0; p < nPrograms; p++) {
    // Se c'è un programma e la valvola è ancora chiusa > apre
    if ((activePrograms[p] > 0 || activeManualPrograms[p] > 0) && valveOpen[p] == false) {
      DPRINTLN("Da aprire " + String(p) + " stato " + String(activePrograms[p]) + ", " + String(activeManualPrograms[p]) + ", valvola " + String(valveOpen[p]));
      openOrCloseValve(p, true);
    }
    // Se non c'è nessun programma e la valvola è ancora aperta > chiude
    if (activePrograms[p] == 0 && activeManualPrograms[p] == 0 && valveOpen[p] == true) {
      DPRINTLN("Da chiudere " + String(p) + " stato " + String(activePrograms[p]) + ", " + String(activeManualPrograms[p]) + ", valvola " + String(valveOpen[p]));
      openOrCloseValve(p, false);
    }
  }

  // Aggiornamento variabili con l'indicazione dello stato
  // Linee attive e programmate
  if (off && activeProgramsTotal == 0) {
    lineeAccese = "Nessuna (off)";
  }
  else {
    String linee = "";
    if (on) {
      linee = "Pr: ";
      for (int p = 0; p < nPrograms; p++) {
        for (int s = 0; s < nSchedules; s++) {
          // Se non è attivo lo mostra (quelli attivi sono già mostrati prima)
          if (checkProgramDay(currentTime, p, s) && !checkProgramDayAndTime(currentTime, p, s)) {
            //display.print(displayProgram(p, s));
            linee.concat(displayProgram(p));
            linee.concat(displaySchedule(s));
            linee.concat(" ");
          }
        }
      }
    }
    // Linee manuali attive
    if (activeProgramsTotal > 0) {
      linee.concat("On: ");
      for (int p = 0; p < nPrograms; p++) {
        if (activePrograms[p] > 0) {
          for (int s = 0; s < nSchedules; s++) {
            if (checkProgramDayAndTime(currentTime, p, s)) {
              linee.concat(displayProgram(p));
              linee.concat(displaySchedule(s));
              linee.concat(" ");
            }
          }
        }
        if (activeManualPrograms[p] > 0) {
          linee.concat(displayProgram(p));
          linee.concat("M ");
        }
      }
    }
    lineeAccese = linee;
  }

  // Gestione display
  display.clearDisplay();
  if (activeProgramsTotal > 0) {
    // Orario mostrato in alto in un rettangolo
    display.fillRect(0, 0, SCREEN_WIDTH, 16, WHITE);
    display.setTextColor(BLACK, WHITE);
    display.setTextSize(1);
    display.setCursor(0, 4);
    char datetime[15];
    sprintf(datetime, "  %02d/%02d/%02d    %02d:%02d", currentTime.getDayOfMonth(), Month2int(currentTime.getMonth()), currentTime.getYear() % 100, currentTime.getHour(), currentTime.getMinutes());
    display.print(datetime);

    display.setCursor(0, 19);
    display.setTextColor(WHITE);
    display.setTextSize(2);
    display.print("P:");
    for (int p = 0; p < nPrograms; p++) {
      for (int s = 0; s < nSchedules; s++) {
        // Se non è attivo lo mostra (quelli attivi sono già mostrati prima)
        if (checkProgramDay(currentTime, p, s) && !checkProgramDayAndTime(currentTime, p, s)) {
          //display.print(displayProgram(p, s));
          display.setTextSize(1);
          display.print(" ");
          display.setTextSize(2);
          display.print(displayProgram(p));
          display.setTextSize(1);
          display.print(displaySchedule(s));
        }
      }
    }

    if (activeProgramsTotal > 0) {
      display.setTextSize(2);
      display.setCursor(0, 48);
      display.print("A:");
      for (int p = 0; p < nPrograms; p++) {
        // Se il programma è attivo controlla le schedulazioni
        if (activePrograms[p] > 0) {
          display.setTextSize(1);
          display.print(" ");
          display.setTextSize(2);
          display.print(displayProgram(p));
          // Se c'è una scedulazione attiva la mostra, altrimenti solo nome programma
          for (int s = 0; s < nSchedules; s++) {
            if (checkProgramDayAndTime(currentTime, p, s)) {
              display.setTextSize(1);
              display.print(displaySchedule(s));
            }
          }
        }
        if (activeManualPrograms[p] > 0) {
          display.setTextSize(1);
          display.print(" ");
          display.setTextSize(2);
          display.print(displayProgram(p));
          display.setTextSize(1);
          display.print("M");
        }
      }
    }
  }
  display.display();
}


/*
  Since LineeAcceseManualmente is READ_WRITE variable, onLineeAcceseManualmenteChange() is
  executed every time a new value is received from IoT Cloud.
*/
void onLineeAcceseManualmenteChange()  {
  // Add your code here to act upon LineeAcceseManualmente change
}

void checkManualPrograms(){
  // Se scrivo off spegne tutto, devo mettere on per farlo andare
  on = lineeAcceseManualmente.indexOf("on") != -1;
  off = lineeAcceseManualmente.indexOf("off") != -1 || !on;
  if (off) {
    for (int p = 0; p < nPrograms; p++) {
      activePrograms[p] = 0;
    }
  }

  // Controllo programmi forzati a mano
  for (int p = 0; p < nPrograms; p++) {
    if (lineeAcceseManualmente.indexOf(displayProgram(p)) != -1) 
      activeManualPrograms[p] = 1;
    else
      activeManualPrograms[p] = 0;
  }
}

String displayProgram(int program) {
  int p = program + 1;
  return String("") + p;
}

String displayProgram(int program, int schedule) {
  char s = 'A';
  s += schedule;
  int p = program + 1;
  return String("") + p + "" + s;
}

String displaySchedule(int schedule) {
  char s = 'A';
  s += schedule;
  return String("") + s;
}

// Controllo dei programmi
bool checkProgramDayAndTime(RTCTime date, int program, int schedule) {
  return checkProgramDay(date, program, schedule) && checkProgramTime(date, program, schedule);
}

bool checkProgramDay(RTCTime date, int program, int schedule) {
  // Se il programma è spento
  if (!isProgramSet(program, schedule))
    return false;

  int day = dayOfWeek(date) - 1;
  int week = weekNumber(date);

  if (week % 2 == 0)
    return schedule_days[program * nSchedules + schedule][day + 7];
  else
    return schedule_days[program * nSchedules + schedule][day];
}

bool checkProgramTime(RTCTime date, int program, int schedule) {
  int index = getScheduleIndex(program, schedule);
  int startHour = schedule_timetables[index][0][0];
  int startMinute = schedule_timetables[index][0][1];
  int endHour = schedule_timetables[index][1][0];
  int endMinute = schedule_timetables[index][1][1];
  return isDateTimeBetween(date, startHour, startMinute, endHour, endMinute);
}

bool isProgramSet(int program, int schedule) {
  int index = getScheduleIndex(program, schedule);
  int startHour = schedule_timetables[index][0][0];
  int startMinute = schedule_timetables[index][0][1];
  int endHour = schedule_timetables[index][1][0];
  int endMinute = schedule_timetables[index][1][1];
  return (startHour > 0 || startMinute > 0 || endHour > 0 || endMinute > 0);
}

// Non c'è un metodo di base per fare questo in modo semplice con i timespan
bool isDateTimeBetween(RTCTime date, int startHour, int startMinute, int endHour, int endMinute) {
  RTCTime dtStart;
  RTC.getTime(dtStart);
  dtStart.setHour(startHour);
  dtStart.setMinute(startMinute);
  dtStart.setSecond(0);

  RTCTime dtEnd;
  RTC.getTime(dtEnd);
  dtEnd.setHour(endHour);
  dtEnd.setMinute(endMinute);
  dtEnd.setSecond(0);

  return dtStart.getUnixTime() <= date.getUnixTime() && date.getUnixTime() <= dtEnd.getUnixTime();
}

int getScheduleIndex(int program, int schedule) {
  return program * nSchedules + schedule;
}

int dayOfWeek(RTCTime date) {
  return DayOfWeek2int(date.getDayOfWeek(), false) - 1;
}

int weekNumber(RTCTime date) {
  int y = date.getYear();
  int m = Month2int(date.getMonth());
  int d = date.getDayOfMonth();
  // compute correction for year
  //     If Jan. 1 falls on: Mo Tu We Th Fr Sa Su
  // then the correction is:  0 +1 +2 +3 -3 -2 -1
  int corr = ((((y - 1881) * 5) / 4) % 7) - 3;
  // compute day of the year (in range 1-366)
  int doy = d;
  if (m > 1) doy += 31;
  if (m > 2) doy += (((y % 4) == 0) ? 29 : 28);
  if (m > 3) doy += 31;
  if (m > 4) doy += 30;
  if (m > 5) doy += 31;
  if (m > 6) doy += 30;
  if (m > 7) doy += 31;
  if (m > 8) doy += 31;
  if (m > 9) doy += 30;
  if (m > 10) doy += 31;
  if (m > 11) doy += 30;
  // compute corrected day number
  int cdn = corr + doy;
  // check for boundary conditions
  // if our calculation would give us "week 53",
  // we need to find out whether week 53 really exists,
  // or whether it is week 1 of the following year
  if (cdn > 364) {
    // check for year beginning on Thurs.
    if (corr == 3) return 53;
    // check for leap year beginning on Wed.
    if (((y % 4) == 0) && (corr == 2)) return 53;
    // otherwise, there is no week 53
    return 1;
  }
  // if our calculation would give us "week 0",
  // then go to the previous year
  // and find out whether we are in week 52 or week 53
  if (cdn < 1) {
    // first, compute correction for the previous year
    corr = ((((y - 1882) * 5) / 4) % 7) - 3;
    // then, compute day of year with respect to that same previous year
    doy = d + (((y % 4) == 1) ? 366 : 365);
    // finally, re-compute the corrected day number
    cdn = corr + doy;
  }
  // compute number of weeks, rounding up to nearest whole week
  return ((cdn + 6) / 7);
}

// Controllo rele valvole per zona
void openOrCloseValve(int program, bool open) {
  int relayOpen = relays[program][0];
  int relayClose = relays[program][1];

  digitalWrite(relayClose, HIGH);
  digitalWrite(relayOpen, HIGH);
  if (open) {
    digitalWrite(relayOpen, LOW);
    //delay(50);
    unsigned long currentMillis = millis();
    unsigned long lastMillis = millis();
    while((currentMillis - lastMillis) < 50)
      {
        currentMillis = millis();
      }
    digitalWrite(relayOpen, HIGH);
  } else {
    digitalWrite(relayClose, LOW);
    //delay(50);
    unsigned long currentMillis = millis();
    unsigned long lastMillis = millis();
    while((currentMillis - lastMillis) < 50)
      {
        currentMillis = millis();
      }
    digitalWrite(relayClose, HIGH);
  }
  valveOpen[program] = open;
}


void printWifiStatus() {
  // print the SSID of the network you're attached to:
  Serial.print("SSID: ");
  Serial.println(WiFi.SSID());

  // print your board's IP address:
  IPAddress ip = WiFi.localIP();
  Serial.print("IP Address: ");
  Serial.println(ip);

  // print the received signal strength:
  long rssi = WiFi.RSSI();
  Serial.print("signal strength (RSSI):");
  Serial.print(rssi);
  Serial.println(" dBm");
}

void connectToWiFi() {
  // check for the WiFi module:
  if (WiFi.status() == WL_NO_MODULE) {
    Serial.println("Communication with WiFi module failed!");
    // don't continue
    while (true)
      ;
  }

  String fv = WiFi.firmwareVersion();
  if (fv < WIFI_FIRMWARE_LATEST_VERSION) {
    Serial.println("Please upgrade the firmware");
  }

  // attempt to connect to WiFi network:
  while (wifiStatus != WL_CONNECTED) {
    Serial.print("Attempting to connect to SSID: ");
    Serial.println(SSID);
    // Connect to WPA/WPA2 network. Change this line if using open or WEP network:
    wifiStatus = WiFi.begin(SSID, PASS);

    // wait 10 seconds for connection:
    delay(10000);
  }

  Serial.println("Connected to WiFi");
  printWifiStatus();
}



Inside setup() many functions are called. One or more of these called functions do not return to setup() to allow setup() to finish. Add a Serial.println("Now I'm HERE!"); after all the function calls in setup() to discover where the return to setup() is not happening.

I changed the connectToWiFi code and it's stuck at WiFi.status() == WL_NO_MODULE Since the last piece of output is "Start connectToWiFi". It's really weird because it's the code taken from Arduino guides.

void connectToWiFi() {
  // check for the WiFi module:
  Serial.println("Start connectToWiFi");
  if (WiFi.status() == WL_NO_MODULE) {
    Serial.println("Communication with WiFi module failed!");
    // don't continue
    while (true)
      ;
  }

  Serial.println("Check WiFi firmware");
  String fv = WiFi.firmwareVersion();
  if (fv < WIFI_FIRMWARE_LATEST_VERSION) {
    Serial.println("Please upgrade the firmware");
  }

  // attempt to connect to WiFi network:
  Serial.println("Before attempting to connect to WiFi network");
  while (wifiStatus != WL_CONNECTED) {
    Serial.print("Attempting to connect to SSID: ");
    Serial.println(SSID);
    // Connect to WPA/WPA2 network. Change this line if using open or WEP network:
    wifiStatus = WiFi.begin(SSID, PASS);

    // wait 10 seconds for connection:
    delay(10000);
  }

  Serial.println("Connected to WiFi");
  printWifiStatus();
}

Also if I comment the entire piece of code that connects to wifi to update the RTC, the setup gets stuck again but in another row (between "Setup DISPLAY" and "Pulizia iniziale DISPLAY"). Looks like it gets stuck after some seconds no matter of the line of code.

Serial.begin(9600);
  // This delay gives the chance to wait for a Serial Monitor without blocking if none is found
  delay(1500);
  Serial.println("setup()...");
  // Defined in thingProperties.h
  initProperties();
  
  // Connect to Arduino IoT Cloud
  ArduinoCloud.begin(ArduinoIoTPreferredConnection);

  /*
     The following function allows you to obtain more information
     related to the state of network and IoT Cloud connection and errors
     the higher number the more granular information you’ll get.
     The default is 0 (only errors).
     Maximum is 4
  */
  setDebugMessageLevel(2);
  ArduinoCloud.printDebugInfo();

  // https://docs.arduino.cc/tutorials/uno-r4-wifi/rtc/
  Serial.println("Starting connection to server...");
  //connectToWiFi();
  // timeClient.begin();
  // timeClient.update();

  // auto timeZoneOffsetHours = 2;
  // auto unixTime = timeClient.getEpochTime() + (timeZoneOffsetHours * 3600);
  // Serial.print("Unix time = ");
  // Serial.println(unixTime);
  RTC.begin();
  // RTCTime timeToSet = RTCTime(unixTime);
  // RTC.setTime(timeToSet);
  
  // Retrieve the date and time from the RTC and print them
  RTCTime currentTime;
  RTC.getTime(currentTime);
  Serial.println("The RTC was just set to: " + String(currentTime));

  // Pin relè dei solenoidi
  Serial.println("Setup pin solenoidi");
  for (int r = 0; r < nPrograms; r++) {
    pinMode(relays[r][0], OUTPUT);
    pinMode(relays[r][1], OUTPUT);
  }

  // Lcd/oled
  Serial.println("Setup DISPLAY");
  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println("Couldn't find DISPLAY");
  }
  Serial.println("Pulizia iniziale DISPLAY");
  display.clearDisplay();
  display.display();

What happens if you run the NTP code example from Arduino 'as it is'. Same for a standard display example.. does that work ?

The base example is working. So no issues with the board i think. If I take my IoT project, I remove all the code (excep for my cloud variables), and I run the exact same code it stops working again. Basically the code below gets stuck in the call of connectToWiFi(); (the function of the examples). So is working in a "normal project, but it get stucked in an IoT project.

/*
  Sketch generated by the Arduino IoT Cloud Thing "Untitled"
  https://create.arduino.cc/cloud/things/b829ddcf-4b2d-4d12-8f54-25a97c42403f

  Arduino IoT Cloud Variables description

  The following variables are automatically generated and updated when changes are made to the Thing

  String lineeAccese;
  String lineeAcceseManualmente;
  String orario;
  float temperatura;
  float umidita;

  Variables which are marked as READ/WRITE in the Cloud Thing will also have functions
  which are called when their values are changed from the Dashboard.
  These functions are generated with the Thing and added at the end of this sketch.
*/

#include "thingProperties.h"

// Include the RTC library
#include "RTC.h"

//Include the NTP library
#include <NTPClient.h>

#if defined(ARDUINO_PORTENTA_C33)
#include <WiFiC3.h>
#elif defined(ARDUINO_UNOWIFIR4)
#include <WiFiS3.h>
#endif

#include <WiFiUdp.h>

// Wifi
int wifiStatus = WL_IDLE_STATUS;
WiFiUDP Udp; // A UDP instance to let us send and receive packets over UDP
NTPClient timeClient(Udp);

void setup() {
  // Initialize serial and wait for port to open:
  Serial.begin(9600);
  // This delay gives the chance to wait for a Serial Monitor without blocking if none is found
  delay(1500);
  
  Serial.println("setup()...");
  
  // Defined in thingProperties.h
  initProperties();
  // Connect to Arduino IoT Cloud
  ArduinoCloud.begin(ArduinoIoTPreferredConnection);

  /*
     The following function allows you to obtain more information
     related to the state of network and IoT Cloud connection and errors
     the higher number the more granular information you’ll get.
     The default is 0 (only errors).
     Maximum is 4
  */
  setDebugMessageLevel(2);
  ArduinoCloud.printDebugInfo();

  Serial.println("connectToWiFi()");
  connectToWiFi();
  Serial.println("RTC.begin");
  RTC.begin();
  Serial.println("Starting connection to server...");
  timeClient.begin();
  timeClient.update();

  // Get the current date and time from an NTP server and convert
  // it to UTC +2 by passing the time zone offset in hours.
  // You may change the time zone offset to your local one.
  auto timeZoneOffsetHours = 2;
  auto unixTime = timeClient.getEpochTime() + (timeZoneOffsetHours * 3600);
  Serial.print("Unix time = ");
  Serial.println(unixTime);
  RTCTime timeToSet = RTCTime(unixTime);
  RTC.setTime(timeToSet);

  // Retrieve the date and time from the RTC and print them
  RTCTime currentTime;
  RTC.getTime(currentTime); 
  Serial.println("The RTC was just set to: " + String(currentTime));
  Serial.println("Fine setup()");
}

void loop() {
  ArduinoCloud.update();
  // Your code here

  // Orario
  RTCTime currentTime;
  RTC.getTime(currentTime);
  orario = currentTime.toString();
}


/*
  Since LineeAcceseManualmente is READ_WRITE variable, onLineeAcceseManualmenteChange() is
  executed every time a new value is received from IoT Cloud.
*/
void onLineeAcceseManualmenteChange()  {
  // Add your code here to act upon LineeAcceseManualmente change
}

function connectToWiFi();

void connectToWiFi(){
  // check for the WiFi module:
  if (WiFi.status() == WL_NO_MODULE) {
    Serial.println("Communication with WiFi module failed!");
    // don't continue
    while (true);
  }

  String fv = WiFi.firmwareVersion();
  if (fv < WIFI_FIRMWARE_LATEST_VERSION) {
    Serial.println("Please upgrade the firmware");
  }

  // attempt to connect to WiFi network:
  while (wifiStatus != WL_CONNECTED) {
    Serial.print("Attempting to connect to SSID: ");
    Serial.println(SSID);
    // Connect to WPA/WPA2 network. Change this line if using open or WEP network:
    wifiStatus = WiFi.begin(SSID, PASS);

    // wait 10 seconds for connection:
    delay(10000);
  }

  Serial.println("Connected to WiFi");
  printWifiStatus();
}

Show all error output.

How can I get all the errors? In the serial output I see only this.
setup()...
SHA256: 128176 bytes (of 128176) read
ArduinoIoTCloudTCP::begin could not read device id.
***** Arduino IoT Cloud - configuration info *****
Device ID: 69f5f411-7ef6-4870-bf0e-42e6f3d3e5f3
MQTT Broker: iot.arduino.cc:8883
connectToWiFi()

Maybe "ArduinoIoTCloudTCP::begin could not read device id." is the problem?

Your Serial Monitor receives the errors. Data from the errors helps find the source.

UPDATE
I've removed the device from the IoT cloud and made from the beginning the procedure to add the thing. It seems to solve the "begin could not read device id" message, but it continues to get stuck on the connectToWiFi() function

How can I read the errors sent from the serial monitor?

I was wrong calling it the "Serial Monitor", sorry.

The window I meant to say is the stderr "OUTPUT" window at the bottom of the IDE.

The OUTPUT window is the same window showing progress during compiling and uploading.

WiFi usually requires an SSID and a password. Can you verify you have both correct?

I only get the compilation output and an Ok at the end. I'm using the web editor because I can't compile IoT with the desktop app.

/usr/local/bin/arduino-cli compile --fqbn arduino:renesas_uno:unor4wifi --build-cache-path /tmp --output-dir /tmp/2318162392/build --build-path /tmp/arduino-build-D19391164D6E18321A891DD114186859 /tmp/2318162392/IrrigazioneIoT
[info] Sketch uses 122192 bytes (46%) of program storage space. Maximum is 262144 bytes.
[info] Global variables use 14296 bytes (43%) of dynamic memory, leaving 18472 bytes for local variables. Maximum is 32768 bytes.

Restarting in bootloader mode
Flashing with command:C:/Users/Alessio/.arduino-create/arduino/bossac/1.9.1-arduino5/bossac.exe -d --port=COM4 -U -e -w C:/Users/Alessio/AppData/Local/Temp/arduino-create-agent2416372502/IrrigazioneIoT.bin -R
Set binary mode
version()=Arduino Bootloader (SAM-BA extended) 2.0 [Arduino:IKXYZ]
Connected at 921600 baud
identifyChip()=nRF52840-QIAA
write(addr=0,size=0x34)
writeWord(addr=0x30,value=0x400)
writeWord(addr=0x20,value=0)
Erase flash
chipErase(addr=0)

Done in 0.001 seconds
Write 122200 bytes to flash (30 pages)

[ ] 0% (0/30 pages)write(addr=0x34,size=0x1000)
writeBuffer(scr_addr=0x34, dst_addr=0, size=0x1000)

[= ] 3% (1/30 pages)write(addr=0x34,size=0x1000)
writeBuffer(scr_addr=0x34, dst_addr=0x1000, size=0x1000)

[== ] 6% (2/30 pages)write(addr=0x34,size=0x1000)
writeBuffer(scr_addr=0x34, dst_addr=0x2000, size=0x1000)

[=== ] 10% (3/30 pages)write(addr=0x34,size=0x1000)
writeBuffer(scr_addr=0x34, dst_addr=0x3000, size=0x1000)

[==== ] 13% (4/30 pages)write(addr=0x34,size=0x1000)
writeBuffer(scr_addr=0x34, dst_addr=0x4000, size=0x1000)

[===== ] 16% (5/30 pages)write(addr=0x34,size=0x1000)
writeBuffer(scr_addr=0x34, dst_addr=0x5000, size=0x1000)

[====== ] 20% (6/30 pages)write(addr=0x34,size=0x1000)
writeBuffer(scr_addr=0x34, dst_addr=0x6000, size=0x1000)

[======= ] 23% (7/30 pages)write(addr=0x34,size=0x1000)
writeBuffer(scr_addr=0x34, dst_addr=0x7000, size=0x1000)

[======== ] 26% (8/30 pages)write(addr=0x34,size=0x1000)
writeBuffer(scr_addr=0x34, dst_addr=0x8000, size=0x1000)

[========= ] 30% (9/30 pages)write(addr=0x34,size=0x1000)
writeBuffer(scr_addr=0x34, dst_addr=0x9000, size=0x1000)

[========== ] 33% (10/30 pages)write(addr=0x34,size=0x1000)
writeBuffer(scr_addr=0x34, dst_addr=0xa000, size=0x1000)

[=========== ] 36% (11/30 pages)write(addr=0x34,size=0x1000)
writeBuffer(scr_addr=0x34, dst_addr=0xb000, size=0x1000)

[============ ] 40% (12/30 pages)write(addr=0x34,size=0x1000)
writeBuffer(scr_addr=0x34, dst_addr=0xc000, size=0x1000)

[============= ] 43% (13/30 pages)write(addr=0x34,size=0x1000)
writeBuffer(scr_addr=0x34, dst_addr=0xd000, size=0x1000)

[============== ] 46% (14/30 pages)write(addr=0x34,size=0x1000)
writeBuffer(scr_addr=0x34, dst_addr=0xe000, size=0x1000)

[=============== ] 50% (15/30 pages)write(addr=0x34,size=0x1000)
writeBuffer(scr_addr=0x34, dst_addr=0xf000, size=0x1000)

[================ ] 53% (16/30 pages)write(addr=0x34,size=0x1000)
writeBuffer(scr_addr=0x34, dst_addr=0x10000, size=0x1000)

[================= ] 56% (17/30 pages)write(addr=0x34,size=0x1000)
writeBuffer(scr_addr=0x34, dst_addr=0x11000, size=0x1000)

[================== ] 60% (18/30 pages)write(addr=0x34,size=0x1000)
writeBuffer(scr_addr=0x34, dst_addr=0x12000, size=0x1000)

[=================== ] 63% (19/30 pages)write(addr=0x34,size=0x1000)
writeBuffer(scr_addr=0x34, dst_addr=0x13000, size=0x1000)

[==================== ] 66% (20/30 pages)write(addr=0x34,size=0x1000)
writeBuffer(scr_addr=0x34, dst_addr=0x14000, size=0x1000)

[===================== ] 70% (21/30 pages)write(addr=0x34,size=0x1000)
writeBuffer(scr_addr=0x34, dst_addr=0x15000, size=0x1000)

[====================== ] 73% (22/30 pages)write(addr=0x34,size=0x1000)
writeBuffer(scr_addr=0x34, dst_addr=0x16000, size=0x1000)

[======================= ] 76% (23/30 pages)write(addr=0x34,size=0x1000)
writeBuffer(scr_addr=0x34, dst_addr=0x17000, size=0x1000)

[======================== ] 80% (24/30 pages)write(addr=0x34,size=0x1000)
writeBuffer(scr_addr=0x34, dst_addr=0x18000, size=0x1000)

[========================= ] 83% (25/30 pages)write(addr=0x34,size=0x1000)
writeBuffer(scr_addr=0x34, dst_addr=0x19000, size=0x1000)

[========================== ] 86% (26/30 pages)write(addr=0x34,size=0x1000)
writeBuffer(scr_addr=0x34, dst_addr=0x1a000, size=0x1000)

[=========================== ] 90% (27/30 pages)write(addr=0x34,size=0x1000)
writeBuffer(scr_addr=0x34, dst_addr=0x1b000, size=0x1000)

[============================ ] 93% (28/30 pages)write(addr=0x34,size=0x1000)
writeBuffer(scr_addr=0x34, dst_addr=0x1c000, size=0x1000)

[============================= ] 96% (29/30 pages)write(addr=0x34,size=0x1000)
writeBuffer(scr_addr=0x34, dst_addr=0x1d000, size=0x1000)

[==============================] 100% (30/30 pages)
Done in 7.493 seconds
reset()
Ok

The credentials are correct. If I test the connectToWiFi() with a non IoT project it works. There is something wrong with IoT version

Format the text you posted inside the < CODE > tags. The format will be easier to read.

Look inside connectToWifi()... to find what it is expecting (and failing). You could post that file here (properly formatted).

Where did you see the wifi-connect error?

I don't see errors, I can only see that it stops executing in the line
if (WiFi.status() == WL_NO_MODULE) {
because the last output in the serial is "connectToWiFi 1", so it stops between "connectToWiFi 1" and "Communication with WiFi module failed!" or "connectToWiFi 2".

void connectToWiFi(){
  // check for the WiFi module:
  Serial.println("connectToWiFi 1");
  if (WiFi.status() == WL_NO_MODULE) {
    Serial.println("Communication with WiFi module failed!");
    // don't continue
    //while (true);
  }

  Serial.println("connectToWiFi 2");
  String fv = WiFi.firmwareVersion();
  if (fv < WIFI_FIRMWARE_LATEST_VERSION) {
    Serial.println("Please upgrade the firmware");
  }

  Serial.println("connectToWiFi 3");
  // attempt to connect to WiFi network:
  while (wifiStatus != WL_CONNECTED) {
    Serial.print("Attempting to connect to SSID: ");
    Serial.println(SSID);
    // Connect to WPA/WPA2 network. Change this line if using open or WEP network:
    wifiStatus = WiFi.begin(SSID, PASS);

    // wait 10 seconds for connection:
    delay(10000);
  }

  Serial.println("Connected to WiFi");
  printWifiStatus();
}

Earlier in this thread, you mentioned that your sketch worked until you made some changes. Do you have a copy of the sketch that was previously working? If yes, does it still work?

I don't use the web editor, so please excuse my ignorance. I see that the create-agent changed two weeks ago. Does that automatically update, or do you have to manually install updates?

Also, the ArduinoIoTCloud changed on March 12.

Since your setup does not do much before the call to

connectToWiFi();

I suspect it might be in the Arduino-supplied code.

You might wish to create a simplified version of your code that does not instantiate things like the Adafruit display and does not create globals at the start. If that works, slowly add functionality until it no longer works.

Your code calls this function and receives a return value of WL_NO_MODULE which causes Communication with WiFi module failed! to be printed.

The program originally stopped itself from proceeding, but you have allowed it to continue.

You need to discover why WiFi.status() returns WL_NO_MODULE... you need to read your documentation on correct connections, signals and coding.