Nothing fancy here. The ESP is 3.3V logic and I had some issue using 5V relais, npn transistors and the digital outputs of the nodemcu. Probably my (lack of) soldering skillz are the issue but sometimes the connections were working, sometimes not. Next best thing: using two solid state relais, as you can see on the figure below.
Software controlling the lamp and the pump is nothing fancy. The lamp is on for 9 hours and then off for 15. The pump is on for 10 minutes and off for 4 hours. No pump when the light is off. Since the ESP has not Real Time Clock, the micros() function is used and each minute I update a parameter to keep track of the time pased during each cycle.
Wanting to be able to update the settings of the pump and the light remotely, and the reason why I am trying the ESP is for finding out what the Wifi module can do.
The easiest way I found was using the ESP8266WebServer and the GET method. Programming the Web-page, of which the first version looks like this:
Getting the information afterward is then easy, checking the content of the parameters attached to the html forms.
if (server.arg("newLightOn") != "") {
String ipt1 = server.arg("newLightOn");
LightCycle_on = ipt1.toInt();
LightCycletime = (((LightStatus + 1) % 2) * LightCycle_off) + (((LightStatus) % 2) * LightCycle_on);
}
The one thing I don't like the way the html page is built, writing it as one long string, which is not really clear and cumbersome to add css styling.
As you might already see in the first image I connected a BME sensor as well, and in the future I want to monitor the temperature and moister, so that I can update pump and light timings for instance.
When resetting the ESP module I need to decouple one of the digital outputs for it to boot up correctly. Uh, if you have any idea why, please let me know.
Not convinced about my settings for the Wifi. I am only able to connect to the module when I am close, and even then it is not consistent. Even though I want the ESP to connect to the avaiable Wifi another hotspot is still created that I can sometimes more easily connect to "esp8266". Could this be something lingering in the flash from a previous build?
UPDATE:
Previous issues were both my fault (which was to be epected). An additional line of code makes sure the ESP does not make his own access point by correclty setting the wifi.mode(WIFI_STA) as a STATION, as defined here.
I was unhappy with the ESP8266's behavior at startup and had some issues connecting to if from a distance. Both are ok, either because of the added line of code as mentioned earlier, or, and I think this mainly solved the start-up bahavior, I do not longer use the GPIO0 pin, which is apparently used UART or FLASH mode of the modue, which makes this page an interesting read.
Below the full code of the project so far, feel free reach out if you have comments or remarks:
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WiFiMulti.h>
#include <ESP8266mDNS.h>
#include <ESP8266WebServer.h>
ESP8266WiFiMulti wifiMulti; // Create an instance of the ESP8266WiFiMulti class, called 'wifiMulti'
ESP8266WebServer server(80); // Create a webserver object that listens for HTTP request on port 80
void handleRoot(); // Function for HTTP handlers
void handleNotFound(); // Function for HTTP handlers
// Parameters for UV light controls
int LightOutputPin = 12; // Pin on the ESP controlling the light
int LightManual = 0; // Manually control state of the light
int LightStatus = 0; // Current status of the light
int LightCycle_on = 8 * 60; // Minutes.
int LightCycle_off = 16 * 60; // Minutes.
int LightCycletime = LightCycle_off; // Cycle to start with
int Light_minutes = 0; // Timer to keep track of the current cycle
// Parameters for the nutrient pump
int PumpOutputPin = 14; // Pin on the ESP controlling the pump
int PumpManual = 0; // manually control state of the light
int PumpStatus = 0; // Current status of the pump
int PumpCycle_on = 10; // Minutes.
int PumpCycle_off = 5 * 60; // Minutes.
int PumpCycletime = PumpCycle_off; // Cycle to start with
int Pump_minutes = 0; // Timer to keep track of the current cycle
// Manual control active yes or no?
int manualCtrl_active = 0;
// timer for the micros() to keep track of the minutes
unsigned long timer;
// Visualizations for the site
String ControlType[2] = {"automatic", "manual"};
String activeState[2] = {"OFF", "ON"};
void setup(void) {
// Setting the initial states of the digital outputs
pinMode(LightOutputPin, OUTPUT);
pinMode(PumpOutputPin, OUTPUT);
digitalWrite(LightOutputPin, LightStatus);
digitalWrite(PumpOutputPin, PumpStatus);
Serial.begin(9600); // Start the Serial communication to send messages to the computer
delay(10);
Serial.println('
');
// Settings if you want an access point for the ESP instead of connecting to your Wifi
//WiFi.softAP("ssid", "password"); // we want its own wifi network not connect to existing
//IPAddress myIP = WiFi.softAPIP();
//Serial.print("AP IP address:");
//Serial.println(myIP); // Send the IP address of the ESP8266 to the computer
WiFi.mode(WIFI_STA); // configures the wifi mode as a STATION
// Settings if you want the ESP to connect with existing WIFI network
wifiMulti.addAP("SSID", "Password"); // add Wi-Fi networks you want to connect to
Serial.println("Connecting ...");
int i = 0;
// Wait for the Wi-Fi to connect: scan for Wi-Fi networks, and connect to the strongest of the networks above
while (wifiMulti.run() != WL_CONNECTED) {
delay(250);
Serial.print('.');
}
Serial.println('
');
Serial.print("Connected to ");
Serial.println(WiFi.SSID()); // Tell us what network we're connected to
Serial.print("IP address: ");
Serial.println(WiFi.localIP()); // Send the IP address of the ESP8266 to the computer
if (MDNS.begin("esp8266")) { // Start the mDNS responder for esp8266.local (not sure what)
Serial.println("mDNS responder started");
} else {
Serial.println("Error setting up MDNS responder!");
}
server.on("/", HTTP_GET, handleRoot); // Call the 'handleRoot' function when a client requests URI "/"
server.onNotFound(handleNotFound); // When a client requests an unknown URI (i.e. something other than "/"), call function "handleNotFound"
server.begin(); // Actually start the server
Serial.println("HTTP server started");
}
void loop(void) {
if ((micros() - timer) >= 60 * 1000000) { // ESP has no RTC and micro's only lasts ~70 minutes
Pump_minutes = Pump_minutes + 1; //
Light_minutes = Light_minutes + 1; //
timer = micros();
}
server.handleClient(); // Listen for HTTP requests from clients
setLightStatus(); // Determine the status of the UV light
setPumpStatus(); // Determine the status of the nutrient pump
}
void handleRoot() { // When URI / is requested, capture the inputs and send the webpage
// Capture any available inputs
if (server.arg("newLightOn") != "") {
String ipt1 = server.arg("newLightOn");
LightCycle_on = ipt1.toInt();
LightCycletime = (((LightStatus + 1) % 2) * LightCycle_off) + (((LightStatus) % 2) * LightCycle_on);
}
if (server.arg("newLightOff") != "") {
String ipt2 = server.arg("newLightOff");
LightCycle_off = ipt2.toInt();
LightCycletime = (((LightStatus + 1) % 2) * LightCycle_off) + (((LightStatus) % 2) * LightCycle_on);
//Serial.println(String(LightCycle_off));
}
if (server.arg("newPumpOn") != "") {
String ipt3 = server.arg("newPumpOn");
PumpCycle_on = ipt3.toInt();
PumpCycletime = (((PumpStatus + 1) % 2) * PumpCycle_off) + (((PumpStatus) % 2) * PumpCycle_on);
//Serial.println(String(PumpCycle_on));
}
if (server.arg("newPumpOff") != "") {
String ipt4 = server.arg("newPumpOff");
PumpCycle_off = ipt4.toInt();
PumpCycletime = (((PumpStatus + 1) % 2) * PumpCycle_off) + (((PumpStatus) % 2) * PumpCycle_on);
//Serial.println(String(PumpCycle_off));
}
if (server.arg("HydroponicCtrl") != "") {
if (server.arg("HydroponicCtrl") == "manual") {
manualCtrl_active = 1;
String ipt5 = server.arg("LightSetMan");
LightManual = ipt5.toInt();
String ipt6 = server.arg("PumpSetMan");
PumpManual = ipt6.toInt();
//Serial.println(String(LightManual));
} else {
timer = micros();
manualCtrl_active = 0;
Pump_minutes = 0;
Light_minutes = 0;
LightCycletime = (((LightStatus +1) % 2) * LightCycle_off) + (((LightStatus) % 2) * LightCycle_on);
PumpCycletime = (((PumpStatus +1) % 2) * PumpCycle_off) + (((PumpStatus) % 2) * PumpCycle_on);
}
}
setLightStatus(); // Determine the status of the UV light
setPumpStatus(); // Determine the status of the nutrient pump
// Visualize the web page:
server.send(200, "text/html", "<div style="width: 50%; border:solid;"><h1>Hydroponic control settings</h1> <h2>Manual control settings</h2> <form action="/" method="GET"><label for="lightCtrl">Manually control the outputs?</label></br> <select name="HydroponicCtrl" id="HydroponicCtrl"> <option value="manual">Manual Control</option> <option value="automatic">Automatic Control</option></select></br> <select name="LightSetMan" id="LightSetMan"> <option value="1">Light ON</option> <option value="0">Light OFF</option></select> <select name="PumpSetMan" id="PumpSetMan"> <option value="1">Pump ON</option> <option value="0">Pump OFF</option></select></br> <input type="submit" value="Submit"> </select></form> <h2>Light control settings</h2> <form action="/" method="GET"> Light on time [min]: (currently: " + String(float(LightCycle_on) / 60) + " h)</br><input type="number" name="newLightOn"></br> Light off time [min]: (currently: " + String(float(LightCycle_off) / 60) + " h)</br><input type="number" name="newLightOff"></br><input type="submit" value="Submit"></form> <h2>Pump control settings</h2> <form action="/" method="GET"> Pump on time [min]: (currently: " + String(float(PumpCycle_on) / 60) + " h)</br><input type="number" name="newPumpOn"></br> Pump off time [min]: (currently: " + String(float(PumpCycle_off) / 60) + " h)</br><input type="number" name="newPumpOff"></br><input type="submit" value="Submit"></form></div> <div style="width: 50%; border:solid;"><h1>Hydroponic control states</h1><table style="width:60%; border:solid;"> <tr><td>Controller type:</td><td>"+ ControlType[manualCtrl_active] +"</td></tr> <tr><td>Light Status:</td><td>"+activeState[LightStatus]+"</td></tr> <tr><td>Cycletime remaining:</td><td>"+(LightCycletime - Light_minutes)+" min</td></tr> <tr><td>Pump Status:</td><td>"+activeState[PumpStatus]+"</td></tr> <tr><td>Cycletime remaining:</td><td>"+(PumpCycletime - Pump_minutes)+" min</td></tr> </table></div>");
}
void handleNotFound() {
server.send(404, "text/plain", "404: Not found"); // Send HTTP status 404 (Not Found) when there's no handler for the URI in the request
}
void setLightStatus() { // Determine and set the status of the Light
if (manualCtrl_active == 1) {
digitalWrite(LightOutputPin, LightManual);
LightStatus = LightManual;
}else {
if ((Light_minutes) >= (LightCycletime)) {
LightStatus = (LightStatus + 1) % 2;
digitalWrite(LightOutputPin, LightStatus);
Light_minutes = 0;
LightCycletime = (((LightStatus + 1) % 2) * LightCycle_off) + (((LightStatus) % 2) * LightCycle_on);
//Serial.println(String(Light_minutes));
}
}
}
void setPumpStatus() { // Determine and set the status of the pump
if (manualCtrl_active == 1) {
digitalWrite(PumpOutputPin, PumpManual);
PumpStatus = PumpManual;
}else if(LightStatus == 0){ // no pump when the lights are out
digitalWrite(PumpOutputPin, 0);
PumpStatus = 0;
}else{
if ((Pump_minutes) >= (PumpCycletime)) {
PumpStatus = (PumpStatus + 1) % 2;
digitalWrite(PumpOutputPin, PumpStatus);
Pump_minutes = 0;
PumpCycletime = (((PumpStatus + 1) % 2) * PumpCycle_off) + (((PumpStatus) % 2) * PumpCycle_on);
//Serial.println(String(Pump_minutes));
}
}
}
Project introduction | August 7th 2020 |
Setting up a setup | September 13th 2020 |
Move to the ESP | October 19th 2020 |