3 examples where i am using different commands to be able to allow the project to compile
//original calculation I run
Serial.print(tdsValue_SUP, 0); Serial.print("ppm"); //this gives me the number rounded up eg 164
//needed to send to mqtt
sprintf(mqtt_tds_SUP, "%d",(int)tdsValue_SUP); //this gives me a number rounded down eg 163
//needed to be able to view on html
web_currSUPPLY = (int)tdsValue_SUP; = gives me the number rounded down eg 163
likely i have this all wrong but evertime i failed to compile i was given a new way to present the data and thus i have ended up with 3 different types, 1 rounds down and 2 round up.
Wow! Thank you, this is exactly what I needed, it’s easy for me to forget to go and learn each line before I type it or trouble shoot it.
Essentially, the floating number is exactly what I care about, then beyond that, it is exactly what you eluded to, I care about the human interaction with that value.
I want that float value to be sent to:
An Matt broker
The serial
A webpage
An lcd (I’ll do this last)
So I am very agnostic to being given a better/correct way to do the above
I kept the OP super vague because most times I post a lot of info and it seems to give people the impression I know what I’m doing or it’s TLDR.
If you are happy to read through my whole code I can paste it? Or if you just prefer I post the header section (where all the pins/variables/libraries are defined) and the sections relating to what I need, just let me know
I was thinking it could give context, but I suppose you are trying to help me understand rather than me getting you to just tell me the answer, which I highly appreciate.
It is interesting, I am sending the data twice, I have it sending to the mqtt device i created over home assistant discovery, and also to another topic in case the mqtt discovery device fails in any way
So if I am trying to learn from what you taught and google definitions, this is me using a string print, sending an integer, so it would mean it is expecting a string, and I would now be asking 'how do I send my float value over a string print to 0 decimal places'?
Ok for this one, I dont know how to work out what exactly the webpage/html expects, as I am still learning on this also (some would say I am out of my depth but I am a quick learner, i think)
existing ways that data presents is via the following lines of code:
So again, using what you have taught, this means my question must still be 'how do I send my float value over a string to 0 decimal places'? or how do I create a string from a float value, to 0 decimal places?
I think I might be seeing how I ended up with an integer
So basically if I understand correctly, it is saying, almost that you are converting a number(float/int) to text(string) and in the same way, if my string has representation of a number, i cant use a string in an equation/calculation?
This is where I am scared to say, I am using a Firebeetle 2 ESP32-E, but the challenges I have experienced I dont believe are affected by this, however happy to be corrected.
now you say Char arrays I realise why you need to see more
//Include Libraries
#include <Arduino.h>
#include <FS.h> //Arduino ESP8266 Core - Handles filesystem functions (read/write config file)
#include <LCD_I2C.h>
#include <FastLED.h> //https://github.com/FastLED/FastLED - LED functionality
#include <EEPROM.h>
#include "GravityTDS.h"
#include <Arduino.h>
#include <WiFiManager.h> //https://github.com/tzapu/WiFiManager (must be v2.0.8-beta or later) - Wifi Onboarding with Portal
#include <PubSubClient.h> //https://github.com/knolleary/pubsubclient Provides MQTT functions
#include <ArduinoJson.h> //https://github.com/bblanchon/ArduinoJson
#include <WebServer.h> //Used for HTTP callback to enable/disable discovery
#include <HTTPUpdateServer.h>
#include <ArduinoOTA.h> //https://github.com/jandrassy/ArduinoOTA
#include <ESPmDNS.h>
#include <Update.h>
#ifdef ESP32
#include <SPIFFS.h>
#endif
#define VERSION "Firebeetle 2 (ESP32)"
//Setup RGB LED for Feedback
// Only one led is connected. The led is connected on IO5
#define LED_DATA_PIN 5
#define WIFIMODE 2 // 0 = Only Soft Access Point, 1 = Only connect to local WiFi network with UN/PW, 2 = Both
#define MQTTMODE 1 // 0 = Disable MQTT, 1 = Enable (will only be enabled if WiFi mode = 1 or 2 - broker must be on same network)
//#define SERIAL_DEBUG 0 // 0 = Disable (must be disabled if using RX/TX pins), 1 = enable
#define NUM_LEDS_MAX 1 // For initialization - recommend actual max 50 LEDs if built as shown
//Define PinOut
#define TDS_SUP_Pin A0
#define TDS_PRE_DI_Pin A1
#define TDS_OUT_Pin A2
//CRGB leds[NUM_LEDS];
//PubSubClient
WiFiClient espClient;
PubSubClient client(espClient);
//WebServer
WebServer server(80);
//WebServer server;
HTTPUpdateServer httpUpdater;
//const char* serverIndex = "<form method='POST' action='/update' enctype='multipart/form-data'><input type='file' name='update'><input type='submit' value='Update'></form>";
const char* host = "TDSMonitor-webupdate";
const char* mqtt_server = "10.0.0.8";
//const char* mqtt_port = "1883";
const char* mqtt_client = "TDSMonitor";
const char* mqtt_pub_topic_supTDS = "Aquarium/TDSMonitor/Sensor/supTDS";
const char* mqtt_pub_topic_prediTDS = "Aquarium/TDSMonitor/Sensor/prediTDS";
const char* mqtt_pub_topic_outTDS = "Aquarium/TDSMonitor/Sensor/outTDS";
const char* mqtt_sub_topic = "Aquarium";
const char* mqtt_supTDS_stat = "homeassistant/stat/TDSMonitor/supTDS";
const char* mqtt_prediTDS_stat = "homeassistant/stat/TDSMonitor/prediTDS";
const char* mqtt_outTDS_stat = "homeassistant/stat/TDSMonitor/outTDS";
const char* mqtt_IP_stat = "homeassistant/stat/TDSMonitor/ipaddress";
//Auto-discover enable/disable option
bool auto_discovery = false; //default to false and provide end-user interface to allow toggling
//Arduino OTA
bool ota_flag = true; // Must leave this as true for board to broadcast port to IDE upon boot
uint16_t ota_boot_time_window = 2500; // minimum time on boot for IP address to show in IDE ports, in millisecs
uint16_t ota_time_window = 20000; // time to start file upload when ota_flag set to true (after initial boot), in millsecs
uint16_t ota_time_elapsed = 0; // Counter when OTA active
uint16_t ota_time = ota_boot_time_window;
//Define device name
String deviceName = "TDSMonitor";
String wifiHostName = "TDSMonitor";
String otaHostName = "TDSMonitorOTA";
String mqttClient = "TDSMonitor";
// ===============================
// MQTT Variables
// ===============================
// MQTT will only be used if a server address other than '0.0.0.0' is entered via portal
byte mqttAddr_1 = 0;
byte mqttAddr_2 = 0;
byte mqttAddr_3 = 0;
byte mqttAddr_4 = 0;
int mqttPort = 0;
String mqttUser = "myusername";
String mqttPW = "mypassword";
uint16_t mqttTelePeriod = 60;
uint32_t mqttLastUpdate = 0;
String mqttTopicSub ="TDSMonitor"; //v0.41 (for now, will always be same as pub)
String mqttTopicPub = "TDSMonitor"; //v0.41
bool mqttEnabled = false; //Will be enabled/disabled depending on whether a valid IP address is defined in Settings (0.0.0.0 disables MQTT)
bool mqttConnected = false; //Will be enabled if defined and successful connnection made. This var should be checked upon any MQTT action.
bool prevCarStatus = false; //v0.44 for forcing MQTT update on state change
bool forceMQTTUpdate = false; //v0.44 for forcing MQTT update on state change
//Variables for creating unique entity IDs and topics (HA discovery)
byte macAddr[6]; //Device MAC address (array is in reverse order)
String strMacAddr; //MAC address as string and in proper order
char uidPrefix[] = "TDSMonitor"; //Prefix for unique ID generation
char devUniqueID[30]; //Generated Unique ID for this device (uidPrefix + last 6 MAC characters)
//---- Captive Portal -------
//flag for saving data in captive portal
bool shouldSaveConfig = false;
//callback notifying us of the need to save config
void saveConfigCallback () {
shouldSaveConfig = true;
}
//---------------------------
//Calibration Variables
byte web_currSUP = 0;
byte web_currPREDI = 0;
byte web_currOUT = 0;
byte web_calSUP = 0;
byte web_calPREDI = 0;
byte web_calOUT = 0;
byte calSUP = 0;
byte calPREDI = 0;
byte calOUT = 0;
//VARIABLES FOR PORTAL USE (JSON vars)
char device_name[18]; //v0.41
char wifi_hostname[18]; //v0.41
char ota_hostname[18]; //v0.41
char mqtt_addr_1[4];
char mqtt_addr_2[4];
char mqtt_addr_3[4];
char mqtt_addr_4[4];
char mqtt_port[6];
char mqtt_user[65];
char mqtt_pw[65];
char mqtt_tele_period[4];
char mqtt_topic_sub[18]; //v0.41
char mqtt_topic_pub[18]; //v0.41
String home_page_message = "";
WiFiManager wifiManager;
WiFiManager wm;
String localIP;
GravityTDS gravityTds_SUP;
GravityTDS gravityTds_PRE_DI;
GravityTDS gravityTds_OUT;
char mqtt_tds_SUP[50];
char mqtt_tds_PRE_DI[50];
char mqtt_tds_OUT[50];
char mqtt_tds_SUP2[50];
char mqtt_tds_PRE_DI2[50];
char mqtt_tds_OUT2[50];
float temperature = 25;
float tdsValue_SUP = 0;
float tdsValue_PRE_DI = 0;
float tdsValue_OUT = 0;
float kValue_SUP = 0;
float kValue_PRE_DI = 0;
float kValue_OUT = 0;
int prevtdsValue_SUP;
int prevtdsValue_PRE_DI;
int prevtdsValue_OUT;
//==========================
// LED Setup & Portal Options
//==========================
// Defaults values - these will be set/overwritten by portal or last saved vals on reboot
int numLEDs = 30;
byte activeBrightness = 100;
byte sleepBrightness = 5;
uint32_t maxOperationTimePark = 60;
uint32_t maxOperationTimeExit = 5;
String ledEffect_m1 = "Out-In";
bool showStandbyLEDs = true;
CRGB LEDs[NUM_LEDS_MAX];
CRGB ledColorOn_m1 = CRGB::White;
CRGB ledColorOff = CRGB::Black;
CRGB ledColorStandby = CRGB::Blue;
CRGB ledColorWake = CRGB::Green;
CRGB ledColorActive = CRGB::Yellow;
CRGB ledColorParked = CRGB::Red;
CRGB ledColorBackup = CRGB::Red;
//************************** Just Some basic Definitions used for the Up Time Logger ************//
long Day=0;
int Hour =0;
int Minute=0;
int Second=0;
int HighMillis=0;
int Rollover=0;
unsigned long previousMillis = 0;
unsigned long interval = 10000;
int TransmitInterval = interval;
// constants do not change:
int CurrentRSSI=100;
/*
void RGB_LED_Setup(){
// Configure an array of (one) leds without SPI
FastLED.addLeds<NEOPIXEL, DATA_PIN>(leds, NUM_LEDS);
}
void RGB_LED(){
leds[0] = CRGB::Black;
FastLED.show();
}
*/
//OTHER GLOBAL VARIABLES
bool blinkOn = false;
int intervalDistance = 0;
bool carDetected = false;
bool isAwake = false;
bool coldStart = true;
byte carDetectedCounter = 0;
byte carDetectedCounterMax = 3;
byte nocarDetectedCounter = 0;
byte nocarDetectedCounterMax = 10;
byte outOfRangeCounter = 0;
uint32_t startTime;
bool exitSleepTimerStarted = false;
bool parkSleepTimerStarted = false;
//Initial distances for default load (only used for initial onboarding)
byte uomDistance = 0; // 0=inches, 1=centimeters
int wakeDistance = 3048; // wake/sleep distance (~10ft)
int startDistance = 1829; // Start countdown distance (~6')
int parkDistance = 610; // Final parked distacce (~2')
int backupDistance = 457; // Flash backup distance (~18")
//===============================
// Web pages and handlers
//===============================
// Main Settings page
// Root / Main Settings page handler
void handleRoot() {
String mainPage = "<html>\
<head>\
<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\
<title>TDS Monitor - Main</title>\
<style>\
body { background-color: #cccccc; font-family: Arial, Helvetica, Sans-Serif; Color: #000088; }\
</style>\
</head>\
<body>\
<h1>Controller Settings (VAR_DEVICE_NAME)</h1><br>\
Changes made here will be used <b><i>until the controller is restarted</i></b>, unless the box to save the settings as new boot defaults is checked.<br><br>\
To test settings, leave the box unchecked and click 'Update'. Once you have settings you'd like to keep, check the box and click 'Update' to write the settings as the new boot defaults.<br><br>\
If you want to change wifi settings or the device name, you must use the 'Reset All' command.<br><br>\
<form method=\"post\" enctype=\"application/x-www-form-urlencoded\" action=\"/postform/\">\
<table>\
<tr>\
<b><u>MQTT Settings</u></b>:<br>\
ONLY enter this information if you already have an MQTT broker configured. <u><i>To disable or remove MQTT functionality, set the IP address to 0.0.0.0</i></u><br>\
Any changes to MQTT require that you check the box to update boot settings below, which will reboot the controller. If a successful connection to your MQTT broker is made, \
a retained message of \"connected\" will be published to the topic \"homeassistant/stat/your_topic/mqtt\".<br><br>";
mainPage += "<table>\
<tr>\
<td><label for=\"mqttaddr1\">Broker IP Address:</label></td>\
<td><input type=\"number\" min=\"0\" max=\"255\" step=\"1\" name=\"mqttaddr1\" style=\"width: 50px\;\" value=\"";
mainPage += String(mqttAddr_1);
mainPage += "\">.<input type=\"number\" min=\"0\" max=\"255\" step=\"1\" name=\"mqttaddr2\" style=\"width: 50px\;\" value=\"";
mainPage += String(mqttAddr_2);
mainPage += "\">.<input type=\"number\" min=\"0\" max=\"255\" step=\"1\" name=\"mqttaddr3\" style=\"width: 50px\;\" value=\"";
mainPage += String(mqttAddr_3);
mainPage += "\">.<input type=\"number\" min=\"0\" max=\"255\" step=\"1\" name=\"mqttaddr4\" style=\"width: 50px\;\" value=\"";
mainPage += String(mqttAddr_4);
mainPage += "\"></td></tr>\
<tr>\
<td><label for=\"mqttport\">MQTT Broker Port:</label></td>\
<td><input type=\"number\" min=\"0\" max=\"65535\" step=\"1\" name=\"mqttport\" style=\"width: 65px\;\" value=\"";
mainPage += String(mqttPort);
mainPage += "\"></td></tr>\
<tr>\
<td><label for=\"mqttuser\">MQTT User Name:</label></td>\
<td><input type=\"text\" name=\"mqttuser\" maxlength=\"64\" value=\"";
mainPage += mqttUser;
mainPage += "\"></td></tr>\
<tr>\
<td><label for=\"mqttpw\">MQTT Password:</label></td>\
<td><input type=\"password\" name=\"mqttpw\" maxlength=\"64\" value=\"";
mainPage += mqttPW;
mainPage += "\"></td></tr>\
<tr>\
<td><label for=\"mqtttopic\">MQTT Topic: stat/</label></td>\
<td><input type=\"text\" name=\"mqtttopic\" maxlength=\"16\" value=\"";
mainPage += mqttTopicPub;
mainPage += "\"> (16 alphanumeric chars max - no spaces, no symbols)</td></tr>\
<tr>\
<td><label for=\"mqttperiod\">Telemetry Period:</label></td>\
<td><input type=\"number\" min=\"60\" max=\"600\" step=\"1\" name=\"mqttperiod\" style=\"width: 50px\;\" value=\"";
mainPage += String(mqttTelePeriod);
mainPage += "\"> seconds (60 min, 600 max)</td></tr>\
<tr>\
<td><label for=\"discovery\">MQTT Discovery:</label></td>\
<td><a href=\"http://";
mainPage += localIP;
mainPage += "/discovery\">Configure Home Assistant MQTT Discovery</a>";
mainPage += "</td></tr>\
<td><label for=\"calibration\">Sensor Calibration:</label></td>\
<td><a href=\"http://";
mainPage += localIP;
mainPage += "/calibration\">Calibrate TDS Sensors</a>";
mainPage += "</td></tr>\
</table><br>\
<input type=\"checkbox\" name=\"chksave\" value=\"save\">Save all settings as new boot defaults (controller will reboot)<br><br>\
<input type=\"submit\" value=\"Update\">\
</form>\
<br>\
<h2>Controller Commands</h2>\
Caution: Restart and Reset are executed immediately when the button is clicked.<br>\
<table border=\"1\" cellpadding=\"10\">\
<tr>\
<td><button id=\"btnrestart\" onclick=\"location.href = './restart';\">Restart</button></td><td>This will reboot controller and reload default boot values.</td>\
</tr><tr>\
<td><button id=\"btnreset\" style=\"background-color:#FAADB7\" onclick=\"location.href = './reset';\">RESET ALL</button></td><td><b>WARNING</b>: This will clear all settings, including WiFi! You must complete initial setup again.</td>\
</tr><tr>\
<td><button id=\"btnupdate\" onclick=\"location.href = './update';\">Firmware Upgrade</button></td><td>Upload and apply new firmware from local file.</td>\
</tr></table><br>\
Current version: VAR_CURRRENT_VER\
</body>\
</html>";
mainPage.replace("VAR_DEVICE_NAME", deviceName);
mainPage.replace("VAR_CURRRENT_VER", VERSION);
server.send(200, "text/html", mainPage);
}
// Settings submit handler - Settings results
void handleForm() {
if (server.method() != HTTP_POST) {
server.send(405, "text/plain", "Method Not Allowed");
} else {
String saveSettings;
mqttAddr_1 = server.arg("mqttaddr1").toInt();
mqttAddr_2 = server.arg("mqttaddr2").toInt();
mqttAddr_3 = server.arg("mqttaddr3").toInt();
mqttAddr_4 = server.arg("mqttaddr4").toInt();
mqttPort = server.arg("mqttport").toInt();
mqttUser = server.arg("mqttuser");
mqttPW = server.arg("mqttpw");
mqttTelePeriod = server.arg("mqttperiod").toInt();
mqttTopicSub = server.arg("mqtttopic");
mqttTopicPub = server.arg("mqtttopic");
saveSettings = server.arg("chksave");
String message = "<html>\
</head>\
<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\
<title>TDS Monitor - Current Settings</title>\
<style>\
body { background-color: #cccccc; font-family: Arial, Helvetica, Sans-Serif; Color: #000088; }\
</style>\
</head>\
<body>\
<H1>Settings updated!</H1><br>\
<H3>Current values are:</H3>";
//example of message return types
//message += "Device Name: " + deviceName + "<br>";
//message += "Num LEDs: " + server.arg("leds") + "<br>";
//message += "Active Brightness: " + server.arg("activebrightness") + "<br>";
//message += "Standby Brightness: " + server.arg("sleepbrightness") + "<br><br>";
//message += "<b>LED active On Times</b><br><br>";
//message += "Park Time: " + server.arg("ledparktime") + " secs.<br>";
//message += "Exit Time: " + server.arg("ledexittime") + " secs.<br><br>";
//message += "Effect: " + server.arg("effect1") + "<br><br>";
message += "<b>MQTT Settings</b><br><br>";
if ((mqttAddr_1 == 0) && (mqttAddr_2 == 0) && (mqttAddr_3) == 0 && (mqttAddr_4 == 0)) {
message += "MQTT: <b><u>Disabled</u></b> ";
if (saveSettings != "save") {
message += "(If you just changed MQTT settings, you must save as new boot defaults for this to take effect)<br>";
}
} else {
message += "MQTT Server: " + server.arg("mqttaddr1") + "." + server.arg("mqttaddr2") + "." + server.arg("mqttaddr3") + "." + server.arg("mqttaddr4") + "<br>";
message += "MQTT Port: " + server.arg("mqttport") + "<br>";
message += "MQTT User: " + server.arg("mqttuser") + "<br>";
message += "MQTT Password: ***********<br>";
message += "MQTT Topic: homeassistant/stat/" + server.arg("mqtttopic") + "<br>";
message += "Telemetry Period: " + server.arg("mqttperiod") + " seconds<br>";
if (saveSettings != "save") {
message += "<br>(If you just changed MQTT settings, you must save as new boot defaults for this to take effect)<br>";
}
}
message += "<br>";
if (saveSettings == "save") {
message += "<br>";
message += "<b>New settings saved as boot defaults.</b> Controller will now reboot.<br>";
message += "You can return to the settings page after boot completes (lights will briefly turn blue then red/green to indicate completed boot).<br>";
} else {
//Wake up system so new setting can be seen/tested... even if car present
carDetectedCounter = carDetectedCounterMax + 1;
}
message += "<br><a href=\"http://";
message += localIP;
message += "/discovery\">Configure Home Assistant MQTT Discovery</a><br>";
message += "<br><a href=\"http://";
message += localIP;
message += "\">Return to settings</a><br>";
message += "</body></html>";
server.send(200, "text/html", message);
delay(1000);
if (saveSettings == "save") {
updateSettings(true);
} else {
updateSettings(false);
}
}
}
// Firmware update handler
void handleUpdate() {
String updFirmware = "<html>\
</head>\
<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\
<title>TDS Monitor - Firmware Update</title>\
<style>\
body { background-color: #cccccc; font-family: Arial, Helvetica, Sans-Serif; Color: #000088; }\
</style>\
</head>\
<body>\
<H1>Firmware Update</H1>\
<H3>Current firmware version: ";
updFirmware += VERSION;
updFirmware += "</H3><br>";
updFirmware += "Notes:<br>";
updFirmware += "<ul>";
updFirmware += "<li>The firmware update will begin as soon as the Update Firmware button is clicked.</li><br>";
updFirmware += "<li>Your current settings will be retained.</li><br>";
updFirmware += "<li><b>Please be patient!</b> The update will take a few minutes. Do not refresh the page or navigate away.</li><br>";
updFirmware += "<li>If the upload is successful, a brief message will appear and the controller will reboot.</li><br>";
updFirmware += "<li>After rebooting, you'll automatically be taken back to the main settings page and the update will be complete.</li><br>";
updFirmware += "</ul><br>";
updFirmware += "</body></html>";
updFirmware += "<form method='POST' action='/update2' enctype='multipart/form-data'>";
updFirmware += "<input type='file' accept='.bin,.bin.gz' name='Select file' style='width: 300px'><br><br>";
updFirmware += "<input type='submit' value='Update Firmware'>";
updFirmware += "</form><br>";
updFirmware += "<br><a href=\"http://";
updFirmware += localIP;
updFirmware += "\">Return to settings</a><br>";
updFirmware += "</body></html>";
server.send(200, "text/html", updFirmware);
}
void updateSettings(bool saveBoot) {
// This updates the current local settings for current session only.
// Will be overwritten with reboot/reset/OTAUpdate
if (saveBoot) {
updateBootSettings(saveBoot);
} else {
//Update FastLED with new brightness values if changed
if (isAwake) {
FastLED.setBrightness(activeBrightness);
} else {
FastLED.setBrightness(sleepBrightness);
}
}
}
void updateBootSettings(bool restart_ESP) {
// Writes new settings to SPIFFS (new boot defaults)
char t_led_count[4];
char t_led_brightness_active[4];
char t_led_brightness_sleep[4];
char t_led_park_time[4];
char t_led_exit_time[4];
char t_uom_distance[4];
char t_wake_mils[6];
char t_start_mils[6];
char t_park_mils[6];
char t_backup_mils[6];
char t_color_standby[4];
char t_color_wake[4];
char t_color_active[4];
char t_color_parked[4];
char t_color_backup[4];
char t_led_effect[16];
int eff_len = 16;
char t_mqtt_addr_1[4];
char t_mqtt_addr_2[4];
char t_mqtt_addr_3[4];
char t_mqtt_addr_4[4];
char t_mqtt_port[6];
char t_mqtt_user[65];
char t_mqtt_pw[65];
char t_mqtt_tele_period[4];
int user_len = 65;
int user_pw = 65;
//v0.41 new values
char t_device_name[18];
char t_mqtt_topic_sub[18];
char t_mqtt_topic_pub[18];
int dev_name_len = 18;
int topic_len = 18;
//#if defined(SERIAL_DEBUG) && (SERIAL_DEBUG == 1)
Serial.println("Attempting to update boot settings");
//#endif
//Convert values into char arrays
sprintf(t_led_count, "%u", numLEDs);
sprintf(t_led_brightness_active, "%u", activeBrightness);
sprintf(t_led_brightness_sleep, "%u", sleepBrightness);
sprintf(t_led_park_time, "%u", maxOperationTimePark);
sprintf(t_led_exit_time, "%u", maxOperationTimeExit);
sprintf(t_uom_distance, "%u", uomDistance);
sprintf(t_wake_mils, "%u", wakeDistance);
sprintf(t_start_mils, "%u", startDistance);
sprintf(t_park_mils, "%u", parkDistance);
sprintf(t_backup_mils, "%u", backupDistance);
ledEffect_m1.toCharArray(t_led_effect, eff_len);
sprintf(t_mqtt_addr_1, "%u", mqttAddr_1);
sprintf(t_mqtt_addr_2, "%u", mqttAddr_2);
sprintf(t_mqtt_addr_3, "%u", mqttAddr_3);
sprintf(t_mqtt_addr_4, "%u", mqttAddr_4);
sprintf(t_mqtt_port, "%u", mqttPort);
sprintf(t_mqtt_tele_period, "%u", mqttTelePeriod);
mqttUser.toCharArray(t_mqtt_user, user_len);
mqttPW.toCharArray(t_mqtt_pw, user_pw);
deviceName.toCharArray(t_device_name, dev_name_len);
mqttTopicSub.toCharArray(t_mqtt_topic_sub, topic_len);
mqttTopicPub.toCharArray(t_mqtt_topic_pub, topic_len);
#ifdef ARDUINOJSON_VERSION_MAJOR >= 6
DynamicJsonDocument json(1024);
#else
DynamicJsonBuffer jsonBuffer;
JsonObject& json = jsonBuffer.createObject();
#endif
json["mqtt_addr_1"] = t_mqtt_addr_1;
json["mqtt_addr_2"] = t_mqtt_addr_2;
json["mqtt_addr_3"] = t_mqtt_addr_3;
json["mqtt_addr_4"] = t_mqtt_addr_4;
json["mqtt_port"] = t_mqtt_port;
json["mqtt_tele_period"] = t_mqtt_tele_period;
json["mqtt_user"] = t_mqtt_user;
json["mqtt_pw"] = t_mqtt_pw;
//v0.41
json["device_name"] = t_device_name;
json["mqtt_topic_sub"] = t_mqtt_topic_sub;
json["mqtt_topic_pub"] = t_mqtt_topic_pub;
if (SPIFFS.begin()) {
File configFile = SPIFFS.open("/config.json", "w");
if (!configFile) {
//#if defined(SERIAL_DEBUG) && (SERIAL_DEBUG == 1)
Serial.println("failed to open config file for writing");
//#endif
}
serializeJson(json, Serial);
serializeJson(json, configFile);
configFile.close();
//end save
//#if defined(SERIAL_DEBUG) && (SERIAL_DEBUG == 1)
Serial.println("Boot settings saved. Rebooting controller.");
//#endif
}
SPIFFS.end();
if (restart_ESP) { //Needed so initial onboarding doesn't cause second reboot
ESP.restart();
}
}
void handleReset() {
String resetMsg = "<HTML>\
</head>\
<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\
<title>Controller Reset</title>\
<style>\
body { background-color: #cccccc; font-family: Arial, Helvetica, Sans-Serif; Color: #000088; }\
</style>\
</head>\
<body>\
<H1>Controller Resetting...</H1><br>\
<H3>After this process is complete, you must setup your controller again:</H3>\
<ul>\
<li>Connect a device to the controller's local access point: ESP_ParkingAsst</li>\
<li>Open a browser and go to: 192.168.4.1</li>\
<li>Enter your WiFi information and set other default settings values</li>\
<li>Click Save. The controller will reboot and join your WiFi</li>\
</ul><br>\
Once the above process is complete, you can return to the main settings page by rejoining your WiFi and entering the IP address assigned by your router in a browser.<br>\
You will need to reenter all of your settings for the system as all values will be reset to original defaults<br><br>\
<b>This page will NOT automatically reload or refresh</b>\
</body></html>";
server.send(200, "text/html", resetMsg);
delay(1000);
SPIFFS.begin();
SPIFFS.format();
SPIFFS.end();
wifiManager.resetSettings();
delay(1000);
ESP.restart();
}
void handleRestart() {
String restartMsg = "<HTML>\
</head>\
<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\
<title>Controller Restart</title>\
<style>\
body { background-color: #cccccc; font-family: Arial, Helvetica, Sans-Serif; Color: #000088; }\
</style>\
</head>\
<body>\
<H1>Controller restarting...</H1><br>\
<H3>Please wait</H3><br>\
After the controller completes the boot process (lights will flash blue, followed by red/green for approx. 2 seconds), you may click the following link to return to the main page:<br><br>\
<a href=\"http://";
restartMsg += localIP;
restartMsg += "\">Return to settings</a><br>";
restartMsg += "</body></html>";
server.send(200, "text/html", restartMsg);
delay(1000);
ESP.restart();
}
// =====================================
// Home Assistant MQTT Discovery - v0.45
// =====================================
void handleDiscovery() {
//Main page for enabling/disabling Home Assistant MQTT Discovery
String discMsg = "<HTML>\
</head>\
<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\
<title> TDS Monitor - MQTT Discovery</title>\
<style>\
body { background-color: #cccccc; font-family: Arial, Helvetica, Sans-Serif; Color: #000088; }\
</style>\
</head>\
<body>\
<H1>Home Assistant MQTT Discovery - BETA</H1>\
(BETA Note: Tested successfully with Home Assistant Core 2024.1 but will be considered a beta feature until broader testing by others has been completed.)<br><br>\
This feature can be used to add or remove the parking assistant device and MQTT entities to Home Assistant without manual YAML editing.<br><br>";
discMsg += "<u><b>Prerequisites and Notes</b></u>";
discMsg += "<ul><li>It is <b><i>strongly recommended</i></b> that you read the  ";
discMsg += "<a href=\"https://github.com/Resinchem/ESP-Parking-Assistant/wiki/08-MQTT-and-Home-Assistant\" target=\"_blank\" rel=\"noopener noreferrer\">MQTT Documentation</a> before using this feature.</li>";
discMsg += "<li>You must have successfully completed the MQTT setup, rebooted and estabished a connection to your broker before these options will work.</li>";
discMsg += "<li>The Home Assistant MQTT integration must be installed, discovery must not have been disabled nor the default discovery topic changed.</li>";
discMsg += "<li>The action to enable/disable will occur immediately in Home Assistant without any interaction or prompts.</li>";
discMsg += "<li>If you have already manually created the Home Assistant MQTT entities, enabling discovery will create duplicate entities with different names.</li></ul>";
discMsg += "<H3>Enable Discovery</H3>";
discMsg += "This will immediately create a device called <b>VAR_DEVICE_NAME</b> in your Home Assistant MQTT Integration.<br>";
discMsg += "It will also create the following entities for this device:\
<ul>\
<li>Supply TDS</li>\
<li>Pre-DI TDS</li>\
<li>Output TDS</li>\
<li>IP Address</li>\
<li>MAC Address</li></ul><br>";
discMsg += "<button id=\"btnenable\" onclick=\"location.href = './discoveryEnabled';\">Enable Discovery</button>";
discMsg += "<br><hr>";
discMsg += "<H3>Disable Discovery</H3>";
discMsg += "This will <b>immediately</b> delete the device and all entities created MQTT Discover for this device.<br>\
If you have any automations, scripts, dashboard entries or other processes that use these entities, you will need to delete or correct those items in Home Assistant.<br><br>";
discMsg += "<button id=\"btndisable\" onclick=\"location.href = './discoveryDisabled';\">Disable Discovery</button><br><br>";
discMsg += "<br><a href=\"http://";
discMsg += localIP;
discMsg += "\">Return to settings</a><br>";
discMsg += "</body></html>";
discMsg.replace("VAR_DEVICE_NAME", deviceName);
server.send(200, "text/html", discMsg);
}
void enableDiscovery() {
String discMsg = "<HTML>\
</head>\
<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\
<title> TDS Monitor MQTT Discovery</title>\
<style>\
body { background-color: #cccccc; font-family: Arial, Helvetica, Sans-Serif; Color: #000088; }\
</style>\
</head>\
<body>\
<H1>Home Assistant MQTT Discovery</H1><br>";
if (mqttEnabled) {
byte retVal = haDiscovery(true);
if (retVal == 0) {
discMsg += "<b><p style=\"color: #5a8f3d;\">MQTT Discovery topics successfully sent to Home Assistant.</p></b><br>";
discMsg += "If the Home Assistant MQTT integration has been configured correctly, you should now find a new device, <b>VAR_DEVICE_NAME</b>, under the MQTT integration.";
discMsg += "<p style=\"color: #1c5410;\">\
<ul>\
<li>You can change the name of the device or any entities, if desired, via Home Assistant</li>\
<li>To remove (permanently delete) the created device and entities, disable MQTT Discovery</li>\
<li>If you disable MQTT Discover and then reenable it, the device and entities will be recreated with their original names.</li>\
</ul></p><br>";
discMsg += "If you do not see the new device under your Home Assistant MQTT integration, please use a utility (e.g. MQTT Explorer or similar) to see if the controller is successfully connecting to your broker.<br>\
You should see an MQTT connected message, along with the IP and MAC addresses of your controller under the topic specified on the main settings page.<br><br>";
discMsg += "For additional troubleshooting tips, please see the <a href=\"https://github.com/Resinchem/ESP-Parking-Assistant/wiki/08-MQTT-and-Home-Assistant\" target=\"_blank\" rel=\"noopener noreferrer\">MQTT Documentation</a><br><br>\
It is recommended that you disable MQTT Discovery in the Parking Assistant app until you resolve any MQTT/Home Assistant issues.<br><br>";
} else if (retVal == 1) {
//Unable to connect or reconnect to broker
discMsg += "<b><p style=\"color: red;\">The Parking Assistant was unable to connect or reconnect to your MQTT broker!</p></b><br>";
discMsg += "Please be sure your broker is running and accessible on the same network and that you have completed the following:";
discMsg += "<ul>\
<li>Entered the proper MQTT broker settings and the correct User Name and Password</li>\
<li>Checked the box to save settings as new boot defaults</li>\
<li>Rebooted the controller</li>\
</ul><br>";
discMsg += "Please use a utility (e.g. MQTT Explorer or similar) to test if the controller is successfully connecting to your broker.<br>\
You should see an MQTT connected message, along with the IP and MAC addresses of your controller under the topic specified on the main settings page.<br><br>\
Until you successfully see these topics in your broker, you will not be able to utilize MQTT Discovery.<br><br>";
discMsg += "<p style=\"color: red;\">No Home Assistant devices or entities were created.</p><br><br>";
} else {
//Unknown error or issue
discMsg += "<b><p style=\"color: red;\">An unknown error or issue occurred!</p></b><br>";
discMsg += "Please be sure your broker is running and accessible on the same network and that you have completed the following:";
discMsg += "<ul>\
<li>Entered the proper MQTT broker settings and the correct User Name and Password</li>\
<li>Checked the box to save settings as new boot defaults</li>\
<li>Rebooted the controller</li>\
</ul><br>";
discMsg += "Please use a utility (e.g. MQTT Explorer or similar) to test if the controller is successfully connecting to your broker.<br>\
You should see an MQTT connected message, along with the IP and MAC addresses of your controller under the topic specified on the main settings page.<br><br>\
Until you successfully see these topics in your broker, you will not be able to utilize MQTT Discovery and may need to manually create the Home Assistant entities.<br><br>";
discMsg += "For additional troubleshooting tips, please see the <a href=\"https://github.com/Resinchem/ESP-Parking-Assistant/wiki/08-MQTT-and-Home-Assistant\" target=\"_blank\" rel=\"noopener noreferrer\">MQTT Documentation</a><br><br>";
discMsg += "<p style=\"color: red;\">No Home Assistant devices or entities were created.</p><br><br>";
}
} else {
discMsg += "<b><p style=\"color: red;\">MQTT is not enabled or was unable to connect to your broker!</p></b><br>";
discMsg += "Before attempting to enable MQTT Discovery, please be sure you have completed the following:";
discMsg += "<ul>\
<li>Entered the proper MQTT broker settings and the correct User Name and Password</li>\
<li>Checked the box to save settings as new boot defaults</li>\
<li>Rebooted the controller</li>\
</ul><br>";
discMsg += "Please use a utility (e.g. MQTT Explorer or similar) to see if the controller is successfully connecting to your broker.<br>\
You should see an MQTT connected message, along with the IP and MAC addresses of your controller under the topic specified on the main settings page.<br><br>\
Until you successfully see these topics in your broker, you will not be able to enable MQTT Discovery.<br>";
}
discMsg += "<br><a href=\"http://";
discMsg += localIP;
discMsg += "\">Return to settings</a><br>";
discMsg += "</body></html>";
discMsg.replace("VAR_DEVICE_NAME", deviceName);
server.send(200, "text/html", discMsg);
//send baseline TDS reading
sprintf(mqtt_tds_SUP, "%d",(int)tdsValue_SUP);
client.publish(mqtt_supTDS_stat, mqtt_tds_SUP);
Serial.println("Supply TDS Baseline Telemetry Sent Successfully!");
Serial.println("");
sprintf(mqtt_tds_PRE_DI, "%d",(int)tdsValue_PRE_DI);
client.publish(mqtt_prediTDS_stat, mqtt_tds_PRE_DI);
Serial.println("Pre-DI TDS Baseline Telemetry Sent Successfully!");
Serial.println("");
sprintf(mqtt_tds_OUT, "%d",(int)tdsValue_OUT);
client.publish(mqtt_outTDS_stat, mqtt_tds_OUT);
Serial.println("Output TDS Baseline Telemetry Sent Successfully!");
Serial.println("");
}
void disableDiscovery() {
String discMsg = "<HTML>\
</head>\
<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\
<title> TDS Monitor MQTT Discovery</title>\
<style>\
body { background-color: #cccccc; font-family: Arial, Helvetica, Sans-Serif; Color: #000088; }\
</style>\
</head>\
<body>\
<H1>Home Assistant MQTT Discovery</H1><br>";
if (mqttEnabled) {
byte retVal = haDiscovery(false);
if (retVal == 0) {
discMsg += "<b><p style=\"color: #5a8f3d;\">MQTT Removal topics successfully sent to Home Assistant.</p></b><br>";
discMsg += "The device <b>VAR_DEVICE_NAME</b> should now be deleted from Home Assistant, along with the following entities:\
<ul>\
<li>Supply TDS</li>\
<li>Pre-DI TDS</li>\
<li>Output TDS</li>\
<li>IP Address</li>\
<li>MAC Address</li></ul><br>";
} else if (retVal == 1) {
//Unable to connect or reconnect to broker
discMsg += "<b><p style=\"color: red;\">The Parking Assistant was unable to connect or reconnect to your MQTT broker!</p></b><br>";
discMsg += "Please be sure your broker is running and accessible on the same network and that you have completed the following:";
discMsg += "<ul>\
<li>Entered the proper MQTT broker settings and the correct User Name and Password</li>\
<li>Checked the box to save settings as new boot defaults</li>\
<li>Rebooted the controller</li>\
</ul><br>";
discMsg += "Please use a utility (e.g. MQTT Explorer or similar) to test if the controller is successfully connecting to your broker.<br>\
You should see an MQTT connected message, along with the IP and MAC addresses of your controller under the topic specified on the main settings page.<br><br>\
Until you successfully see these topics in your broker, you will not be able to utilize MQTT Discovery.<br><br>";
discMsg += "<p style=\"color: red;\">No Home Assistant devices or entities were removed.</p><br><br>";
} else {
//Unknown error or issue
discMsg += "<b><p style=\"color: red;\">An unknown error or issue occurred!</p></b><br>";
discMsg += "Please be sure your broker is running and accessible on the same network and that you have completed the following:";
discMsg += "<ul>\
<li>Entered the proper MQTT broker settings and the correct User Name and Password</li>\
<li>Checked the box to save settings as new boot defaults</li>\
<li>Rebooted the controller</li>\
</ul><br>";
discMsg += "Please use a utility (e.g. MQTT Explorer or similar) to test if the controller is successfully connecting to your broker.<br>\
You should see an MQTT connected message, along with the IP and MAC addresses of your controller under the topic specified on the main settings page.<br><br>\
Until you successfully see these topics in your broker, you will not be able to utilize MQTT Discovery and may need to manually create the Home Assistant entities.<br><br>";
discMsg += "For additional troubleshooting tips, please see the <a href=\"https://github.com/Resinchem/ESP-Parking-Assistant/wiki/08-MQTT-and-Home-Assistant\" target=\"_blank\" rel=\"noopener noreferrer\">MQTT Documentation</a><br><br>";
discMsg += "<p style=\"color: red;\">No Home Assistant devices or entities were removed.</p><br><br>";
}
} else {
discMsg += "<b><p style=\"color: red;\">MQTT is not enabled or was unable to connect to your broker!</p></b><br>";
discMsg += "Before attempting to use MQTT Discovery, please be sure you have completed the following:";
discMsg += "<ul>\
<li>Entered the proper MQTT broker settings and the correct User Name and Password</li>\
<li>Checked the box to save settings as new boot defaults</li>\
<li>Rebooted the controller</li>\
</ul><br>";
discMsg += "Please use a utility (e.g. MQTT Explorer or similar) to see if the controller is successfully connecting to your broker.<br>\
You should see an MQTT connected message, along with the IP and MAC addresses of your controller under the topic specified on the main settings page.<br><br>\
Until you successfully see these topics in your broker, you will not be able to enable MQTT Discovery.<br>";
}
discMsg += "<br><a href=\"http://";
discMsg += localIP;
discMsg += "\">Return to settings</a><br>";
discMsg += "</body></html>";
discMsg.replace("VAR_DEVICE_NAME", deviceName);
server.send(200, "text/html", discMsg);
}
// Settings submit handler - Settings results
void calibration() {
String saveCalibration;
//String.format("%d", tdsValue_SUP)
//Need to get the TDS Value to display as a label on line 884
web_currSUP = (int)tdsValue_SUP
web_currPREDI = (int)tdsValue_PRE_DI;
web_currOUT = (int)tdsValue_OUT;
web_calSUP = server.arg("web_calsup").toInt();
web_calPREDI = server.arg("web_calpredi").toInt();
web_calOUT = server.arg("web_calout").toInt();
saveCalibration = server.arg("calsave");
String calSensors = "<html>\
</head>\
<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\
<title>TDS Monitor - Sensor Calibration</title>\
<style>\
body { background-color: #cccccc; font-family: Arial, Helvetica, Sans-Serif; Color: #000088; }\
</style>\
</head>\
<body>\
<H1>Calibrate TDS Sensor</H1><br>\
<H3>Current values are:</H3>";
//these are labels
calSensors += "<b>TDS Sensors</b><br>";
/*
calSensors += "Current Supply TDS: " + tdsValue_SUP + "<br>";
calSensors += "Current Pre-DI TDS: " + tdsValue_PRE_DI + "<br>";
calSensors += "Current Output TDS: " + tdsValue_OUT + "<br>" ;
*/
calSensors += "Current Supply TDS: ";
calSensors += web_currSUP;
calSensors += "ppm";
calSensors += "<br>";
calSensors += "Current Pre-DI TDS: ";
calSensors += web_currPREDI;
calSensors += "ppm";
calSensors += "<br>";
calSensors += "Current Output TDS: ";
calSensors += web_currOUT;
calSensors += "ppm";
calSensors += "<br>";
//these are input boxes
calSensors += "<table>\
<tr>\
<td><label for=\"web_calsup\">Calibrated Supply TDS Value:</label></td>\
<td><input type=\"number\" min=\"1\" max=\"1000\" step=\"1\" name=\"web_calsup\" style=\"width: 50px\;\" value=\"";
calSensors += String(web_currSUP);
calSensors += "\"> ppm<br>\
<tr>\
<td><label for=\"web_calpredi\">Calibrated Pre-DI TDS Value:</label></td>\
<td><input type=\"number\" min=\"1\" max=\"1000\" step=\"1\" name=\"web_calpredi\" style=\"width: 50px\;\" value=\"";
calSensors += String(web_currPREDI);
calSensors += "\"> ppm<br>\
<tr>\
<td><label for=\"web_calout\">Calibrated Output TDS Value:</label></td>\
<td><input type=\"number\" min=\"1\" max=\"1000\" step=\"1\" name=\"web_calout\" style=\"width: 50px\;\" value=\"";
calSensors += String(web_calOUT);
calSensors += "\"> ppm<br></td></tr>\"";
calSensors += "<br>";
calSensors += "<br><a href=\"http://";
calSensors += localIP;
calSensors += "/discovery\">Configure Home Assistant MQTT Discovery</a><br>";
calSensors += "<br><a href=\"http://";
calSensors += localIP;
calSensors += "\">Return to settings</a><br>";
calSensors += "</td></tr>\
</table><br>\
<input type=\"checkbox\" name=\"calsave\" value=\"save\">Store Calibration and Replace Defaults (Controller Will Reboot)<br><br>\
<input type=\"submit\" value=\"Submit Calibration\">\
</form>\
<br>\
<h2>Controller Commands</h2>\
Caution: Restart and Reset are executed immediately when the button is clicked.<br>\
<table border=\"1\" cellpadding=\"10\">\
<tr>\
<td><button id=\"btnrestart\" onclick=\"location.href = './restart';\">Restart</button></td><td>This will reboot controller and reload default boot values.</td>\
</tr><tr>\
<td><button id=\"btnreset\" style=\"background-color:#FAADB7\" onclick=\"location.href = './reset';\">RESET ALL</button></td><td><b>WARNING</b>: This will clear all settings, including WiFi! You must complete initial setup again.</td>\
</tr><tr>\
<td><button id=\"btnupdate\" onclick=\"location.href = './update';\">Firmware Upgrade</button></td><td>Upload and apply new firmware from local file.</td>\
</tr></table><br>\
Current version: VAR_CURRRENT_VER\
</body>\
</html>";
calSensors.replace("VAR_DEVICE_NAME", deviceName);
calSensors.replace("VAR_CURRRENT_VER", VERSION);
server.send(200, "text/html", calSensors);
delay(1000);
}
// Not found or invalid page handler
void handleNotFound() {
String message = "File Not Found or invalid command.\n\n";
message += "URI: ";
message += server.uri();
message += "\nMethod: ";
message += (server.method() == HTTP_GET) ? "GET" : "POST";
message += "\nArguments: ";
message += server.args();
message += "\n";
server.send(404, "text/plain", message);
}
// ===================================
// SETUP MQTT AND CALLBACKS
// ===================================
bool setup_mqtt() {
byte mcount = 0;
IPAddress mymqttServer = IPAddress(mqttAddr_1, mqttAddr_2, mqttAddr_3, mqttAddr_4);
client.setServer(mymqttServer, mqttPort);
client.setBufferSize(512);
client.setCallback(callback);
//#if defined(SERIAL_DEBUG) && (SERIAL_DEBUG == 1)
Serial.print("Connecting to MQTT broker.");
//#endif
while (!client.connected( )) {
//#if defined(SERIAL_DEBUG) && (SERIAL_DEBUG == 1)
Serial.print(".");
//#endif
client.connect(mqttClient.c_str(), mqttUser.c_str(), mqttPW.c_str());
if (mcount >= 60) {
//#if defined(SERIAL_DEBUG) && (SERIAL_DEBUG == 1)
Serial.println();
Serial.println("Could not connect to MQTT broker. MQTT disabled.");
//#endif
// Could not connect to MQTT broker
return false;
}
delay(500);
mcount++;
}
//#if defined(SERIAL_DEBUG) && (SERIAL_DEBUG == 1)
Serial.println();
Serial.println("Successfully connected to MQTT broker.");
//#endif
client.subscribe(("cmnd/" + mqttTopicSub + "/#").c_str());
client.publish(("homeassistant/stat/" + mqttTopicPub + "/mqtt").c_str(), "connected", true);
//Publish current IP and MAC addresses
client.publish(("homeassistant/stat/" + mqttTopicPub + "/ipaddress").c_str(), localIP.c_str(), true);
client.publish(("homeassistant/stat/" + mqttTopicPub + "/macaddress").c_str(), strMacAddr.c_str(), true);
mqttConnected = true;
//send baseline TDS reading
sprintf(mqtt_tds_SUP, "TDS-SUP: %d",(int)tdsValue_SUP);
client.publish(mqtt_pub_topic_supTDS, mqtt_tds_SUP);
Serial.println("Supply TDS Telemetry Sent Successfully!");
Serial.println("");
sprintf(mqtt_tds_PRE_DI, "TDS-PRE_DI: %d",(int)tdsValue_PRE_DI);
client.publish(mqtt_pub_topic_prediTDS, mqtt_tds_PRE_DI);
Serial.println("Pre-DI TDS Telemetry Sent Successfully!");
Serial.println("");
sprintf(mqtt_tds_OUT, "TDS-OUT: %d",(int)tdsValue_OUT);
client.publish(mqtt_pub_topic_outTDS, mqtt_tds_OUT);
Serial.println("Output TDS Telemetry Sent Successfully!");
Serial.println("");
client.publish("Aquarium","TDS Monitor Connected!");
client.subscribe("Aquarium");
Serial.println("MQTT Connected - Connection Mesage Sent!");
//send baseline TDS reading
sprintf(mqtt_tds_SUP, "%d",(int)tdsValue_SUP);
client.publish(mqtt_supTDS_stat, mqtt_tds_SUP);
Serial.println("Supply TDS Telemetry Sent Successfully!");
Serial.println("");
sprintf(mqtt_tds_PRE_DI, "%d",(int)tdsValue_PRE_DI);
client.publish(mqtt_prediTDS_stat, mqtt_tds_PRE_DI);
Serial.println("Pre-DI TDS Telemetry Sent Successfully!");
Serial.println("");
sprintf(mqtt_tds_OUT, "%d",(int)tdsValue_OUT);
client.publish(mqtt_outTDS_stat, mqtt_tds_OUT);
Serial.println("Output TDS Telemetry Sent Successfully!");
Serial.println("");
return true;
}
void callback(char* topic, byte* payload, unsigned int length) {
payload[length] = '\0';
String message = (char*)payload;
/*
* Add any commands submitted here
* Example:
* if (strcmp(topic, "cmnd/matrix/mode")==0) {
* MyVal = message;
* Do something
* return;
* };
*/
}
void readConfigFile() {
if (SPIFFS.begin()) {
//#if defined(SERIAL_DEBUG) && (SERIAL_DEBUG == 1)
Serial.println("mounted file system");
//#endif
if (SPIFFS.exists("/config.json")) {
//file exists, reading and loading
//#if defined(SERIAL_DEBUG) && (SERIAL_DEBUG == 1)
Serial.println("reading config file");
//#endif
File configFile = SPIFFS.open("/config.json", "r");
if (configFile) {
//#if defined(SERIAL_DEBUG) && (SERIAL_DEBUG == 1)
Serial.println("opened config file");
//#endif
size_t size = configFile.size();
// Allocate a buffer to store contents of the file.
std::unique_ptr<char[]> buf(new char[size]);
configFile.readBytes(buf.get(), size);
DynamicJsonDocument json(1024);
auto deserializeError = deserializeJson(json, buf.get());
serializeJson(json, Serial);
if ( ! deserializeError ) {
//#if defined(SERIAL_DEBUG) && (SERIAL_DEBUG == 1)
Serial.println("\nparsed json");
//#endif
// Read values here from SPIFFS (v0.42 - add defaults for all values in case they don't exist to avoid potential boot loop)
strcpy(mqtt_addr_1, json["mqtt_addr_1"]|"0");
strcpy(mqtt_addr_2, json["mqtt_addr_2"]|"0");
strcpy(mqtt_addr_3, json["mqtt_addr_3"]|"0");
strcpy(mqtt_addr_4, json["mqtt_addr_4"]|"0");
strcpy(mqtt_port, json["mqtt_port"]|"0");
strcpy(mqtt_tele_period, json["mqtt_tele_period"]|"60");
strcpy(mqtt_user, json["mqtt_user"]|"mqttuser");
strcpy(mqtt_pw, json["mqtt_pw"]|"mqttpwd");
strcpy(device_name, json["device_name"]|"parkasst"); // Default needed if upgrading to v0.41, because value won't exist in config
strcpy(mqtt_topic_sub, json["mqtt_topic_sub"]|"parkasst"); // Default needed if upgrading to v0.41, because value won't exist in config
strcpy(mqtt_topic_pub, json["mqtt_topic_pub"]|"parkasst"); // Default needed if upgrading to v0.41, because value won't exist in config
//Need to set wifihostname here
deviceName = String(device_name);
} else {
//#if defined(SERIAL_DEBUG) && (SERIAL_DEBUG == 1)
Serial.println("failed to load json config");
//#endif
}
configFile.close();
}
}
SPIFFS.end();
} else {
//#if defined(SERIAL_DEBUG) && (SERIAL_DEBUG == 1)
Serial.println("failed to mount FS");
//#endif
}
}
void setup_Wifi() {
delay(10);
Serial.println("Booting");
Serial.println("Connecting to WiFi");
WiFi.mode(WIFI_STA);
WiFiManager wm;
bool res;
res = wm.autoConnect("TDS Monitor", "password");
if(!res) {
Serial.println("Failed to connect");
}
else {
Serial.println("WiFi Connection Success!");
client.publish(mqtt_IP_stat, localIP.c_str());
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
randomSeed(micros());
localIP = WiFi.localIP().toString();
Serial.println("");
Serial.println("WiFi connected");
Serial.print("IP address: ");
Serial.print(WiFi.localIP());
Serial.println("");
}
}
void resetWiFiSettings(){
//Wifi Reset Button if held for 10 seconds, reset wifi settings
pinMode(27, INPUT_PULLUP);
pinMode(2, OUTPUT);
int wifiReset = digitalRead(27);
if (wifiReset == HIGH) {
digitalWrite(2, LOW);
} else {
digitalWrite(2, HIGH);
delay(10000);
digitalWrite(2, LOW);
delay(150);
digitalWrite(2, HIGH);
delay(150);
digitalWrite(2, LOW);
delay(150);
digitalWrite(2, HIGH);
delay(150);
digitalWrite(2, LOW);
delay(150);
digitalWrite(2, HIGH);
delay(150);
digitalWrite(2, LOW);
delay(150);
digitalWrite(2, HIGH);
delay(150);
digitalWrite(2, LOW);
delay(150);
Serial.println("WiFi Reset...TDS Monitor Restarting");
delay(150);
wm.resetSettings();
delay(150);
//Restarts device after wiping wifi settings
ESP.restart();
}
}
void wifiReconnect() {
// Loop until we're reconnected
while (WiFi.status() != WL_CONNECTED) {
Serial.print("Attempting to Re-Establish WiFi Connection...");
if (WiFi.status() == WL_CONNECTED) {
Serial.println("WiFi Connection Re-Established!");
client.publish(mqtt_IP_stat, localIP.c_str());
} else {
Serial.print("failed, rc=");
Serial.print(WiFi.status());
Serial.println(" try again in 5 seconds");
// Wait 5 seconds before retrying
delay(5000);
setup_Wifi();
}
}
}
// ===============================
// LED and Display Functions
// ===============================
void blinkLEDs(CRGB color) {
if (blinkOn) {
fill_solid(LEDs, numLEDs, color);
} else {
fill_solid(LEDs, numLEDs, CRGB::Black);
}
blinkOn = !blinkOn;
}
void updateOutIn(int curDistance) {
byte numberToLight = 1;
fill_solid(LEDs, numLEDs, CRGB::Black);
//Get number of LEDs to light up on each end, based on interval
numberToLight = (startDistance - curDistance) / intervalDistance;
if (numberToLight ==0 ) numberToLight = 1; //Assure at least 1 light if integer truncation results in 0
for (int i=0; i < numberToLight; i++) {
LEDs[i] = ledColorActive;
LEDs[(numLEDs-1) - i] = ledColorActive;
}
}
void updateInOut(int curDistance) {
byte numberToLight = 1;
byte startLEDLeft = 0;
byte startLEDRight = 0;
fill_solid(LEDs, numLEDs, CRGB::Black);
//Get number of LEDs to light up on each end, based on interval
numberToLight = ((startDistance - curDistance) / intervalDistance);
if (numberToLight ==0 ) numberToLight = 1; //Assure at least 1 light if integer truncation results in 0
//Find center LED(s) - single of odd number, two if even number of LEDS
startLEDLeft = (numLEDs / 2);
startLEDRight = startLEDLeft;
if ((startLEDLeft % 2) == 0) {
startLEDLeft --;
}
for (int i=0; i < numberToLight; i++) {
LEDs[(startLEDLeft - i)] = ledColorActive;
LEDs[(startLEDRight + i)] = ledColorActive;
}
}
void updateFullStrip(int curDistance) {
byte numberToLight = 1;
fill_solid(LEDs, numLEDs, CRGB::Black);
//Get number of LEDs to light up from start of LED strip, based on interval
numberToLight = (startDistance - curDistance) / intervalDistance;
if (numberToLight ==0 ) numberToLight = 1; //Assure at least 1 light if integer truncation results in 0
for (int i=0; i < numberToLight; i++) {
LEDs[i] = ledColorActive;
}
}
void updateFullStripInv(int curDistance) {
byte numberToLight = 1;
fill_solid(LEDs, numLEDs, CRGB::Black);
//Get number of LEDs to light up from end of LED strip, based on interval
numberToLight = (startDistance - curDistance) / intervalDistance;
if (numberToLight ==0 ) numberToLight = 1; //Assure at least 1 light if integer truncation results in 0
for (int i=0; i < numberToLight; i++) {
LEDs[((numLEDs - i)- 1)] = ledColorActive;
}
}
void updateSolid(int curDistance) {
fill_solid(LEDs, numLEDs, CRGB::Black);
if ((curDistance > startDistance) && (curDistance <= wakeDistance)) {
fill_solid(LEDs, numLEDs, ledColorWake);
} else if ((curDistance > parkDistance) && (curDistance <= startDistance)) {
fill_solid(LEDs, numLEDs, ledColorActive);
} else if ((curDistance > backupDistance) && (curDistance <= parkDistance)) {
fill_solid(LEDs, numLEDs, ledColorParked);
}
}
void updateSleepMode() {
fill_solid(LEDs, numLEDs, CRGB::Black);
FastLED.setBrightness(sleepBrightness);
if (showStandbyLEDs) {
LEDs[0] = ledColorStandby;
LEDs[numLEDs - 1] = ledColorStandby;
}
}
void updateOTA() {
fill_solid(LEDs, numLEDs, CRGB::Black);
//Alternate LED colors using red and green
FastLED.setBrightness(activeBrightness);
for (int i=0; i < (numLEDs-1); i = i + 2) {
LEDs[i] = CRGB::Red;
LEDs[i+1] = CRGB::Green;
}
FastLED.show();
}
// ==============================
// MQTT Functions and Procedures
// ==============================
void createDiscoveryUniqueID() {
//Generate UniqueID from uidPrefix + last 6 chars of device MAC address
//This should insure that even multiple devices installed in same HA instance are unique
strcpy(devUniqueID, uidPrefix);
int preSizeBytes = sizeof(uidPrefix);
int preSizeElements = (sizeof(uidPrefix) / sizeof(uidPrefix[0]));
//Now add last 6 chars from MAC address (these are 'reversed' in the array)
int j = 0;
//for (int i = 2; i >= 0; i--) {
for (int i = 3; i < 6; i++) {
sprintf(&devUniqueID[(preSizeBytes - 1) + (j)], "%02X", macAddr[i]); //preSizeBytes indicates num of bytes in prefix - null terminator, plus 2 (j) bytes for each hex segment of MAC
j = j + 2;
}
//devUniqueID would now contain something like "TDSMonitor02BE4F" which can be used for topics/payloads
// End result is a unique ID for this device (e.g. rctdevE350CA)
Serial.print("Unique ID: ");
Serial.println(devUniqueID);
}
byte haDiscovery (bool enable) {
char topic[128];
//if (auto_discovery) {
char buffer1[512];
char buffer2[512];
char buffer3[512];
char buffer4[512];
char buffer5[512];
char uid[128];
if (mqttEnabled) {
createDiscoveryUniqueID();
if (enable) {
//Add device and entities
if (!client.connected()) {
if (!mqttReconnect_soft()) {
return 1;
}
}
//Should have valid MQTT Connection at this point
DynamicJsonDocument doc(2048);
Serial.println("Discovering new devices...");
Serial.println("Adding TDS Sensor...Supply TDS");
//Create supTDS Sensor with full device details
//topic
strcpy(topic, "homeassistant/sensor/");
strcat(topic, "TDSMonitor");
strcat(topic, "supTDS/config");
//Create unique_id based on devUniqueID
strcpy(uid, "TDSMonitor");
strcat(uid, "supTDS");
//JSON payload
doc.clear();
doc["name"] = "Supply TDS";
doc["obj_id"] = "mqtt_TDSmonitor_supTDS";
doc["dev_cla"] = "volatile_organic_compounds_parts";
doc["uniq_id"] = uid;
doc["stat_t"] = "homeassistant/stat/TDSMonitor/supTDS";
doc["unit_of_meas"] = "ppm";
doc["ic"] = "mdi:water-pump";
JsonObject devicesupTDS = doc.createNestedObject("device");
devicesupTDS["ids"] = "tdsmonitor";
devicesupTDS["name"] = "TDS Monitor";
devicesupTDS["mf"] = "DeezNutzInc";
devicesupTDS["mdl"] = "TDS Monitor Elite";
devicesupTDS["sw"] = "1.89";
devicesupTDS["hw"] = "1.0";
devicesupTDS["cu"] = "http://" + localIP + "/config"; //web interface for device, with discovery toggle
serializeJson(doc, buffer1);
//Publish discovery topic and payload (with retained flag)
client.publish(topic, buffer1, true);
//Create prediTDS Sensor
Serial.println("Adding TDS Sensor...Pre-DI TDS");
//Create unique Topic based on devUniqueID
strcpy(topic, "homeassistant/sensor/");
strcat(topic, "TDSMonitor");
strcat(topic, "prediTDS/config");
//Create unique_id based on decUniqueID
strcpy(uid, "TDSMonitor");
strcat(uid, "prediTDS");
//Create JSON payload per HA documentation
doc.clear();
doc["name"] = "Pre-DI TDS";
doc["obj_id"] = "mqtt_TDSmonitor_prediTDS";
doc["dev_cla"] = "volatile_organic_compounds_parts";
doc["uniq_id"] = uid;
doc["stat_t"] = "homeassistant/stat/TDSMonitor/prediTDS";
doc["unit_of_meas"] = "ppm";
doc["ic"] = "mdi:toilet";
JsonObject deviceprediTDS = doc.createNestedObject("device");
deviceprediTDS["ids"] = "tdsmonitor";
deviceprediTDS["name"] = "TDS Monitor";
serializeJson(doc, buffer2);
//Publish discovery topic and payload (with retained flag)
client.publish(topic, buffer2, true);
//outTDS Sensor
Serial.println("Adding TDS Sensor...Output TDS");
//Create unique Topic based on devUniqueID
strcpy(topic, "homeassistant/sensor/");
strcat(topic, "TDSMonitor");
strcat(topic, "outTDS/config");
//Create unique_id based on decUniqueID
strcpy(uid, "TDSMonitor");
strcat(uid, "outTDS");
//Create JSON payload per HA documentation
doc.clear();
doc["name"] = "Output TDS";
doc["obj_id"] = "mqtt_TDSmonitor_outTDS";
doc["dev_cla"] = "volatile_organic_compounds_parts";
doc["uniq_id"] = uid;
doc["stat_t"] = "homeassistant/stat/TDSMonitor/outTDS";
doc["unit_of_meas"] = "ppm";
doc["ic"] = "mdi:water-check";
JsonObject deviceoutTDS = doc.createNestedObject("device");
deviceoutTDS["ids"] = "tdsmonitor";
deviceoutTDS["name"] = "TDS Monitor";
serializeJson(doc, buffer3);
//Publish discovery topic and payload (with retained flag)
client.publish(topic, buffer3, true);
//IP Address Diagnostic Sensor
Serial.println("Adding IP Diagnostic Sensor...");
//Create unique Topic based on devUniqueID
strcpy(topic, "homeassistant/sensor/");
strcat(topic, "TDSMonitor");
strcat(topic, "IP/config");
//Create unique_id based on decUniqueID
strcpy(uid, "TDSMonitor");
strcat(uid, "IP");
//Create JSON payload per HA documentation
doc.clear();
doc["name"] = "IP Address";
doc["uniq_id"] = uid;
doc["ent_cat"] = "diagnostic";
doc["stat_t"] = "homeassistant/stat/TDSMonitor/ipaddress";
JsonObject deviceIP = doc.createNestedObject("device");
deviceIP = doc.createNestedObject("device");
deviceIP["ids"] = "tdsmonitor";
deviceIP["name"] = "TDS Monitor";
serializeJson(doc, buffer4);
//Publish discovery topic and payload (with retained flag)
client.publish(topic, buffer4, true);
Serial.println("All Devices Added!");
//Create MAC Address as Diagnostic Sensor
//topic
strcpy(topic, "homeassistant/sensor/");
strcat(topic, "TDSMonitor");
strcat(topic, "MAC/config");
//Unique ID
strcpy(uid, "TDSMonitor");
strcat(uid, "MAC");
//JSON Payload
doc.clear();
doc["name"] = deviceName + " MAC Address";
doc["uniq_id"] = uid;
doc["ent_cat"] = "diagnostic";
doc["stat_t"] = "homeassistant/stat/TDSMonitor/macaddress";
JsonObject deviceMAC = doc.createNestedObject("device");
deviceMAC["ids"] = "tdsmonitor";
deviceMAC["name"] = "TDS Monitor";
serializeJson(doc, buffer5);
client.publish(topic, buffer5, true);
return 0;
} else {
//Remove all entities by publishing empty payloads
if (!client.connected()) {
if (!mqttReconnect_soft()) {
return 1;
}
}
//Must use original topic, so recreate from original Unique ID
//This will immediately remove/delete the device/entities from HA
Serial.println("Removing discovered devices...");
//supTDS Sensor
strcpy(topic, "homeassistant/sensor/");
strcat(topic, "TDSMonitor");
strcat(topic, "supTDS/config");
client.publish(topic, "");
//prediTDS Sensor
strcpy(topic, "homeassistant/sensor/");
strcat(topic, "TDSMonitor");
strcat(topic, "prediTDS/config");
client.publish(topic, "");
//outTDS Sensor
strcpy(topic, "homeassistant/sensor/");
strcat(topic, "TDSMonitor");
strcat(topic, "outTDS/config");
client.publish(topic, "");
//IP Diagnostics Sensor
strcpy(topic, "homeassistant/sensor/");
strcat(topic, "TDSMonitor");
strcat(topic, "IP/config");
client.publish(topic, "");
//MAC Address Sensor
strcpy(topic, "homeassistant/sensor/");
strcat(topic, "TDSMonitor");
strcat(topic, "MAC/config");
client.publish(topic, "");
Serial.println("All Devices Removed...");
return 0;
}
} else {
//MQTT not enabled... should never hit this as it is checked before calling
return 90;
}
//catch all
return 99;
}
/*Might not be required anymore can delete when working and after using baseline tds reading
void mqttConnect(){
client.setServer(mqtt_server, 1883);
client.setBufferSize(512);
client.setCallback(callback);
if (client.connect("TDSMonitor", "mqtt-user", "21October11")) {
client.publish("Aquarium","TDS Monitor Connected!");
client.subscribe("Aquarium");
Serial.println("MQTT Connected - Connection Mesage Sent!");
//send baseline TDS reading
sprintf(mqtt_tds_SUP, "TDS-SUP: %d",(int)tdsValue_SUP);
client.publish(mqtt_pub_topic_supTDS, mqtt_tds_SUP);
Serial.println("Supply TDS Telemetry Sent Successfully!");
Serial.println("");
sprintf(mqtt_tds_PRE_DI, "TDS-PRE_DI: %d",(int)tdsValue_PRE_DI);
client.publish(mqtt_pub_topic_prediTDS, mqtt_tds_PRE_DI);
Serial.println("Pre-DI TDS Telemetry Sent Successfully!");
Serial.println("");
sprintf(mqtt_tds_OUT, "TDS-OUT: %d",(int)tdsValue_OUT);
client.publish(mqtt_pub_topic_outTDS, mqtt_tds_OUT);
Serial.println("Output TDS Telemetry Sent Successfully!");
Serial.println("");
}
}
*/
void mqttReconnect() {
int retries = 0;
// Loop until we're reconnected
while (!client.connected()) {
if(retries <150)
{
//#if defined(SERIAL_DEBUG) && (SERIAL_DEBUG == 1)
Serial.print("Attempting to Re-Establish MQTT Connection...");
//#endif
if (client.connect(mqttClient.c_str(), mqttUser.c_str(), mqttPW.c_str()))
{
//#if defined(SERIAL_DEBUG) && (SERIAL_DEBUG == 1)
Serial.println("TDS Monitor Re-connected After Failure!");
//#endif
// ... and resubscribe
client.subscribe("Aquarium/#");
}
else
{
//#if defined(SERIAL_DEBUG) && (SERIAL_DEBUG == 1)
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
//#endif
retries++;
// Wait 5 seconds before retrying
delay(5000);
}
}
if ((retries > 149) && (mqttEnabled))
{
ESP.restart();
}
}
}
bool mqttReconnect_soft() {
//Attempt MQTT reconnect. If fails, return false instead of forcing ESP Reboot
int retries = 0;
while (!client.connected()) {
if(retries < 10)
{
//#if defined(SERIAL_DEBUG) && (SERIAL_DEBUG == 1)
Serial.print("Attempting to Re-Establish MQTT Connection...");
//#endif
if (client.connect(mqttClient.c_str(), mqttUser.c_str(), mqttPW.c_str()))
{
//#if defined(SERIAL_DEBUG) && (SERIAL_DEBUG == 1)
Serial.println("TDS Monitor Re-connected After Failure!");
//#endif
// ... and resubscribe
client.subscribe("Aquarium/#");
return true;
}
else
{
//#if defined(SERIAL_DEBUG) && (SERIAL_DEBUG == 1)
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
//#endif
retries++;
// Wait 3 seconds before retrying
delay(3000);
yield();
}
}
if ((retries > 9) && (mqttEnabled))
{
return false;
}
}
}
/* old mqtt call back, delete when working
void mqttCallback(char* topic, byte* payload, unsigned int length) {
// message received
}
*/
void GravityTDS_setup(){
gravityTds_SUP.setPin(TDS_SUP_Pin);
gravityTds_SUP.setAref(5.0); //reference voltage on ADC, default 5.0V on Arduino UNO
gravityTds_SUP.setAdcRange(1024); //1024 for 10bit ADC;4096 for 12bit ADC
gravityTds_SUP.setKvalueAddress(4); //set the EEPROM address to store the k value,default address:0x08
gravityTds_SUP.begin(); //initialization
gravityTds_PRE_DI.setPin(TDS_PRE_DI_Pin);
gravityTds_PRE_DI.setAref(5.0); //reference voltage on ADC, default 5.0V on Arduino UNO
gravityTds_PRE_DI.setAdcRange(1024); //1024 for 10bit ADC;4096 for 12bit ADC
gravityTds_PRE_DI.setKvalueAddress(8); //set the EEPROM address to store the k value,default address:0x08
gravityTds_PRE_DI.begin(); //initialization
gravityTds_OUT.setPin(TDS_OUT_Pin);
gravityTds_OUT.setAref(5.0); //reference voltage on ADC, default 5.0V on Arduino UNO
gravityTds_OUT.setAdcRange(1024); //1024 for 10bit ADC;4096 for 12bit ADC
gravityTds_OUT.setKvalueAddress(12); //set the EEPROM address to store the k value,default address:0x08
gravityTds_OUT.begin(); //initialization
}
void GravityTDS_results(){
//temperature = readTemperature(); //add your temperature sensor and read it
//Calculate TDS from Supply Water
gravityTds_SUP.setTemperature(temperature); // set the temperature and execute temperature compensation
gravityTds_SUP.update(); //sample and calculate
tdsValue_SUP = gravityTds_SUP.getTdsValue(); // then get the value
//Calculate TDS from Output of Membrane
gravityTds_PRE_DI.setTemperature(temperature); // set the temperature and execute temperature compensation
gravityTds_PRE_DI.update(); //sample and calculate
tdsValue_PRE_DI = gravityTds_PRE_DI.getTdsValue(); // then get the value
//Calculate TDS from Output
gravityTds_OUT.setTemperature(temperature); // set the temperature and execute temperature compensation
gravityTds_OUT.update(); //sample and calculate
tdsValue_OUT = gravityTds_OUT.getTdsValue(); // then get the value
if(tdsValue_SUP == prevtdsValue_SUP) {
Serial.print("Supply TDS reading hasnt changed (Current/Last Read): ");
Serial.print(tdsValue_SUP, 0); Serial.print("ppm");
Serial.print(" : ");
Serial.print(prevtdsValue_SUP); Serial.println("ppm");
delay(100);
// return;
} else {
prevtdsValue_SUP = tdsValue_SUP;
Serial.print("SUP TDS: "); Serial.print(tdsValue_SUP, 0); Serial.println("ppm");
sprintf(mqtt_tds_SUP, "%d",(int)tdsValue_SUP);
client.publish(mqtt_supTDS_stat, mqtt_tds_SUP);
sprintf(mqtt_tds_SUP2, "TDS-SUP: %d",(int)tdsValue_SUP);
client.publish(mqtt_pub_topic_supTDS, mqtt_tds_SUP2);
Serial.println("Supply TDS Telemetry Sent Successfully!");
delay(100);
}
if(tdsValue_PRE_DI == prevtdsValue_PRE_DI) {
Serial.print("Pre-DI TDS reading hasnt changed (Current/Last Read): ");
Serial.print(tdsValue_PRE_DI, 0); Serial.print("ppm");
Serial.print(" : ");
Serial.print(prevtdsValue_PRE_DI); Serial.println("ppm");
delay(100);
// return;
} else {
prevtdsValue_PRE_DI = tdsValue_PRE_DI;
Serial.print("PRE-DI TDS: "); Serial.print(tdsValue_PRE_DI, 0); Serial.print("ppm");
sprintf(mqtt_tds_PRE_DI, "%d",(int)tdsValue_PRE_DI);
client.publish(mqtt_prediTDS_stat, mqtt_tds_PRE_DI);
sprintf(mqtt_tds_PRE_DI2, "TDS-PRE_DI: %d",(int)tdsValue_PRE_DI);
client.publish(mqtt_pub_topic_prediTDS, mqtt_tds_PRE_DI2);
Serial.println("Pre-DI TDS Telemetry Sent Successfully!");
delay(100);
}
if(tdsValue_OUT == prevtdsValue_OUT) {
Serial.print("Output TDS reading hasnt changed (Current/Last Read): ");
Serial.print(tdsValue_OUT, 0); Serial.print("ppm");
Serial.print(" : ");
Serial.print(prevtdsValue_OUT); Serial.println("ppm");
delay(100);
// return;
} else {
prevtdsValue_OUT = tdsValue_OUT;
Serial.print("OUT TDS: "); Serial.print(tdsValue_OUT, 0); Serial.println("ppm");
sprintf(mqtt_tds_OUT, "%d",(int)tdsValue_OUT);
client.publish(mqtt_outTDS_stat, mqtt_tds_OUT);
sprintf(mqtt_tds_OUT2, "TDS-OUT: %d",(int)tdsValue_OUT);
client.publish(mqtt_pub_topic_outTDS, mqtt_tds_OUT2);
Serial.println("Output TDS Telemetry Sent Successfully!");
delay(100);
}
delay(3000);
}
void setup() {
// Serial monitor
//#if defined(SERIAL_DEBUG) && (SERIAL_DEBUG == 1)
Serial.begin(115200);
Serial.println("Booting...");
//#endif
//RGB_LED_Setup();
// -------------
// SETUP FASTLED
// -------------
FastLED.addLeds<WS2812B, LED_DATA_PIN, GRB>(LEDs, NUM_LEDS_MAX);
FastLED.setDither(false);
FastLED.setCorrection(TypicalLEDStrip);
FastLED.setBrightness(activeBrightness);
setup_Wifi();
//mqttConnect();
client.setCallback(callback);
resetWiFiSettings();
GravityTDS_setup();
MDNS.begin(host);
//Get MAC address when joining wifi and place into char array
WiFi.macAddress(macAddr);
//Call routing (or embed here) to create initial Unique ID
createDiscoveryUniqueID();
//Handle web callbacks for enabling or disabling discovery (using this method is just one of many ways to do this)
// Default Wifi to station mode - fixes issue #16
// Local AP will be handed by WifiManager for onboarding
WiFi.mode(WIFI_STA);
// -----------------------------------------
// Captive Portal and Wifi Onboarding Setup
// -----------------------------------------
//clean FS, for testing - uncomment next line ONLY if you wish to wipe current FS
//SPIFFS.format();
// *******************************
// read configuration from FS json
// *******************************
//#if defined(SERIAL_DEBUG) && (SERIAL_DEBUG == 1)
Serial.println("mounting FS...");
//#endif
readConfigFile();
// The extra parameters to be configured (can be either global or just in the setup)
// After connecting, parameter.getValue() will get you the configured value
// id/name placeholder/prompt default length
WiFiManagerParameter custom_text("<p>Each parking assistant must have a unique name. Letters and numbers only, no spaces, 16 characters max. See the wiki documentation for more info.</p>");
WiFiManagerParameter custom_dev_id("devName", "Unique Device Name", "parkasst", 16, " 16 chars/alphanumeric only");
//set config save notify callback
wifiManager.setSaveConfigCallback(saveConfigCallback);
//set static ip
//wifiManager.setSTAStaticIPConfig(IPAddress(10, 0, 1, 99), IPAddress(10, 0, 1, 1), IPAddress(255, 255, 255, 0));
//add all your parameters here
wifiManager.addParameter(&custom_text);
wifiManager.addParameter(&custom_dev_id);
//reset settings - for testing
//wifiManager.resetSettings();
//sets timeout until configuration portal gets turned off
//useful to make it all retry or go to sleep
//in seconds
wifiManager.setTimeout(360);
//fetches ssid and pass and tries to connect
//if it does not connect it starts an access point with the specified name "ESP_ParkingAsst"
//If not supplied, will use ESP + last 7 digits of MAC
//and goes into a blocking loop awaiting configuration. If a password
//is desired for the AP, add it after the AP name (e.g. autoConnect("MyApName", "12345678")
if (!wifiManager.autoConnect("TDS Monitor")) { //
//#if defined(SERIAL_DEBUG) && (SERIAL_DEBUG == 1)
Serial.println("failed to connect and hit timeout");
//#endif
delay(3000);
//reset and try again, or maybe put it to deep sleep
ESP.restart();
delay(5000);
}
//Get custom device name and set wifi and OTA host name, mqttclient and
if (shouldSaveConfig) {
strcpy(device_name, custom_dev_id.getValue());
}
deviceName = String(device_name);
//Use device name to define host names and mqttclient name
wifiHostName = deviceName;
otaHostName = deviceName + "OTA";
mqttClient = deviceName;
//if you get here you have connected to the WiFi
WiFi.hostname(wifiHostName.c_str());
//#if defined(SERIAL_DEBUG) && (SERIAL_DEBUG == 1)
Serial.println("connected to your wifi...yay!");
//#endif
//If callback was excuted, flag was set to save parameters
if (shouldSaveConfig) {
updateBootSettings(false); //don't reboot
delay(1000);
readConfigFile(); //Load settings
}
//#if defined(SERIAL_DEBUG) && (SERIAL_DEBUG == 1)
Serial.println("local ip");
Serial.println(WiFi.localIP());
//#endif
localIP = WiFi.localIP().toString();
WiFi.macAddress(macAddr); //Array for Unique ID gen
strMacAddr = WiFi.macAddress(); //String for MQTT
// ----------------------------
mqttAddr_1 = (String(mqtt_addr_1)).toInt();
mqttAddr_2 = (String(mqtt_addr_2)).toInt();
mqttAddr_3 = (String(mqtt_addr_3)).toInt();
mqttAddr_4 = (String(mqtt_addr_4)).toInt();
//Disable MQTT if IP = 0.0.0.0
if ((mqttAddr_1 == 0) && (mqttAddr_2 == 0) && (mqttAddr_3 == 0) && (mqttAddr_4 == 0)) {
mqttPort = 0;
mqttEnabled = false;
mqttConnected = false;
} else {
mqttPort = (String(mqtt_port)).toInt();
mqttTelePeriod = (String(mqtt_tele_period)).toInt();
mqttUser = String(mqtt_user);
mqttPW = String(mqtt_pw);
mqttTopicSub = String(mqtt_topic_sub);
mqttTopicPub = String(mqtt_topic_pub);
mqttEnabled = true;
}
//------------------------------
// Setup handlers for web calls
//------------------------------
server.on("/", handleRoot);
server.on("/postform/", handleForm);
server.onNotFound(handleNotFound);
server.on("/update", handleUpdate);
server.on("/restart", handleRestart);
server.on("/reset", handleReset);
server.on("/discovery", handleDiscovery);
server.on("/discoveryEnabled", enableDiscovery);
server.on("/discoveryDisabled", disableDiscovery);
server.on("/calibration", calibration);
server.on("/otaupdate",[]() {
//Called directly from browser address (//ip_address/otaupdate) to put controller in ota mode for uploadling from Arduino IDE
server.send(200, "text/html", "<h1>Ready for upload...<h1><h3>Start upload from IDE now</h3>");
ota_flag = true;
ota_time = ota_time_window;
ota_time_elapsed = 0;
});
//Firmware Update Handler
httpUpdater.setup(&server, "/update2");
httpUpdater.setup(&server);
server.begin();
//#if defined(SERIAL_DEBUG) && (SERIAL_DEBUG == 1)
Serial.println("Setup complete - starting main loop");
//#endif
//PROBABLY DONT NEED BUT KEEP TIL WORKING
server.on("/discovery_on",[]() {
server.send(200, "text/html", "<h1>Discovery ON...<h1><h3>Home Assistant MQTT Discovery enabled</h3>");
delay(200);
auto_discovery = true;
byte retVal = haDiscovery(true);
});
server.on("/discovery_off",[]() {
server.send(200, "text/html", "<h1>Discovery OFF...<h1><h3>Home Assistant MQTT Discovery disabled. Previous entities removed.</h3>");
delay(200);
auto_discovery = false;
byte retVal = haDiscovery(false);
});
// =====================
// MQTT Setup
// =====================
if (mqttEnabled) {
//Attempt to connect to MQTT broker - if fails, disable MQTT
if (!setup_mqtt()) {
mqttEnabled = false;
}
}
//-----------------------------
// Setup OTA Updates
//-----------------------------
ArduinoOTA.setHostname(otaHostName.c_str());
ArduinoOTA.onStart([]() {
String type;
if (ArduinoOTA.getCommand() == U_FLASH) {
type = "sketch";
} else { // U_FS
type = "filesystem";
}
// NOTE: if updating FS this would be the place to unmount FS using FS.end()
});
ArduinoOTA.begin();
}
void loop() {
if (WiFi.status() != WL_CONNECTED) {
wifiReconnect();
}
//RGB_LED();
GravityTDS_results();
resetWiFiSettings();
server.handleClient();
//Handle OTA updates when OTA flag set via HTML call to http://ip_address/otaupdate
if (ota_flag) {
updateOTA(); //Show update on LED strip
uint32_t ota_time_start = millis();
while (ota_time_elapsed < ota_time) {
ArduinoOTA.handle();
ota_time_elapsed = millis()-ota_time_start;
delay(10);
}
ota_flag = false;
updateSleepMode();
}
uint32_t currentMillis = millis();
int16_t distance = 0;
/*
******** UPDATE WITH SOMETHING LIKE IF STATE CHANGE FOR TDS
//v0.44 - force MQTT update if car state changes
if (carDetected != prevCarStatus) {
prevCarStatus = carDetected;
forceMQTTUpdate = true;
}
*/
//Put system to sleep if parking or exit time elapsed
uint32_t elapsedTime = currentMillis - startTime;
if (((elapsedTime > (maxOperationTimePark * 1000)) && (parkSleepTimerStarted)) || ((elapsedTime > (maxOperationTimeExit * 1000)) && (exitSleepTimerStarted ))) {
updateSleepMode();
isAwake = false;
startTime = currentMillis;
exitSleepTimerStarted = false;
parkSleepTimerStarted = false;
}
//Handle MQTT calls (if enabled)
if (mqttEnabled) {
#if defined(MQTTMODE) && (MQTTMODE == 1 && (WIFIMODE == 1 || WIFIMODE == 2))
if (!client.connected())
{
mqttReconnect();
}
client.loop();
#endif
}
//Update MQTT Stats per tele period
if (mqttEnabled) {
if (((currentMillis - mqttLastUpdate) > (mqttTelePeriod * 1000)) || (forceMQTTUpdate)) {
mqttLastUpdate = currentMillis;
forceMQTTUpdate = false;
if (!client.connected()) {
mqttReconnect();
}
//Keep to use with own criteria
/*
// Publish MQTT values
char outMsg[6];
byte carStatus = 0;
float measureDistance = 0;
if (carDetected) carStatus = 1;
sprintf(outMsg, "%1u",carStatus);
client.publish(("homeassistant/stat/" + mqttTopicPub + "/cardetected").c_str(), outMsg, true);
sprintf(outMsg, "%2.1f", measureDistance);
client.publish(("homeassistant/stat/" + mqttTopicPub + "/parkdistance").c_str(), outMsg, true);
*/
}
}
//Show/Refresh LED Strip
FastLED.show();
delay(200);
}
Apologies in advance I know I have bit off a lot more than I can chew but in reality for my purposes, it literally all works bar 2 things:
those values being off by the rounding
the page for calibration whereby I am planning to update to the eeprom the values for the calibration function built into the gravtytds library (it will be my next job once i figure out these rounded numbers)
Then all that is left is just to remove all the unnecessary codes which references stuff from the original project I pulled from eg the parking project