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();
}