For a diagnostics and datalogging device I've been building for a while now, I'm using a float switch to determine whether a water tanks water level is okay or not. I noticed that it would be an issue if there were any hardware connection problems, e.g. the float switch cable, for whatever reason, stops receiving signals, either by a disconnection, a loose connection etc. On my breadboard, I was using resistors to see if I could use an analogue pin and have differing values: I found that if I used a 100K resistor and placed it between the float switch pin (A3 pin) and GND, the values would be different and were consistent enough for me to be confident that I could use that in my circuit. The values I get on my PC's serial monitor from the analogue pin for the float switch is ~3600, with the exception of a few 4095 readings, which I've compensated for in my sketch. I have the circuit created on a Vero board and everything was working fine, until I tried to use my laptop as a power supply with a different cable. The readings I were getting were 4095 when the switch was open and connected. I tried the original cable on my laptop and it was still giving me a reading of 4095. I took the other cable to my PC and tried it, which gives me- again- 4095. I tested the voltage across VBUS and GND and I was getting ~5V on both cables and both computers. I have no idea why this is happening. I'll attach an image of my circuit and my sketch. Any help would be appreciated. Thanks!
Sketch:
//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
#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);
//time variables
RTC_DS3231 rtc;
time_t now;
struct tm myTimeInfo;
unsigned long tStart = millis();
unsigned long totMillis;
int totSeconds;
int totMinutes;
int totHours;
int totDays;
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;
static int year;
static int month;
static int day;
static int hour;
static int minute;
static int second;
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
uint8_t funcPassThru = 0;
uint8_t iconNo = 1;
//SD card global variables
char dirName[9] = "/TANKLOG";
//fileName and fullPath specified 24 uint8_ts to avoid format overflow
char fileName[24];
char fullPath[24];
File dataFile;
//get time from NTP server and use WiFi
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);
int WiFiTimeout;
//only used for water tank logging (change for any other type of data)
#include <OneWire.h>
#include <DallasTemperature.h>
#define floatSw 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
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);
}
if(SD.begin(CHIP_SELECT) && rtc.begin()) {
lcd.clear();
resetFunc();
}
}
//show the reason for the last reset of the ESP32
int printResetReason() {
Serial.print("\n\n\n");
int reason = esp_reset_reason();
Serial.printf("CPU reset reason: %d\n", 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;
}
return(reason);
}
//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");
Serial.print(tankTempSensor[i], HEX);
}
}
//initialise pin configurations and begin serial communication and LCD
void init() {
pinMode(BUZZER_PIN, OUTPUT);
pinMode(BUZZER_TOGGLE_SWITCH, INPUT_PULLUP);
pinMode(floatSw, INPUT);
pinMode(RED_LED, OUTPUT);
pinMode(GREEN_LED, OUTPUT);
pinMode(BLUE_LED, OUTPUT);
pinMode(RED_ALARM, OUTPUT);
pinMode(GREEN_ALARM, OUTPUT);
digitalWrite(RED_LED, LOW);
digitalWrite(GREEN_LED, HIGH);
digitalWrite(BLUE_LED, LOW);
lcd.begin(20, 4);
delay(2000);
lcd.setBacklight(255);
lcd.clear();
}
//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);
}
//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();
}
uint8_t cardType = SD.cardType();
if(cardType == CARD_NONE) {
lcd.clear();
printAll("No SD card attached");
hardwareErr();
}
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);
}
//initialise the RTC
void initRTC(){
lcd.setCursor(0, 1);
printAll("Initialising RTC...");
delay(1000);
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();
}
lcd.clear();
lcd.setCursor(0, 0);
}
//initialise WiFi
void initWiFi() {
Serial.print("Connecting to WiFi...");
lcd.print("Connecting to WiFi..");
WiFi.begin(Triogen_ssid, Triogen_password);
while(WiFi.status() != WL_CONNECTED) {
Serial.print(".");
delay(1000);
WiFiTimeout+=1;
if(WiFiTimeout == 15) {
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");
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);
DateTime now = rtc.now();
year = now.year();
month = now.month();
day = now.day();
hour = now.hour();
minute = now.minute();
second = now.second();
break;
}
}
if(WiFi.status() == WL_CONNECTED){
lcd.setCursor(0, 1);
Serial.println();
printAll("WIFI CONNECTED");
initNTPtime();
}
}
//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(".");
}
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;
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);
}
//create the file name and directory for data logging
void createFileName() {
sprintf(fileName, "%02d-%02d-%d.txt", day, month, year - 2000);
strlcpy(fullPath, dirName, sizeof fullPath);
strlcat(fullPath, "/", sizeof fullPath);
strlcat(fullPath, fileName, sizeof fullPath);
SD.mkdir(dirName);
Serial.printf("Full File Path: X:%s \n\n", fullPath);
}
//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);
dataFile.printf("Date: %s\n", fileName);
dataFile.println("Time(s),Sw State,Temp(°C)");
dataFile.close();
lcd.clear();
}
//all time information relevant for loop
void timeInfo() {
//use rtc time for data logging
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);
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;
}
lcd.setCursor(6, 1);
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;
}
char runtime[24];
char DoWday[8];
char monthTime[16];
if(!rtc.begin()) {
lcd.clear();
printAll(F("RTC failed"));
hardwareErr();
}
//create new file if a new day occurs
if(hour == 0 && minute == 0 && second == 0 && funcPassThru <= 2) {
funcPassThru += 1;
createFileName();
firstWrite();
}
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;
totSeconds %= 60;
totMinutes %= 60;
totHours %= 24;
totDays %= 7;
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
dtostrf(totDays, 1, 0, charDays);
dtostrf(totHours, 2, 0, charHours);
dtostrf(totMinutes, 2, 0, charMinutes);
dtostrf(totSeconds, 2, 0, charSeconds);
sprintf(runtime, "%sd%sh%sm%ss", charDays, charHours, charMinutes, charSeconds);
lcd.setCursor(2, 0);
lcd.print(runtime);
funcPassThru = 0;
}
//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);
}
else if(buzzerPinOutState == 0) {
lcd.createChar(3, ALARM_OFF_ICON);
}
if(totMillis > tIcon) {
iconNo += 1;
tIcon += WATER_ICON_TM;
}
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;
}
if(iconNo > 6) {
iconNo = 1;
}
}
else {
lcd.createChar(4, WATER_6_ICON);
}
int signalStrength = WiFi.RSSI();
if(WiFi.status() != WL_CONNECTED) {
lcd.createChar(6, NO_SIGNAL_ICON);
lcd.createChar(7, SIGNAL_4_BAR_ICON);
}
//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);
}
else if (signalStrength >= -89 && signalStrength <= -80) {
lcd.createChar(6, SIGNAL_2_BAR_ICON);
lcd.createChar(7, SIGNAL_CLEAR_ICON);
}
else if (signalStrength >= -79 && signalStrength <= -70) {
lcd.createChar(6, SIGNAL_3_BAR_ICON);
lcd.createChar(7, SIGNAL_CLEAR_ICON);
}
else if (signalStrength >= -69) {
lcd.createChar(6, SIGNAL_3_BAR_ICON);
lcd.createChar(7, SIGNAL_4_BAR_ICON);
}
lcd.createChar(0, SPEAKER_ICON);
lcd.createChar(1, RUNTIME_L_ICON);
lcd.createChar(2, RUNTIME_R_ICON);
//3 is dynamic dependent on conditions
//4 is dynamic dependent on conditions
lcd.createChar(5, THERMOMETER_ICON);
//6 is dynamic dependent on conditions
//7 is dynamic dependent on conditions
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);
}
//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("*");
}
//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;
}
else {
outputState = 0;
}
}
if(buzzerPinOutState == 1) {
digitalWrite(BUZZER_PIN, outputState);
}
else {
digitalWrite(BUZZER_PIN, 0);
}
if(outputState == 1) {
analogWrite(RED_LED, 0);
analogWrite(GREEN_LED, 0);
}
analogWrite(BLUE_LED, outputState * 255);
}
//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;
}
else {
outputState = 0;
}
}
if(buzzerPinOutState == 1) {
digitalWrite(BUZZER_PIN, outputState);
}
else {
digitalWrite(BUZZER_PIN, 0);
}
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;
}
}
//if the temperature is very high
if(highTempErrState == 1 && floatErrState == 0) {
lcd.setCursor(12, 1);
if(totMillis - lastMillis >= HIGH_TEMP_TM) {
lastMillis = totMillis;
if(outputState == 0) {
outputState = 1;
}
else {
outputState = 0;
}
}
if(buzzerPinOutState == 1) {
digitalWrite(BUZZER_PIN, outputState);
}
else {
digitalWrite(BUZZER_PIN, 0);
}
if(outputState == 1) {
analogWrite(GREEN_LED, 0);
analogWrite(BLUE_LED, 0);
}
analogWrite(RED_LED, outputState * 255);
}
//float error and temperature error
if(floatErrState == 1 && tempErrState == 1) {
if(totMillis - lastMillis >= TEMP_FLOAT_ERR_TM) {
lastMillis = totMillis;
if(outputState == 0) {
outputState = 1;
}
else {
outputState = 0;
}
}
if(buzzerPinOutState == 1) {
digitalWrite(BUZZER_PIN, outputState);
}
else {
digitalWrite(BUZZER_PIN, 0);
}
//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);
}
//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;
}
else {
outputState = 0;
}
}
if(buzzerPinOutState == 1) {
digitalWrite(BUZZER_PIN, outputState);
}
else {
digitalWrite(BUZZER_PIN, 0);
}
analogWrite(BLUE_LED, outputState * 255);
analogWrite(RED_LED, !outputState * 255);
}
//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);
}
}
//check the state of the float switch to check if the water tank is over flowing
void checkFloatState() {
lcd.setCursor(2, 3);
floatValue = analogRead(floatSw);
static int floatSmoothingError = 0;
if(floatValue >= 1500 && floatValue <= 3700) {
lcd.print("WATER LEVEL OK ");
floatErrState = 0;
floatSmoothingError = 0;
}
else if(floatValue < 200) {
lcd.print("WATER LEVEL HIGH");
floatErrState = 1;
floatSmoothingError = 0;
}
else if (floatValue > 4000) {
floatSmoothingError += 1;
}
if(floatSmoothingError >= 5) {
lcd.print("NO SWITCH FOUND ");
floatErrState = 1;
totalErrorCount += 1;
}
Serial.println(floatValue);
}
//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;
}
//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);
}
highTempErrState = 0;
break;
case 26 ... 28:
if(outputState == 0) {
analogWrite(RED_LED, 255);
analogWrite(GREEN_LED, 255);
analogWrite(BLUE_LED, 0);
}
highTempErrState = 0;
break;
case 29 ... 31:
if(outputState == 0) {
analogWrite(RED_LED, 255);
analogWrite(GREEN_LED, 70);
analogWrite(BLUE_LED, 0);
}
highTempErrState = 0;
break;
case 32 ... 34:
if(outputState == 0) {
analogWrite(RED_LED, 255);
analogWrite(GREEN_LED, 0);
analogWrite(BLUE_LED, 0);
}
highTempErrState = 0;
break;
case 35 ... 84:
highTempErrState = 1;
break;
case 85:
tempErrState = 1;
lcd.setCursor(2, 2);
lcd.print("CONVERSION ERROR ");
break;
}
}
//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();
}
aveT1 = totT1 / nSample;
}
//log the data
void logData() {
//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 aveData[20];
char charTmDayInS[7];
dtostrf(tmDayInS, 5, 0, charTmDayInS);
sprintf(aveData, "%s,%d,%s", charTmDayInS, !floatErrState, charAveT1);
dataFile = SD.open(fullPath, FILE_APPEND);
if(!dataFile) {
lcd.clear();
printAll(F("File write Err"));
hardwareErr();
}
static int logCount = 0;
if(nSample == 9) {
nSample -=1;
dataFile.println(aveData);
dataFile.close();
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;
}
}
//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;
}
if((totMillis - lastDebounceTime) > DEBOUNCE_DELAY) {
if(newBTstate != BTstate) {
BTstate = newBTstate;
if(BTstate == 1) {
buzzerPinState = !buzzerPinState;
}
}
}
lcd.setCursor(7, 0);
if(buzzerPinState == 1) {
buzzerPinOutState = 1;
digitalWrite(RED_ALARM, LOW);
digitalWrite(GREEN_ALARM, HIGH);
}
else if(buzzerPinState == 0) {
buzzerPinOutState = 0;
digitalWrite(GREEN_ALARM, LOW);
digitalWrite(RED_ALARM, HIGH);
}
lastBTstate = newBTstate;
}
//setup function
void setup() {
Wire.begin();
Serial.begin(BAUD);
delay(1500);
Wire.setClock(400000);
printResetReason();
init();
initDS18B20();
initSD();
initRTC();
initWiFi();
createFileName();
firstWrite();
tStart = millis();
}
//loop function
void loop() {
timeInfo();
customChars();
checkForErrs();
checkFloatState();
checkTemp();
sampleData();
logData();
alarmFunc();
}
// END OF PROGRAM
/********************************************************************************************************************************************************************************/

