I'm trying to make an automated greenhouse and everything works except when trying to use sliders on the Nextion to change pH and Nutrient setpoints.
I've looked at Perry Bebbington's page on making it work without the library, but I'm at a loss.
Here's the Arduino code, but I'm pretty sure I'm messing up in the Nextion Editor:
#include <DHT.h>
#include <PID_v1_bc.h>
#include <Nextion.h>
#define DHTPIN 2
#define DHTTYPE DHT22
#define PH_PIN A0 // Pin connected to Po on the pH sensor
#define TDS_PIN A1
#define TEMP_PIN A2 // Pin connected to T1 or T2 for water temperature
#define PUMP_PH_UP_PIN 8
#define PUMP_PH_DOWN_PIN 9
#define PUMP_NUTRIENT_PIN 10
DHT dht(DHTPIN, DHTTYPE);
// PID parameters
double Setpoint_pH, Input_pH, Output_pH;
double Setpoint_TDS, Input_TDS, Output_TDS;
double Kp_pH = 2.0, Ki_pH = 5.0, Kd_pH = 1.0;
double Kp_TDS = 2.0, Ki_TDS = 5.0, Kd_TDS = 1.0;
// Create PID controllers
PID pH_PID(&Input_pH, &Output_pH, &Setpoint_pH, Kp_pH, Ki_pH, Kd_pH, DIRECT);
PID TDS_PID(&Input_TDS, &Output_TDS, &Setpoint_TDS, Kp_TDS, Ki_TDS, Kd_TDS, DIRECT);
// Timing variables for delays
unsigned long lastPHCorrectionTime = 0;
unsigned long lastTDSCorrectionTime = 0;
const unsigned long correctionDelay = 120000; // 2 minutes in milliseconds
void setup() {
Serial.begin(9600); // For debugging
Serial1.begin(9600); // For Nextion communication
dht.begin();
pinMode(PUMP_PH_UP_PIN, OUTPUT);
pinMode(PUMP_PH_DOWN_PIN, OUTPUT);
pinMode(PUMP_NUTRIENT_PIN, OUTPUT);
// Initialize PID controllers
Setpoint_pH = 6.5; // Desired pH setpoint
Setpoint_TDS = 1200; // Desired TDS setpoint
pH_PID.SetMode(AUTOMATIC);
TDS_PID.SetMode(AUTOMATIC);
// Debug message to confirm setup
Serial.println("Starting sensor data transmission to Nextion display...");
}
void loop() {
// Read sensors
float airTemp = dht.readTemperature();
float humidity = dht.readHumidity();
float waterTemp = readWaterTemp(); // Read water temperature from T1 or T2
if (isnan(airTemp) || isnan(humidity)) {
Serial.println("Failed to read from DHT sensor!");
return;
}
Input_pH = readPHValue(); // Read pH value from the sensor
Input_TDS = analogRead(TDS_PIN) * (1500.0 / 1023.0); // Example calculation for TDS
// Update setpoints from Nextion controls
updateSetpointsFromNextion();
// Compute PID and control pH
computePHControl();
// Compute PID and control TDS
controlNutrientPump();
// Send data to the Nextion display
sendToNextion(airTemp, humidity, Input_pH, Input_TDS, Setpoint_pH, waterTemp, Setpoint_TDS);
// Debug output
Serial.print("Air Temp: ");
Serial.println(airTemp);
Serial.print("Humidity: ");
Serial.println(humidity);
Serial.print("Water Temp: ");
Serial.println(waterTemp);
Serial.print("pH: ");
Serial.println(Input_pH);
Serial.print("TDS: ");
Serial.println(Input_TDS);
delay(1000); // Update every second
}
// Function to compute and control pH
void computePHControl() {
pH_PID.Compute();
// Deadband of ±0.2 around the setpoint
double deadband = 0.2;
// Calculate the difference from the setpoint
double pH_difference = Setpoint_pH - Input_pH;
// Get the current time
unsigned long currentTime = millis();
if (currentTime - lastPHCorrectionTime >= correctionDelay) {
if (abs(pH_difference) <= deadband) {
// Within acceptable range, turn off both pumps
digitalWrite(PUMP_PH_UP_PIN, LOW);
digitalWrite(PUMP_PH_DOWN_PIN, LOW);
} else if (pH_difference > 0) {
// pH is too low, turn on the pH up pump
digitalWrite(PUMP_PH_UP_PIN, HIGH);
digitalWrite(PUMP_PH_DOWN_PIN, LOW);
lastPHCorrectionTime = currentTime; // Update the last correction time
} else {
// pH is too high, turn on the pH down pump
digitalWrite(PUMP_PH_UP_PIN, LOW);
digitalWrite(PUMP_PH_DOWN_PIN, HIGH);
lastPHCorrectionTime = currentTime; // Update the last correction time
}
} else {
// If within the delay period, ensure pumps are off
digitalWrite(PUMP_PH_UP_PIN, LOW);
digitalWrite(PUMP_PH_DOWN_PIN, LOW);
}
}
// Function to control nutrient pump based on TDS PID output
void controlNutrientPump() {
TDS_PID.Compute();
// Deadband of ±100 around the setpoint
double deadband = 100.0;
// Calculate the difference from the setpoint
double TDS_difference = Setpoint_TDS - Input_TDS;
// Get the current time
unsigned long currentTime = millis();
if (currentTime - lastTDSCorrectionTime >= correctionDelay) {
if (abs(TDS_difference) <= deadband) {
// Within acceptable range, turn off the nutrient pump
digitalWrite(PUMP_NUTRIENT_PIN, LOW);
} else if (TDS_difference > 0) {
// TDS is too low, turn on the nutrient pump
digitalWrite(PUMP_NUTRIENT_PIN, HIGH);
lastTDSCorrectionTime = currentTime; // Update the last correction time
} else {
// TDS is too high, turn off the nutrient pump
digitalWrite(PUMP_NUTRIENT_PIN, LOW);
}
} else {
// If within the delay period, ensure pump is off
digitalWrite(PUMP_NUTRIENT_PIN, LOW);
}
}
// Function to send data to the Nextion display
void sendToNextion(float airTemp, float humidity, float pH, float TDS, float pHSetpoint, float waterTemp, float TDSSetpoint) {
char buffer[20];
// Air temperature
dtostrf(airTemp, 4, 1, buffer);
sendNextionCommand("airTempTxt", buffer);
// Humidity
dtostrf(humidity, 4, 1, buffer);
sendNextionCommand("humidityTxt", buffer);
// pH
dtostrf(pH, 4, 2, buffer);
sendNextionCommand("pHTxt", buffer);
// TDS
dtostrf(TDS, 4, 0, buffer);
sendNextionCommand("tdsTxt", buffer);
// pH Setpoint
dtostrf(pHSetpoint, 4, 2, buffer);
sendNextionCommand("pHSet", buffer);
// Water temperature
dtostrf(waterTemp, 4, 1, buffer);
sendNextionCommand("waterTempTxt", buffer);
// TDS Setpoint
dtostrf(TDSSetpoint, 4, 0, buffer);
sendNextionCommand("tdsSet", buffer);
}
// Helper function to send commands to the Nextion display
void sendNextionCommand(const char* component, const char* value) {
Serial1.print(component);
Serial1.print(".txt=\"");
Serial1.print(value);
Serial1.print("\"");
sendEndCommand();
}
// Function to send the end command for Nextion communication
void sendEndCommand() {
Serial1.write(0xff);
Serial1.write(0xff);
Serial1.write(0xff);
}
// Function to read pH value from the sensor
float readPHValue() {
int sensorValue = analogRead(PH_PIN);
float voltage = sensorValue * (5.0 / 1023.0); // Convert analog reading to voltage
// Convert the voltage to pH value based on the sensor's characteristics
return mapVoltageToPH(voltage);
}
// Function to map voltage to pH value
float mapVoltageToPH(float voltage) {
// Example conversion based on a typical pH sensor
// Adjust the mapping based on your sensor's datasheet
return 7.0 + ((2.5 - voltage) * 3.0);
}
// Function to read water temperature from T1 or T2 pin
float readWaterTemp() {
int sensorValue = analogRead(TEMP_PIN);
float voltage = sensorValue * (5.0 / 1023.0); // Convert analog reading to voltage
// Example conversion for typical temperature sensor (e.g., TMP36)
// Adjust this formula based on your specific sensor.
float temperatureC = (voltage - 0.5) * 100.0;
return temperatureC;
}
// Function to update setpoints from Nextion controls
void updateSetpointsFromNextion() {
// Read pH setpoint from phCtrl
Serial1.print("get phCtrl.val");
sendEndCommand();
while (Serial1.available() == 0); // Wait for response
Setpoint_pH = Serial1.parseInt(); // Read the integer value
updateTextField("pHSet", Setpoint_pH);
// Read TDS setpoint from tdsCtrl
Serial1.print("get tdsCtrl.val");
sendEndCommand();
while (Serial1.available() == 0); // Wait for response
Setpoint_TDS = Serial1.parseInt(); // Read the integer value
updateTextField("tdsSet", Setpoint_TDS);
}
// Helper function to update a text field on the Nextion display
void updateTextField(const char* component, int value) {
char buffer[10];
itoa(value, buffer, 10); // Convert integer to string
sendNextionCommand(component, buffer);
if (nexSerial.available()) {
String receivedData = nexSerial.readStringUntil('\n'); // Adjust the delimiter if necessary
// Handle pH slider input
if (receivedData.startsWith("phSlider.val=")) {
Setpoint_pH = receivedData.substring(13).toFloat(); // Extract the value after the equals sign and convert it to float
// Update the display or take other actions with the new Setpoint_pH value
}
// Handle TDS slider input
if (receivedData.startsWith("tdsSlider.val=")) {
Setpoint_TDS = receivedData.substring(14).toFloat(); // Extract the value after the equals sign and convert it to float
// Update the display or take other actions with the new Setpoint_TDS value
}
}
}