I'm using sockets to send data from my ESP32 project to my computer so I can use the data in a web server, but, for reasons I am not aware of, any time Wi-Fi is initialised, the custom characters I've made for the Wi-Fi signal strength is being interfered with. It is the only 2 characters out of the 8 custom characters I'm using to be interfered with. This has only started occurring once I added in the code for the socket connection. The characters work fine if the Wi-Fi connection times out, but if Wi-Fi is initialised, even if the socket isn't connected, its interfering with the custom character and displaying a character which I have never seen on my LCD before.
I thought it had something to do with the WiFiClient being used, but I checked the RSSI and it was at normal levels (about -40/-60).
Can someone give me a bit of help with this? My code is long-ish, but I know people always ask for the full sketch, so here it is:
// custom library
#include "Triogen.h"
// other libraries used
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <SD.h>
#include <SPI.h>
#include <FS.h>
#include <RTClib.h>
#include <WiFi.h>
#include <NTPClient.h>
#include <WiFiUdp.h>
#include <Time.h>
// pin selection using GPIO pin numbering as opposed to Arduino pin numbering
#define RED_LED 1
#define GREEN_LED 2
#define BLUE_LED 3
#define BUZZER_TOGGLE_SWITCH 5
#define RED_ALARM 6
#define GREEN_ALARM 7
#define BUZZER_PIN 8
#define CHIP_SELECT 21
// set reset vector at address 0 - essentially does a reset on the board once called
void (*resetFunc)(void) = 0;
// lcd pin configurations
LiquidCrystal_I2C lcd(0x27, 20, 4);
// RTC variables
RTC_DS3231 rtc;
time_t now;
struct tm myTimeInfo;
// total time that the program has ran for
// this is necessary as the duration of initialisation has to be deducted for accurate logging
unsigned long tStart = millis();
unsigned long totMillis;
int totSeconds;
int totMinutes;
int totHours;
int totDays;
// timing variables
unsigned long lastMillis = 0;
const unsigned long SAMPLE_TM = 1250;
unsigned long tSample = totMillis + SAMPLE_TM;
const unsigned long WATER_ICON_TM = 250;
unsigned long tIcon = totMillis + WATER_ICON_TM;
// timekeeping variables
// these variables use both NTP and RTC
static int year;
static int month;
static int day;
static int hour;
static int minute;
static int second;
// day and month string arrays
char dayName[7][4] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
char monthName[12][4] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
// toggle alarm
bool buzzerPinState;
bool BTstate;
bool buzzerPinOutState;
// blink and bleep without delay
bool outputState;
// count errors to only print error once per logging interval
uint8_t errCount = 0;
int totalErrorCount = 0;
// number of samples
uint8_t nSample = 1;
// ensure the new file functions are only ran once if the time is 00:00:00
// sometimes the initialisation process can happen so quick that it happens a few times in a second
// this variable stops this from happening
uint8_t funcPassThru = 0;
uint8_t iconNo = 1;
// SD card global variables
char DIR_NAME[9] = "/TANKLOG";
char DATA_TYPE[12] = "Water Tank";
// fileName and fullPath specified 24 bytes to avoid format overflow
char fileName[24];
char fullPath[24];
File dataFile;
bool pathExists = 0;
// NTP and WiFi variables
const char* ssid = TRIOGEN_SSID;
const char* password = TRIOGEN_PASSWORD;
const uint16_t port = 8080;
const char * host = "10.2.30.116";
const char* NTP_SERVER = "pool.ntp.org";
const long GMT_OFFSET_SEC = 0;
const int DAYLIGHT_OFFSET_SEC = 3600;
const char* TIME_ZONE = PSTR("GMT0BST,M3.5.0/1,M10.5.0");
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "pool.ntp.org", GMT_OFFSET_SEC);
byte wifiState;
// Variable to check if the socket is available to send data to
// This stops the program from delaying when no socket is available (host or WiFi unavailable)
bool socketConnectState;
// only used for water tank logging (change for any other type of data)
#include <OneWire.h>
#include <DallasTemperature.h>
#define FLOAT_SW 4
#define tempResolution 12
#define ONE_WIRE_BUS 10
OneWire oneWire(ONE_WIRE_BUS);
DeviceAddress tankTempSensor;
DallasTemperature sensors(&oneWire);
int floatValue;
// temperature variables
static float totT1;
float aveT1;
static float tempC;
// buffers for float to string conversion for formatting data
// these buffers are used as for my board, sprintf or Serial.printf cannot use float values
char charAveT1[6];
char charTotT1[8];
char charTemp[8];
// error flags
bool floatErrState = 0;
bool tempErrState = 0;
bool highTempErrState = 0;
// alarm icons
uint8_t SPEAKER_ICON[8] = {B00001, B00011, B00111, B11111, B11111, B00111, B00011, B00001};
uint8_t ALARM_ON_ICON[8] = {B00010, B01001, B00101, B10101, B10101, B00101, B01001, B00010};
uint8_t ALARM_OFF_ICON[8] = {B00000, B00000, B01010, B00100, B00100, B01010, B00000, B00000};
// water tank diagnostic icons
uint8_t THERMOMETER_ICON[8] = {B00100, B01010, B01010, B01010, B01110, B11111, B11111, B01110};
uint8_t WATER_1_ICON[8] = {B00100, B00100, B01010, B01010, B10001, B10001, B10001, B01110};
uint8_t WATER_2_ICON[8] = {B00100, B00100, B01010, B01010, B10001, B10001, B11111, B01110};
uint8_t WATER_3_ICON[8] = {B00100, B00100, B01010, B01010, B10001, B11111, B11111, B01110};
uint8_t WATER_4_ICON[8] = {B00100, B00100, B01010, B01010, B11111, B11111, B11111, B01110};
uint8_t WATER_5_ICON[8] = {B00100, B00100, B01010, B01110, B11111, B11111, B11111, B01110};
uint8_t WATER_6_ICON[8] = {B00100, B00100, B01110, B01110, B11111, B11111, B11111, B01110};
// runtime icons
uint8_t RUNTIME_L_ICON[8] = {B00001, B00000, B01001, B11101, B01001, B01000, B00100, B00011};
uint8_t RUNTIME_R_ICON[8] = {B11000, B00100, B00010, B00010, B11010, B00010, B00100, B11000};
// signal icons
uint8_t SIGNAL_1_BAR_ICON[8] = {B11111, B10001, B01010, B00100, B00100, B00000, B00000, B10000};
uint8_t SIGNAL_2_BAR_ICON[8] = {B11111, B10001, B01010, B00100, B00100, B00000, B00100, B10100};
uint8_t SIGNAL_3_BAR_ICON[8] = {B11111, B10001, B01010, B00100, B00100, B00001, B00101, B10101};
uint8_t SIGNAL_4_BAR_ICON[8] = {B00000, B00000, B00000, B00000, B10000, B10000, B10000, B10000};
uint8_t SIGNAL_CLEAR_ICON[8] = {B00000, B00000, B00000, B00000, B00000, B00000, B00000, B00000};
uint8_t NO_SIGNAL_ICON[8] = {B00000, B00000, B10100, B01000, B10100, B00001, B00101, B10101};
// function runs if SD card or RTC is not connected
void hardwareErr() {
analogWrite(RED_LED, 0);
analogWrite(BLUE_LED, 0);
while (!SD.begin() || !rtc.begin()) {
digitalWrite(BUZZER_PIN, 1);
analogWrite(GREEN_LED, 175);
delay(500);
digitalWrite(BUZZER_PIN, 0);
analogWrite(GREEN_LED, 0);
delay(1500);
} // end while
if (SD.begin(CHIP_SELECT) && rtc.begin()) {
lcd.clear();
resetFunc();
} // end if
} // end hardwareErr()
// show the reason for the last reset of the ESP32
int printResetReason() {
// isolate from last serial output using newlines
Serial.print("\n\n\n");
int reason = esp_reset_reason();
Serial.printf("CPU reset reason: %d ", reason);
switch (reason) {
case ESP_RST_UNKNOWN: Serial.println("unknown"); break;
case ESP_RST_POWERON: Serial.println("POWER ON RESET"); break;
case ESP_RST_SW: Serial.println("SOFTWARE RESET"); break;
case ESP_RST_PANIC: Serial.println("PANIC RESET"); break;
case ESP_RST_TASK_WDT: Serial.println("TASK WDT RESET"); break;
case ESP_RST_WDT: Serial.println("OTHER WDT RESET"); break;
case ESP_RST_DEEPSLEEP: Serial.println("EXIT DEEP SLEEP RESET"); break;
case ESP_RST_BROWNOUT: Serial.println("BROWNOUT RESET"); break;
case ESP_RST_SDIO: Serial.println("SDIO RESET"); break;
} // end switch case
return (reason);
} // end printResetReason()
// print hexadecimal address of DS18B20 temperature sensor
void printAddress(DeviceAddress tankTempSensor) {
for (uint8_t i = 0; i < 8; i++) {
if (tankTempSensor[i] < 16) {
Serial.print("0");
} // end if
Serial.print(tankTempSensor[i], HEX);
} // end for
} // end printAddress()
// initialise pin modes
void initPinModes() {
pinMode(BUZZER_PIN, OUTPUT);
pinMode(BUZZER_TOGGLE_SWITCH, INPUT_PULLUP);
pinMode(FLOAT_SW, INPUT_PULLUP);
pinMode(RED_LED, OUTPUT);
pinMode(GREEN_LED, OUTPUT);
pinMode(BLUE_LED, OUTPUT);
pinMode(RED_ALARM, OUTPUT);
pinMode(GREEN_ALARM, OUTPUT);
} // end initPinModes()
// initialise state of digital outputs
void initGPIOstates() {
digitalWrite(RED_LED, LOW);
digitalWrite(GREEN_LED, HIGH);
digitalWrite(BLUE_LED, LOW);
digitalWrite(GREEN_ALARM, HIGH);
digitalWrite(RED_ALARM, LOW);
} // end initGPIOstates()
// initialise LCD
void initLCD() {
lcd.begin(20, 4);
lcd.home();
delay(2000);
lcd.setBacklight(255);
lcd.clear();
} //end initLCD()
// initialise the DS18B20 temperature sensor
void initDS18B20() {
sensors.begin();
sensors.setWaitForConversion(false);
sensors.requestTemperatures();
Serial.print("DS18B20 Address: ");
sensors.getAddress(tankTempSensor, 0);
printAddress(tankTempSensor);
Serial.print("\n");
sensors.setResolution(tankTempSensor, tempResolution);
} // end initDS18B20()
// initialise the SD card
void initSD() {
SD.begin(CHIP_SELECT);
lcd.setCursor(0, 0);
printAll("Initialising SD...");
delay(1000);
if (!SD.begin()) {
lcd.clear();
printAll("SD card failed.");
lcd.setCursor(0, 1);
lcd.print("Check that the SD");
lcd.setCursor(0, 2);
lcd.print("is present and all");
lcd.setCursor(0, 3);
lcd.print("wiring is correct");
Serial.println("Check that the SD card is mounted and all wiring and connections have been made and are correct.");
hardwareErr();
} // end if
uint8_t cardType = SD.cardType();
// if SD fails, print error message and loop into hardware error function until fixed
if (cardType == CARD_NONE) {
lcd.clear();
printAll("No SD card attached");
hardwareErr();
} // end if
Serial.print("SD Card Type: ");
if (cardType == CARD_MMC) Serial.println("MMC");
else if (cardType == CARD_SD) Serial.println("SDSC");
else if (cardType == CARD_SDHC) Serial.println("SDHC");
else Serial.println("UNKNOWN");
uint64_t cardSize = SD.cardSize() / (1024 * 1024);
Serial.printf("SD Card Size: %lluMB\n", cardSize);
} //end initSD()
// initialise the RTC
void initRTC() {
lcd.setCursor(0, 1);
printAll("Initialising RTC...");
delay(1000);
// if RTC fails, print error message and loop into hardware error function until fixed
if (!rtc.begin()) {
lcd.clear();
printAll(F("RTC failed."));
lcd.setCursor(0, 1);
lcd.print("Check that the RTC");
lcd.setCursor(0, 2);
lcd.print("is wired and mounted");
lcd.setCursor(0, 3);
lcd.print("correctly.");
Serial.println("Check that the RTC is mounted and all wiring and connections have been made and are correct");
hardwareErr();
} // end if
lcd.clear();
lcd.setCursor(0, 0);
} // end initRTC()
// initialise WiFi
void initWiFi() {
unsigned WiFiTimeout = 30;
Serial.print("Connecting to WiFi...");
lcd.print("Connecting to WiFi..");
WiFi.begin(ssid, password);
while(WiFi.status() != WL_CONNECTED) {
Serial.print(".");
delay(1000);
lcd.setCursor(0, 3);
char WiFiTimeoutBuffer[20];
sprintf(WiFiTimeoutBuffer, "WiFi Timeout in %02ds", WiFiTimeout);
lcd.print(WiFiTimeoutBuffer);
WiFiTimeout -=1;
// ESP32 hates 0's for some reason so -2 used instead of 0 to reach 0?? This method has worked
// If it isn't broke, don't fix it!
if (WiFiTimeout == -2) {
Serial.print("\nCannot connect to WiFi. time will derive from last time set on RTC and offline mode will be used instead.\n\n");
lcd.clear();
lcd.print("No WiFi available");
lcd.setCursor(0,2);
lcd.print("Offline mode will");
lcd.setCursor(0,3);
lcd.print("be used instead");
wifiState = 0;
// buzz alarm 5 times if wifi is not available
digitalWrite(BUZZER_PIN, HIGH);
delay(100);
digitalWrite(BUZZER_PIN, LOW);
delay(100);
digitalWrite(BUZZER_PIN, HIGH);
delay(100);
digitalWrite(BUZZER_PIN, LOW);
delay(100);
digitalWrite(BUZZER_PIN, HIGH);
delay(100);
digitalWrite(BUZZER_PIN, LOW);
delay(4500);
// make time variables equal to current RTC time
DateTime now = rtc.now();
year = now.year();
month = now.month();
day = now.day();
hour = now.hour();
minute = now.minute();
second = now.second();
break;
} // end if
} // end while
// only run NTP function if wifi is connected
if (WiFi.status() == WL_CONNECTED) {
lcd.setCursor(0, 1);
Serial.println();
printAll("WIFI CONNECTED");
initNTPtime();
initSocketClient();
wifiState = 1;
} // end if
} // end initWiFi()
// initialise NTP and get time (function only runs if WiFi is connected)
void initNTPtime() {
timeClient.begin();
timeClient.update();
lcd.setCursor(0, 3);
Serial.printf("Getting time from %s...", NTP_SERVER);
lcd.setCursor(0, 2);
lcd.print("Getting time from ");
lcd.setCursor(0, 3);
lcd.print(NTP_SERVER);
lcd.print("... ");
time(&now);
localtime_r(&now, &myTimeInfo);
configTime(GMT_OFFSET_SEC, DAYLIGHT_OFFSET_SEC, NTP_SERVER);
while(!getLocalTime(&myTimeInfo)) {
Serial.print(".");
} // end while
// if NTP time is avaiable, make time variables equal to time that has been acquired from NTP
year = myTimeInfo.tm_year + 1900;
month = myTimeInfo.tm_mon + 1;
day = myTimeInfo.tm_mday;
hour = myTimeInfo.tm_hour;
minute = myTimeInfo.tm_min;
second = myTimeInfo.tm_sec;
// adjust RTC time to NTP time for accurate timekeeping
char NTPtime[70];
rtc.adjust(DateTime(year, month, day, hour, minute, second));
sprintf(NTPtime, "Date: %02d-%02d-%02d Time: %02d:%02d:%02d", day, month, year, hour, minute, second);
Serial.println();
Serial.println(NTPtime);
} // end initNTPtime()
void initSocketClient() {
WiFiClient client;
if(!client.connect(host, port)) {
Serial.print("\n\nConnection to host failed\n\n");
socketConnectState = 0;
}
else {
socketConnectState = 1;
Serial.print("\n\nSocket connected");
}
}
// create the file name and directory for data logging
void createFileName() {
sprintf(fileName, "%02d-%02d-%d.txt", day, month, year - 2000);
strlcpy(fullPath, DIR_NAME, sizeof fullPath);
strlcat(fullPath, "/", sizeof fullPath);
strlcat(fullPath, fileName, sizeof fullPath);
SD.exists(fullPath) ? pathExists = 1 : pathExists = 0;
SD.mkdir(DIR_NAME);
Serial.printf("Full File Path: X:%s\n", fullPath);
} // end createFileName()
// first write to the file created in the SD card
void firstWrite() {
uint8_t date = strlen(fileName)-4;
fileName[date] = '\0';
dataFile = SD.open(fullPath, FILE_APPEND);
if (pathExists == 0) {
Serial.println("File contains no data. Creating headers...");
dataFile.printf("Date: %s | Data Type: %s\n", fileName, DATA_TYPE);
dataFile.println("Time(s),Sw State,Temp(C)");
dataFile.close();
}
else if (pathExists == 1) {
Serial.println("File already contains data.");
}
Serial.print("Beginning data logging...\n\n");
lcd.clear();
} // end first write
// all time information relevant for loop
void timeInfo() {
// use rtc time for data logging while program loops
DateTime now = rtc.now();
year = now.year();
month = now.month() - 1;
day = now.day();
hour = now.hour();
minute = now.minute();
second = now.second();
int DoW = now.dayOfTheWeek();
int formattedHour = hour;
lcd.setCursor(18, 1);
// format time from 24 hour to 12 hour time for LCD readability
switch(hour) {
case 0: formattedHour = hour + 12; lcd.print("am"); break;
case 1 ... 11: formattedHour = hour; lcd.print("am"); break;
case 12: formattedHour = hour; lcd.print("pm"); break;
case 13 ... 23: formattedHour = hour - 12; lcd.print("pm"); break;
} // end switch case
lcd.setCursor(6, 1);
// add ordinal indicator suffixes to date for LCD readability
switch(day) {
case 1: case 21: case 31: lcd.print("st"); break;
case 2: case 22: lcd.print("nd"); break;
case 3: case 23: lcd.print("rd"); break;
case 4 ... 20: case 24 ... 30: lcd.print("th"); break;
} // end switch case
// LCD time buffers
char runtime[24];
char DoWday[8];
char monthTime[16];
// if RTC fails, loop into hardware error function until it is connected
if (!rtc.begin()) {
lcd.clear();
printAll(F("RTC failed"));
hardwareErr();
} // end if
// create new file if a new day occurs
if (hour == 0 && minute == 0 && second == 0 && funcPassThru <= 1) {
funcPassThru += 1;
createFileName();
firstWrite();
} //end if
sprintf(DoWday, "%s %02d", dayName[DoW], day);
sprintf(monthTime, "%s %2d:%02d", monthName[month], formattedHour, minute);
lcd.setCursor(0, 1);
lcd.print(DoWday);
lcd.setCursor(9, 1);
lcd.print(monthTime);
// total run time of datalogging
totMillis = millis() - tStart;
totSeconds = totMillis / 1000;
totMinutes = totSeconds / 60;
totHours = totMinutes / 60;
totDays = totHours / 24;
// compound remainders for run time variables
totSeconds %= 60;
totMinutes %= 60;
totHours %= 24;
totDays %= 8; // 7 days for a week + 1 so the program resets each week
//character buffers for time variables
char charDays[2];
char charHours[3];
char charMinutes[3];
char charSeconds[3];
// these values are converted to formatted strings as float values
// cannot be used in sprintf for my board (Arduino Nano ESP32)
dtostrf(totDays, 1, 0, charDays);
dtostrf(totHours, 2, 0, charHours);
dtostrf(totMinutes, 2, 0, charMinutes);
dtostrf(totSeconds, 2, 0, charSeconds);
sprintf(runtime, "%dd%2dh%2dm%2ds", totDays, totHours, totMinutes, totSeconds);
lcd.setCursor(2, 0);
lcd.print(runtime);
// if run time is over a week, reset the program
if (totDays >= 7) {
resetFunc();
} // end if
// reset function pass through variable
funcPassThru = 0;
} // end timeInfo()
// create and write custom characters for LCD
void customChars() {
// changes the alarm icon that is displayed whenever the alarm is on of off
if (buzzerPinOutState == 1) {
lcd.createChar(3, ALARM_ON_ICON);
} // end if
else if (buzzerPinOutState == 0) {
lcd.createChar(3, ALARM_OFF_ICON);
} // end else if
// change water icon
if (totMillis > tIcon) {
iconNo += 1;
tIcon += WATER_ICON_TM;
} // end if
// switch case statement for water icons
if (floatValue >= 1500 && floatValue <= 2600) {
switch(iconNo) {
case 1: lcd.createChar(4, WATER_1_ICON); break;
case 2: lcd.createChar(4, WATER_2_ICON); break;
case 3: lcd.createChar(4, WATER_3_ICON); break;
case 4: lcd.createChar(4, WATER_4_ICON); break;
case 5: lcd.createChar(4, WATER_5_ICON); break;
case 6: lcd.createChar(4, WATER_6_ICON); break;
} // end switch case
// reset water icon
if (iconNo > 6) {
iconNo = 1;
} // end if
} // end if
else {
lcd.createChar(4, WATER_6_ICON);
} // end else
int signalStrength = WiFi.RSSI();
Serial.println(signalStrength);
// print no signal icon if wifi is not connected
if (WiFi.status() != WL_CONNECTED) {
wifiState = 0;
lcd.createChar(6, NO_SIGNAL_ICON);
lcd.createChar(7, SIGNAL_4_BAR_ICON);
} // end if
else if (WiFi.status() == WL_CONNECTED) wifiState = 1;
// switch case statements are causing errors in the writing of these characters,
// therefore else if statements are being used instead
else if (signalStrength <= -90) {
lcd.createChar(6, SIGNAL_1_BAR_ICON);
lcd.createChar(7, SIGNAL_CLEAR_ICON);
} // end else if
else if (signalStrength >= -89 && signalStrength <= -80) {
lcd.createChar(6, SIGNAL_2_BAR_ICON);
lcd.createChar(7, SIGNAL_CLEAR_ICON);
} // end else if
else if (signalStrength >= -79 && signalStrength <= -70) {
lcd.createChar(6, SIGNAL_3_BAR_ICON);
lcd.createChar(7, SIGNAL_CLEAR_ICON);
} // end else if
else if (signalStrength >= -69) {
lcd.createChar(6, SIGNAL_3_BAR_ICON);
lcd.createChar(7, SIGNAL_4_BAR_ICON);
} // end else if
lcd.createChar(0, SPEAKER_ICON);
lcd.createChar(1, RUNTIME_L_ICON);
lcd.createChar(2, RUNTIME_R_ICON);
// character 3 is dynamic and dependent on the condition of the alarm state
// character 4 is dynamic and dependent on the coniditon of the water icon time
lcd.createChar(5, THERMOMETER_ICON);
// character 6 is dynamic and dependent on the condition of the signal strength
// character 7 is dynamic and dependent on the condition of the signal strength
// write characters to LCD
lcd.setCursor(14, 0);
lcd.write(0);
lcd.setCursor(0, 0);
lcd.write(1);
lcd.setCursor(1, 0);
lcd.write(2);
lcd.setCursor(15, 0);
lcd.write(3);
lcd.setCursor(0, 3);
lcd.write(4);
lcd.setCursor(0, 2);
lcd.write(5);
lcd.setCursor(17, 0);
lcd.write(6);
lcd.setCursor(18, 0);
lcd.write(7);
} // end customChars()
//all relevant code for alarm, as well as button debouncing
void alarmFunc() {
static bool lastBTstate = 0;
unsigned long lastDebounceTime = 0;
const uint8_t DEBOUNCE_DELAY = 50;
bool newBTstate = digitalRead(BUZZER_TOGGLE_SWITCH);
if (newBTstate != lastBTstate) {
lastDebounceTime = totMillis;
} // end if
if ((totMillis - lastDebounceTime) > DEBOUNCE_DELAY) {
if (newBTstate != BTstate) {
BTstate = newBTstate;
if (BTstate == 1) {
buzzerPinState = !buzzerPinState;
} // end if
} // end if
} // end if
lcd.setCursor(7, 0);
if (buzzerPinState == 1) {
buzzerPinOutState = 1;
digitalWrite(RED_ALARM, LOW);
digitalWrite(GREEN_ALARM, HIGH);
} // end if
else if (buzzerPinState == 0) {
buzzerPinOutState = 0;
digitalWrite(GREEN_ALARM, LOW);
digitalWrite(RED_ALARM, HIGH);
} // end else if
lastBTstate = newBTstate;
} // end alarmFunc()
// check the temperature and float error states
void checkForErrs() {
// time intervals for all possible error states
const int FLOAT_SW_TM = 500;
const int HIGH_TEMP_TM = 750;
const int TEMP_ERR_TM = 1000;
const int TEMP_FLOAT_ERR_TM = 250;
const int HIGH_TEMP_FLOAT_ERR_TM = 125;
if (totalErrorCount >= 1) {
lcd.setCursor(19, 0);
lcd.print("*");
} // end if
// if float switch is "NOT OK"
if (floatErrState == 1 && tempErrState == 0 && highTempErrState == 0) {
if (totMillis - lastMillis >= FLOAT_SW_TM) {
lastMillis = totMillis;
if (outputState == 0) {
outputState = 1;
} // end if
else {
outputState = 0;
} // end else
} //end if
if (buzzerPinOutState == 1) {
digitalWrite(BUZZER_PIN, outputState);
} // end if
else {
digitalWrite(BUZZER_PIN, 0);
} // end else
if (outputState == 1) {
analogWrite(RED_LED, 0);
analogWrite(GREEN_LED, 0);
} // end if
analogWrite(BLUE_LED, outputState * 255);
} // end if
// if a temperature error occurs (-127 or 85)
if (tempErrState == 1 && floatErrState == 0) {
if (totMillis - lastMillis >= TEMP_ERR_TM) {
lastMillis = totMillis;
if (outputState == 0) {
outputState = 1;
} // end if
else {
outputState = 0;
} // end else
} // end if
if (buzzerPinOutState == 1) {
digitalWrite(BUZZER_PIN, outputState);
} // end if
else {
digitalWrite(BUZZER_PIN, 0);
} // end else
analogWrite(RED_LED, outputState * 255);
analogWrite(GREEN_LED, outputState * 255);
analogWrite(BLUE_LED, outputState * 255);
if (errCount < 1) {
dataFile = SD.open(fullPath, FILE_APPEND);
dataFile.print("ERROR:");
dataFile.close();
Serial.print("ERROR: ");
errCount+=1;
} // end if
} // end if
// if the temperature is very high and the water level is OK
if (highTempErrState == 1 && floatErrState == 0) {
lcd.setCursor(12, 1);
if (totMillis - lastMillis >= HIGH_TEMP_TM) {
lastMillis = totMillis;
if (outputState == 0) {
outputState = 1;
} // end if
else {
outputState = 0;
} // end else
} // end if
if (buzzerPinOutState == 1) {
digitalWrite(BUZZER_PIN, outputState);
} // end if
else {
digitalWrite(BUZZER_PIN, 0);
} // end else
if (outputState == 1) {
analogWrite(GREEN_LED, 0);
analogWrite(BLUE_LED, 0);
} // end if
analogWrite(RED_LED, outputState * 255);
} // end if
// float error and temperature error
if (floatErrState == 1 && tempErrState == 1) {
if (totMillis - lastMillis >= TEMP_FLOAT_ERR_TM) {
lastMillis = totMillis;
if (outputState == 0) {
outputState = 1;
} // end if
else {
outputState = 0;
} // end else
} // end if
if (buzzerPinOutState == 1) {
digitalWrite(BUZZER_PIN, outputState);
} // end if
else {
digitalWrite(BUZZER_PIN, 0);
} // end else
// flash blue for float error
analogWrite(BLUE_LED, 255);
// then flash white for temperature error
analogWrite(RED_LED, outputState * 255);
analogWrite(GREEN_LED, outputState * 255);
} // end if
// float error and high temperature
if (floatErrState == 1 && highTempErrState == 1) {
if (totMillis - lastMillis >= HIGH_TEMP_FLOAT_ERR_TM) {
lastMillis = totMillis;
if (outputState == 0) {
outputState = 1;
} // end if
else {
outputState = 0;
} // end else
} // end if
if (buzzerPinOutState == 1) {
digitalWrite(BUZZER_PIN, outputState);
} // end if
else {
digitalWrite(BUZZER_PIN, 0);
} // end else
analogWrite(BLUE_LED, outputState * 255);
analogWrite(RED_LED, !outputState * 255);
} // end if
// changes the output state to 0 and sends a low signal to buzzer to ensure hardware isn't stuck
// in a constant error state (e.g. buzzer HIGH or RGB LED constant BLUE_LED)
if (floatErrState == 0 && tempErrState == 0 && highTempErrState == 0) {
outputState = 0;
digitalWrite(BUZZER_PIN, 0);
} // end if
} // end checkForErrs()
// check the state of the float switch to check if the water tank is over flowing
void checkFloatState() {
lcd.setCursor(2, 3);
floatValue = analogRead(FLOAT_SW);
static int floatSmoothingError = 0;
if (floatValue >= 500 && floatValue <= 4000) {
lcd.print("WATER LEVEL OK ");
floatErrState = 0;
floatSmoothingError = 0;
} // end if
else if (floatValue < 500) {
lcd.print("WATER LEVEL HIGH");
floatErrState = 1;
floatSmoothingError = 0;
} // end else if
else if (floatValue > 4000) {
floatSmoothingError += 1;
} // end else if
if (floatSmoothingError >= 2) {
lcd.print("NO SWITCH FOUND ");
floatErrState = 1;
totalErrorCount += 1;
} // end if
Serial.println(floatValue);
} // end checkFloatState()
// check the temperature of the water tank to ensure the water is not too hot for cooling
void checkTemp() {
// if temperature is a valid value
if (tempC >-127 && tempC < 85) {
lcd.setCursor(2, 2);
lcd.print(tempC);
lcd.print("\337C ");
tempErrState = 0;
} // end if
// so temperature values can be used in a switch case statement (easier for code readability)
int intTemp = floor(tempC);
switch(intTemp) {
case -127:
tempErrState = 1;
lcd.setCursor(2, 2);
lcd.print("NO SENSOR FOUND ");
break;
case 4 ... 25:
if (outputState == 0) {
analogWrite(RED_LED, 0);
analogWrite(GREEN_LED, 255);
analogWrite(BLUE_LED, 0);
} // end if
highTempErrState = 0;
break;
case 26 ... 28:
if (outputState == 0) {
analogWrite(RED_LED, 255);
analogWrite(GREEN_LED, 255);
analogWrite(BLUE_LED, 0);
} // end if
highTempErrState = 0;
break;
case 29 ... 31:
if (outputState == 0) {
analogWrite(RED_LED, 255);
analogWrite(GREEN_LED, 70);
analogWrite(BLUE_LED, 0);
} // end if
highTempErrState = 0;
break;
case 32 ... 34:
if (outputState == 0) {
analogWrite(RED_LED, 255);
analogWrite(GREEN_LED, 0);
analogWrite(BLUE_LED, 0);
} // end if
highTempErrState = 0;
break;
case 35 ... 84:
highTempErrState = 1;
break;
case 85:
tempErrState = 1;
lcd.setCursor(2, 2);
lcd.print("CONVERSION ERROR ");
break;
} // end switch case
} // end checkTemp()
// sample the data
void sampleData() {
// store temperature in a float variable
tempC = sensors.getTempCByIndex(0);
char sampleData[150];
// sample data within the sampling interval
if (totMillis > tSample) {
totT1 = tempC + totT1;
aveT1 = totT1 / nSample;
dtostrf(aveT1, 5, 2, charAveT1);
dtostrf(totT1, 7, 2, charTotT1);
dtostrf(tempC, 7, 2, charTemp);
sprintf(sampleData, "Time: %02d:%02d:%02d | Runtime: %02dd %02dh %02dm %02ds | Sample: %d | Sw: %d | temp(°C):%s | Ave: %s | Tot:%s\n",
hour, minute, second, totDays, totHours, totMinutes, totSeconds,
nSample, !floatErrState, charTemp, charAveT1, charTotT1);
Serial.print(sampleData);
nSample +=1;
tSample += SAMPLE_TM;
sensors.requestTemperatures();
} // end if
aveT1 = totT1 / nSample;
} // end sampleData()
// log the data
void logSocketData() {
WiFiClient client;
if(WiFi.status() == WL_CONNECTED && socketConnectState == 1) {
if(!client.connect(host, port)) {
Serial.print("\n\nConnection to host failed\n\n");
}
}
// converts the time of day to seconds for the logging file
unsigned long hToM = hour * 60;
unsigned long hToS = hToM * 60;
unsigned long mToS = (minute * 60);
unsigned long sToS = second;
unsigned long tmDayInS = hToS + mToS + sToS;
char data[20];
char charTmDayInS[7];
dtostrf(tmDayInS, 5, 0, charTmDayInS);
sprintf(data, "%s,%d,%s", charTmDayInS, !floatErrState, charAveT1);
dataFile = SD.open(fullPath, FILE_APPEND);
if (!dataFile) {
lcd.clear();
printAll(F("File write Err"));
hardwareErr();
} // end if
static int logCount = 0;
if (nSample == 9) {
nSample -=1;
dataFile.println(data);
dataFile.close();
if(wifiState == 1 && socketConnectState == 1) {
client.print(data);
client.stop();
}
logCount+=1;
Serial.printf("\n LOG NO.%d\n", logCount);
Serial.println("| Time(s) | Float Switch State | Average Temperature(°C) |");
Serial.printf("| %s | %d | %s |\n\n",
charTmDayInS, !floatErrState, charAveT1);
totT1 = 0;
nSample = 1;
errCount = 0;
} // end if
} // end logData()
// setup function
void setup() {
Wire.begin();
Serial.begin(BAUD);
delay(1500);
Wire.setClock(400000);
printResetReason();
initPinModes();
initGPIOstates();
initLCD();
initDS18B20();
initSD();
initRTC();
initWiFi();
createFileName();
firstWrite();
tStart = millis();
} // end setup()
// loop function
void loop() {
timeInfo();
customChars();
alarmFunc();
checkForErrs();
checkFloatState();
checkTemp();
sampleData();
logSocketData();
} // end loop()
// END OF PROGRAM