#include #include #include #include #include #include #include "secrets.h" #define WIFI_CHECK_INTERVAL 30000 #define SENSOR_SAMPLE_INTERVAL 5000 #define MQTT_PUSH_INTERVAL 60000 #define SAMPLE_HISTORY_N 60 // 60*5s => 5m running average #define PIN_ONEWIRE 4 #define PIN_TDS 34 #define VREF 3.3 float ec_calibration = 1.0f; #define CTEMP temperatures[sensors_current_i] #define CTDS tds[sensors_current_i] #define CEC ec[sensors_current_i] const char *ssid = WIFI_SSID; const char *password = WIFI_PASSWORD; const char *mqtt_broker = MQTT_BROKER; const char *topic_temp = "emqx/nft_system_temp"; const char *topic_tds = "emqx/nft_system_tds"; const char *topic_ec = "emqx/nft_system_ec"; const char *mqtt_username = MQTT_USERNAME; const char *mqtt_password = MQTT_PASSWORD; const int mqtt_port = 1883; WiFiClient espClient; PubSubClient client(espClient); OneWire oneWire(PIN_ONEWIRE); DallasTemperature sensors(&oneWire); int sensors_current_i = 0; bool sensors_initialized = false; float temperatures[SAMPLE_HISTORY_N]; float temperatures_total = 0.0f; float temperatures_average = 0.0f; bool temperature_mqtt_last_publish_status = false; float tds[SAMPLE_HISTORY_N]; float tds_total = 0.0f; float tds_average = 0.0f; bool tds_mqtt_last_publish_status = false; float ec[SAMPLE_HISTORY_N]; float ec_total = 0.0f; float ec_average = 0.0f; bool ec_mqtt_last_publish_status = false; unsigned long lastWifiCheck = 0; unsigned long lastSensorSample = 0; unsigned long lastMqttPublish = 0; WebServer server(80); const char *serverIndex PROGMEM = "" "" "" "" "
SensorsInitialized: %dLast sample: %dms ago

" "
Temperature%.1fC ~ %.1fCMQTT: %d

" "
TDS%.0f ~ %.0fMQTT: %d

" "
EC%.1f ~ %.1fMQTT: %d

" "
MQTTLast %dms ago

" "
" "" ""; void setup_wifi() { delay(3); Serial.println(); Serial.print("Connecting to "); Serial.println(ssid); WiFi.mode(WIFI_STA); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } randomSeed(micros()); Serial.println(""); Serial.println("WiFi connected"); Serial.println("IP address: "); Serial.println(WiFi.localIP()); } void reconnect() { while (!client.connected()) { Serial.print("Attempting MQTT connection..."); String client_id = "esp32-client-"; client_id += String(WiFi.macAddress()); if (client.connect(client_id.c_str(), mqtt_username, mqtt_password)) { Serial.println("connected"); } else { Serial.print("failed, rc="); Serial.print(client.state()); Serial.println(" try again in 5 seconds"); delay(5000); } } } void initialize_sensors() { sensors.begin(); pinMode(PIN_TDS, INPUT); for (int i = 0; i < SAMPLE_HISTORY_N; i++) { temperatures[i] = 0.0f; } } void setup() { Serial.begin(115200); Serial.println("init"); initialize_sensors(); setup_wifi(); client.setServer(mqtt_broker, mqtt_port); server.on("/", HTTP_GET, []() { unsigned long now = millis(); server.sendHeader("Connection", "close"); char buffer[strlen(serverIndex)+64]; sprintf( buffer, serverIndex, sensors_initialized, now - lastSensorSample, CTEMP, temperatures_average, temperature_mqtt_last_publish_status, CTDS, tds_average, tds_mqtt_last_publish_status, CEC, ec_average, ec_mqtt_last_publish_status, now - lastMqttPublish ); server.send(200, "text/html", buffer); }); server.on( "/update", HTTP_POST, []() { server.sendHeader("Connection", "close"); server.send(200, "text/plain", (Update.hasError()) ? "FAIL" : "OK"); ESP.restart(); }, []() { HTTPUpload &upload = server.upload(); if (upload.status == UPLOAD_FILE_START) { Serial.setDebugOutput(true); Serial.printf("Update: %s\n", upload.filename.c_str()); if (!Update.begin()) { //start with max available size Update.printError(Serial); } } else if (upload.status == UPLOAD_FILE_WRITE) { if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) { Update.printError(Serial); } } else if (upload.status == UPLOAD_FILE_END) { if (Update.end(true)) { //true to set the size to the current progress Serial.printf("Update Success: %u\nRebooting...\n", upload.totalSize); } else { Update.printError(Serial); } Serial.setDebugOutput(false); } else { Serial.printf(F("Update Failed Unexpectedly (likely broken connection): status=%d\n"), upload.status); } } ); server.begin(); } void updateSensorValues() { sensors.requestTemperatures(); sensors_current_i++; if (sensors_current_i >= SAMPLE_HISTORY_N) { sensors_current_i = 0; sensors_initialized = true; } // Temperature 1 temperatures_total -= CTEMP; CTEMP = sensors.getTempCByIndex(0); temperatures_total += CTEMP; temperatures_average = temperatures_total / SAMPLE_HISTORY_N; // TDS 1/EC 1 ec_total -= CEC; tds_total -= CTDS; float rawEc = analogRead(PIN_TDS) * VREF / 4096.0; // read the analog value more stable by the median filtering algorithm, and convert to voltage value float temperatureCoefficient = 1.0 + 0.02 * (temperatures_average - 25.0); // temperature compensation formula: fFinalResult(25^C) = fFinalResult(current)/(1.0+0.02*(fTP-25.0)); CEC = (rawEc / temperatureCoefficient) * ec_calibration; // temperature and calibration compensation ec_total += CEC; ec_average = ec_total / SAMPLE_HISTORY_N; CTDS = (133.42 * pow(CEC, 3) - 255.86 * CEC * CEC + 857.39 * CEC) * 0.5; //convert voltage value to tds value tds_total += CTDS; tds_average = tds_total / SAMPLE_HISTORY_N; Serial.print(F("TDS:")); Serial.println(CTDS); Serial.print(F("EC:")); Serial.println(CEC, 2); Serial.print(F(" ")); Serial.print(CTEMP); Serial.print(F(" C")); Serial.print(F("~")); Serial.print(temperatures_average); Serial.println(F(" C")); } void loop() { unsigned long now = millis(); if ((WiFi.status() != WL_CONNECTED) && (now - lastWifiCheck > WIFI_CHECK_INTERVAL)) { Serial.println("Reconnecting to WiFi..."); WiFi.disconnect(); WiFi.reconnect(); lastWifiCheck = now; } server.handleClient(); if (!client.connected()) { reconnect(); } client.loop(); now = millis(); if (now - lastSensorSample > SENSOR_SAMPLE_INTERVAL) { lastSensorSample = now; updateSensorValues(); } if (now - lastMqttPublish > MQTT_PUSH_INTERVAL) { lastMqttPublish = now; if (sensors_initialized) { // Publish temperature char result[8]; // Buffer big enough for 7-character float dtostrf(temperatures_average, 6, 1, result); // Leave room for too large numbers! temperature_mqtt_last_publish_status = client.publish(topic_temp, result); Serial.println("Published temperature update"); // Publish tds result[8]; // Buffer big enough for 7-character float dtostrf(tds_average, 6, 0, result); // Leave room for too large numbers! tds_mqtt_last_publish_status = client.publish(topic_tds, result); Serial.println("Published tds update"); // Publish ec result[8]; // Buffer big enough for 7-character float dtostrf(ec_average, 6, 1, result); // Leave room for too large numbers! ec_mqtt_last_publish_status = client.publish(topic_ec, result); Serial.println("Published ec update"); } } } // median filtering algorithm // https://randomnerdtutorials.com/arduino-tds-water-quality-sensor/ int getMedianNum(int bArray[], int iFilterLen){ int bTab[iFilterLen]; for (byte i = 0; i bTab[i + 1]) { bTemp = bTab[i]; bTab[i] = bTab[i + 1]; bTab[i + 1] = bTemp; } } } if ((iFilterLen & 1) > 0){ bTemp = bTab[(iFilterLen - 1) / 2]; } else { bTemp = (bTab[iFilterLen / 2] + bTab[iFilterLen / 2 - 1]) / 2; } return bTemp; }