{"id":322,"date":"2023-01-15T16:32:14","date_gmt":"2023-01-15T16:32:14","guid":{"rendered":"https:\/\/trewest.dev\/blog\/?p=322"},"modified":"2023-12-05T09:34:34","modified_gmt":"2023-12-05T09:34:34","slug":"project-esp8266-plant-watering","status":"publish","type":"post","link":"https:\/\/trewest.dev\/blog\/index.php\/2023\/01\/15\/project-esp8266-plant-watering\/","title":{"rendered":"Project: ESP8266 Plant Watering"},"content":{"rendered":"<div class=\"wp-post-series-box series-project-overviews wp-post-series-box--expandable\">\n\t\t\t<input id=\"collapsible-series-project-overviews69db37af8ce84\" class=\"wp-post-series-box__toggle_checkbox\" type=\"checkbox\">\n\t\n\t<label\n\t\tclass=\"wp-post-series-box__label\"\n\t\t\t\t\tfor=\"collapsible-series-project-overviews69db37af8ce84\"\n\t\t\ttabindex=\"0\"\n\t\t\t\t>\n\t\t<p class=\"wp-post-series-box__name wp-post-series-name\">\n\t\t\tThis is post 15 of 21 in the series <em>&ldquo;Project Overviews&rdquo;<\/em>\t\t<\/p>\n\t\t\t\t\t<div class=\"wp-post-series-box__description wp-post-series-description\">\n\t\t\t\t<p>A series of posts detailing and highlighting projects that I&#8217;ve made.<\/p>\n\t\t\t<\/div>\n\t\t\t<\/label>\n\n\t\t\t<div class=\"wp-post-series-box__posts\">\n\t\t\t<ol>\n\t\t\t\t\t\t\t\t\t<li><a href=\"https:\/\/trewest.dev\/blog\/index.php\/2023\/01\/10\/project-bugtrakt\/\">Project: Bugtrakt<\/a><\/li>\n\t\t\t\t\t\t\t\t\t<li><a href=\"https:\/\/trewest.dev\/blog\/index.php\/2023\/01\/10\/project-imgalib\/\">Project: Imgalib<\/a><\/li>\n\t\t\t\t\t\t\t\t\t<li><a href=\"https:\/\/trewest.dev\/blog\/index.php\/2023\/01\/10\/project-go-fish\/\">Project: Go Fish!<\/a><\/li>\n\t\t\t\t\t\t\t\t\t<li><a href=\"https:\/\/trewest.dev\/blog\/index.php\/2023\/01\/11\/project-trewest-dev\/\">Project: trewest.dev<\/a><\/li>\n\t\t\t\t\t\t\t\t\t<li><a href=\"https:\/\/trewest.dev\/blog\/index.php\/2023\/01\/11\/arduino-light-effects\/\">Arduino Light effects<\/a><\/li>\n\t\t\t\t\t\t\t\t\t<li><a href=\"https:\/\/trewest.dev\/blog\/index.php\/2023\/01\/11\/project-mini-stacker\/\">Project: Mini Stacker<\/a><\/li>\n\t\t\t\t\t\t\t\t\t<li><a href=\"https:\/\/trewest.dev\/blog\/index.php\/2023\/01\/12\/project-arduino-nes-controller\/\">Project: Arduino NES Controller<\/a><\/li>\n\t\t\t\t\t\t\t\t\t<li><a href=\"https:\/\/trewest.dev\/blog\/index.php\/2023\/01\/12\/project-metroidvania-like\/\">Project: Metroidvania-Like<\/a><\/li>\n\t\t\t\t\t\t\t\t\t<li><a href=\"https:\/\/trewest.dev\/blog\/index.php\/2023\/01\/12\/project-susminer\/\">Project: Susminer<\/a><\/li>\n\t\t\t\t\t\t\t\t\t<li><a href=\"https:\/\/trewest.dev\/blog\/index.php\/2023\/01\/12\/project-farming-demo\/\">Project: Farming Demo<\/a><\/li>\n\t\t\t\t\t\t\t\t\t<li><a href=\"https:\/\/trewest.dev\/blog\/index.php\/2023\/01\/13\/project-vrc-basement\/\">Project: VRC Basement<\/a><\/li>\n\t\t\t\t\t\t\t\t\t<li><a href=\"https:\/\/trewest.dev\/blog\/index.php\/2023\/01\/13\/project-hash-clear\/\">Project: Hash Clear<\/a><\/li>\n\t\t\t\t\t\t\t\t\t<li><a href=\"https:\/\/trewest.dev\/blog\/index.php\/2023\/01\/13\/project-titan-help\/\">Project: Titan Help<\/a><\/li>\n\t\t\t\t\t\t\t\t\t<li><a href=\"https:\/\/trewest.dev\/blog\/index.php\/2023\/01\/13\/project-computer-craft-scripts\/\">Project: Computer Craft Scripts<\/a><\/li>\n\t\t\t\t\t\t\t\t\t<li><span class=\"wp-post-series-box__current\">Project: ESP8266 Plant Watering<\/span><\/li>\n\t\t\t\t\t\t\t\t\t<li><a href=\"https:\/\/trewest.dev\/blog\/index.php\/2023\/01\/15\/project-golf-demo\/\">Project: Golf Demo<\/a><\/li>\n\t\t\t\t\t\t\t\t\t<li><a href=\"https:\/\/trewest.dev\/blog\/index.php\/2023\/01\/15\/project-ghost-holic\/\">Project: Ghost Holic<\/a><\/li>\n\t\t\t\t\t\t\t\t\t<li><a href=\"https:\/\/trewest.dev\/blog\/index.php\/2023\/03\/27\/project-multi-chat-viewer\/\">Project: Multi-Chat Viewer<\/a><\/li>\n\t\t\t\t\t\t\t\t\t<li><a href=\"https:\/\/trewest.dev\/blog\/index.php\/2024\/08\/22\/project-interview-simulator\/\">Project: Interview Simulator<\/a><\/li>\n\t\t\t\t\t\t\t\t\t<li><a href=\"https:\/\/trewest.dev\/blog\/index.php\/2024\/10\/18\/project-healers-journey\/\">Project: Healer&#8217;s Journey<\/a><\/li>\n\t\t\t\t\t\t\t\t\t<li><a href=\"https:\/\/trewest.dev\/blog\/index.php\/2024\/10\/18\/project-biota\/\">Project: Biota<\/a><\/li>\n\t\t\t\t\t\t\t<\/ol>\n\t\t<\/div>\n\t<\/div>\n\n<p>Status: <mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-luminous-vivid-amber-color\">Untested<\/mark><br>Type: Microcontroller<br>Technology: ESP8266, Arduino, PlatformIO<\/p>\n\n\n\n<div class=\"wp-block-buttons is-layout-flex wp-block-buttons-is-layout-flex\">\n<div class=\"wp-block-button\"><a class=\"wp-block-button__link wp-element-button\" href=\"https:\/\/trewest.dev\">Back to Portfolio<\/a><\/div>\n<\/div>\n\n\n\n<p>After I had become somewhat comfortable with programming for Microcontrollers using Arduino\/PlatformIO I decided that I&#8217;d make something that&#8217;d be a little practical, something that&#8217;d water some plants for me whenever the soil was too dry.  Thankfully there&#8217;s kits for this already made with some simple code!  Using relays to power on and off water pumps and taking in data from soil moisture meters makes it a very simple affair.  But that&#8217;s no fun for a programmer, so I figured I&#8217;d change up the formula.<br>This caused me to utilize pumps to manage water PH (Up and Down) to keep it as neutral as possible, while also being able to mix in some nutrients as needed, making it usable for Hydroponics which was a field of interest to me at the time.  And then of course there&#8217;s still a pump that feeds water to everything as needed.  Additionally, I had water level sensors that could make sure we&#8217;re not running pumps dry and accidentally killing them.  But since this is a IOT board I of course had to try and make it capable of internet connection and remote monitoring, which I did do some things for but since I didn&#8217;t get to the stage of deploying the device and code I never really expanded beyond toggling pumps on and off, and never made a decent web interface for it.  Ideally I would&#8217;ve made it use websockets to connect to a local server of some kind that&#8217;d store historical data, but again I didn&#8217;t get to the refinement stage.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"485\" height=\"1024\" src=\"https:\/\/trewest.dev\/blog\/wp-content\/uploads\/2023\/01\/20220327_103040-485x1024.jpg\" alt=\"\" class=\"wp-image-323\" srcset=\"https:\/\/trewest.dev\/blog\/wp-content\/uploads\/2023\/01\/20220327_103040-485x1024.jpg 485w, https:\/\/trewest.dev\/blog\/wp-content\/uploads\/2023\/01\/20220327_103040-142x300.jpg 142w, https:\/\/trewest.dev\/blog\/wp-content\/uploads\/2023\/01\/20220327_103040-768x1623.jpg 768w, https:\/\/trewest.dev\/blog\/wp-content\/uploads\/2023\/01\/20220327_103040-727x1536.jpg 727w, https:\/\/trewest.dev\/blog\/wp-content\/uploads\/2023\/01\/20220327_103040-969x2048.jpg 969w, https:\/\/trewest.dev\/blog\/wp-content\/uploads\/2023\/01\/20220327_103040-scaled.jpg 1211w\" sizes=\"auto, (max-width: 485px) 100vw, 485px\" \/><figcaption class=\"wp-element-caption\">A plant of mine<\/figcaption><\/figure>\n\n\n\n<p>So, why didn&#8217;t I test and use this code in the end?  Well there&#8217;s a few things.  It was getting costly for one and investing more into was difficult to justify.  Secondly my garden had a lot of troubles such as my pets frequently messing with the plants, so adding fragile computers to the mix of it didn&#8217;t seem like a great idea.  Third it was somewhat unnecessary as Florida is a very relaxed climate for plants, we have enough rainwater that plants don&#8217;t need too much help.  And fourth I was starting to run into issues with the board I was using losing too much power running everything, I&#8217;m not trained in electrical engineering and stuff like this is just a hobby for me, but I&#8217;m guessing running everything was just drawing too many amps from the microcontroller, which would cause it to reset.  And while I could simplify it down some more and hope it&#8217;d be fine, I&#8217;d rather not and just try and make some more cool stuff with the things I have than risk burning something out.  <\/p>\n\n\n\n<figure class=\"wp-block-video\"><video height=\"360\" style=\"aspect-ratio: 480 \/ 360;\" width=\"480\" controls src=\"https:\/\/trewest.dev\/blog\/wp-content\/uploads\/2023\/01\/20220103-121641-1.m4v\"><\/video><figcaption class=\"wp-element-caption\">Early test video<\/figcaption><\/figure>\n\n\n\n<pre class=\"wp-block-code\"><code>#include &lt;Arduino.h>\n#include &lt;ESPAsyncTCP.h>\n#include &lt;ESPAsyncWebServer.h>\n#include &lt;Wire.h> \n#include \"SSD1306Wire.h\"\n\nSSD1306Wire display(0x3c, SDA, SCL);\n\nconst char ssid&#91;] = \"\";\nconst char password &#91;] = \"\";\nbool isMyFlipped;\nlong long prevMili;\n\nconst char webpage &#91;] PROGMEM = R\"=====(\n&lt;html>\n  &lt;head>\n    &lt;title>ESP12E Simple Web Page&lt;\/title>\n  &lt;\/head>\n  &lt;body>\n    &lt;p>Hello from ESP12E Module!&lt;\/p>\n  &lt;\/body>\n&lt;\/html>\n)=====\";\n\n#define LOGO16_GLCD_HEIGHT 16 \n#define LOGO16_GLCD_WIDTH  16 \n\n#define relayPin D5\n#define phUpPin D6\n#define phDownPin D7\n#define nutrientPin D8\n\n#define _wl_low D3\n#define _wl_high D4\n\nbool isPumpOn = false;\nbool isPHUpOn = false;\nbool isPHDownOn = false;\nbool isNutrientOn = false;\n\nbool wLevelLow = false;\nbool wLevelHigh = false;\n\nbool watering = false;\n\nshort waterState = 0; \/\/ water low\n\nshort forceTime = 0;\n#define forceDelay 30000\nString IPaddress;\n\nstatic const unsigned char PROGMEM logo16_glcd_bmp&#91;] =\n{ B00000000, B11000000,\n  B00000001, B11000000,\n  B00000001, B11000000,\n  B00000011, B11100000,\n  B11110011, B11100000,\n  B11111110, B11111000,\n  B01111110, B11111111,\n  B00110011, B10011111,\n  B00011111, B11111100,\n  B00001101, B01110000,\n  B00011011, B10100000,\n  B00111111, B11100000,\n  B00111111, B11110000,\n  B01111100, B11110000,\n  B01110000, B01110000,\n  B00000000, B00110000 };\n\n\/\/const char lol &#91;] = \"{ a:10, b:20, c:30}\";\n\nAsyncWebServer server(80);\n\/*\nvoid onRequest(AsyncWebServerRequest *request) \n{\n  request->send(404);\n}*\/\n\n\nvoid setup() {\n  \/\/ put your setup code here, to run once:\n  Serial.begin(115200);\n\n  Serial.println(\"trying to start the display\") ;\n\n  display.init();\n\n  \/\/display.flipScreenVertically();\n  display.clear();\n\n  display.setFont(ArialMT_Plain_10);\n  display.setTextAlignment(TEXT_ALIGN_RIGHT);\n  display.flipScreenVertically();\n  display.drawString(128, 54, String(millis()));\n  display.drawString(128, 42, \"Initializing!\" );\n\n  display.displayOn();\n  display.display();\n  \n\n  pinMode(relayPin,OUTPUT);\n  pinMode(phUpPin,OUTPUT);\n  pinMode(phDownPin,OUTPUT);\n  pinMode(nutrientPin,OUTPUT);\n\n  pinMode(13, OUTPUT);\n\n  digitalWrite(13, HIGH);\n\n  pinMode (_wl_low,INPUT);\n  pinMode(_wl_high,INPUT);\n\n  Serial.printf(\"Connecting to %s\\n\",ssid);\n\n  WiFi.mode(WIFI_STA);\n  WiFi.begin(ssid,password);\n\n  if (WiFi.waitForConnectResult() != WL_CONNECTED) \n  {\n    Serial.println(\"WiFi failed to connect.\");\n    delay(1000);\n    ESP.restart();\n  }\n  Serial.printf(\"Connection successful\\n\");\n  Serial.printf(\"Web Server IP address: %s \\n\", WiFi.localIP().toString().c_str());\n\n  \/\/String myip = \"http:\/\/\" + WiFi.localIP().toString();\n  IPaddress = WiFi.localIP().toString();\n\n  server.on(\"\/index.json\", HTTP_GET, &#91;] (AsyncWebServerRequest *request) {\n    char lol &#91;256];\n    sprintf(lol, \"Uptime: %ld milliseconds\", millis());\n\n    request->send_P(200, \"text\/plain\", lol);\n  }\n  );\n\n  server.on(\"\/\", HTTP_GET, &#91;] (AsyncWebServerRequest *request) {\n    request->redirect(\"\/index.json\");\n    Serial.printf(\"%ld: Redirecting\\n\",millis() );\n    \/\/request->send_P(200, \"text\/html\", webpage);\n  }\n  );\n\n  isMyFlipped = false;\n\n  server.on(\"\/forcePump\", HTTP_GET, &#91;] (AsyncWebServerRequest *request) {\n    \/\/request->redirect(\"\/index.json\");\n    Serial.printf(\"%ld: Forcing Pump\\n\",millis() );\n    isPumpOn = !isPumpOn;\n    String response = \"\";\n    if (isPumpOn) \n    {\n      response = \"Forcefully turned the pump on\";\n    }\n    else \n    {\n      response = \"Forcefully turned the pump off\";\n    }\n    forceTime = forceDelay;\n    request->send_P(200, \"text\/html\", response.c_str());\n  }\n  );\n\n  server.on(\"\/forceNutrient\", HTTP_GET, &#91;] (AsyncWebServerRequest *request) {\n    \/\/request->redirect(\"\/index.json\");\n    Serial.printf(\"%ld: Forcing Nutrient Pump\\n\",millis() );\n    isNutrientOn = !isNutrientOn;\n    String response = \"\";\n    if (isNutrientOn) \n    {\n      response = \"Forcefully turned the Nutrient pump on\";\n    }\n    else \n    {\n      response = \"Forcefully turned the Nutrient pump off\";\n    }\n    forceTime = forceDelay;\n    request->send_P(200, \"text\/html\", response.c_str());\n  }\n  );\n\n  server.on(\"\/forcePHUp\", HTTP_GET, &#91;] (AsyncWebServerRequest *request) {\n    \/\/request->redirect(\"\/index.json\");\n    Serial.printf(\"%ld: Forcing PH Up Pump\\n\",millis() );\n    isPHUpOn = !isPHUpOn;\n    String response = \"\";\n    if (isPHUpOn) \n    {\n      response = \"Forcefully turned the PH Up pump on\";\n    }\n    else \n    {\n      response = \"Forcefully turned the PH Up pump off\";\n    }\n    forceTime = forceDelay;\n    request->send_P(200, \"text\/html\", response.c_str());\n  }\n  );\n  server.on(\"\/forcePHDown\", HTTP_GET, &#91;] (AsyncWebServerRequest *request) {\n    \/\/request->redirect(\"\/index.json\");\n    Serial.printf(\"%ld: Forcing PH Down Pump\\n\",millis() );\n    isPHDownOn = !isPHDownOn;\n    String response = \"\";\n    if (isPHDownOn) \n    {\n      response = \"Forcefully turned the PH Down pump on\";\n    }\n    else \n    {\n      response = \"Forcefully turned the PH Down pump off\";\n    }\n    forceTime = forceDelay;\n    request->send_P(200, \"text\/html\", response.c_str());\n  }\n  );\n  \n  server.begin();\n}\nvoid drawScr1(int dt);\nvoid loop() {\n  \/\/Delta time is the time between loops happening, a somewhat important value for keeping time.\n  int deltaTime = millis() -  prevMili;\n  prevMili = millis();\n  drawScr1(deltaTime);\n  \n\n\n  if (forceTime > 0) \n  {\n    forceTime -= deltaTime;\n  }\n  else \n  {\n    \/\/ here we'll check if the state has changed\n    isPumpOn = watering;\n  }\n\n  if (isPumpOn) \n  {\n    digitalWrite(relayPin, LOW);\n  }\n  else \n  {\n    digitalWrite(relayPin, HIGH);\n  }\n\n  if (isNutrientOn) \n  {\n    digitalWrite(nutrientPin, LOW);\n  }\n  else \n  {\n    digitalWrite(nutrientPin, HIGH);\n  }\n\n  if (isPHUpOn) \n  {\n    digitalWrite(phUpPin, LOW);\n  }\n  else \n  {\n    digitalWrite(phUpPin, HIGH);\n  }\n\n  if (isPHDownOn) \n  {\n    digitalWrite(phDownPin, LOW);\n  }\n  else \n  {\n    digitalWrite(phDownPin, HIGH);\n  }\n\n  if (digitalRead(_wl_low) == LOW ){\n  wLevelLow = true;\n  Serial.println(\"Low Switch On\");\n  }else {\n    wLevelLow = false;\n  }\n\nif (digitalRead(_wl_high) == LOW) {\n  wLevelHigh = true;\n  Serial.println(\"High Switch On\");\n  } else {\n    wLevelHigh = false;\n  }\n\nif (wLevelHigh &amp;&amp; !wLevelLow) { \/\/ High\n  waterState = 2;\n  \/\/isPumpOn = true;\n  watering = true;\n  Serial.println(\"WlH\");\n} else if (!wLevelHigh &amp;&amp; wLevelLow) \/\/ Low \n{\n  waterState = 0;\n  watering = false;\n  \/\/isPumpOn = false;\n  Serial.println(\"WL Low\");\n} else if (wLevelLow == false &amp;&amp; wLevelHigh == false) \n{\n  waterState = 1; \/\/ Water is between high and low\n  \n  \/\/isPumpOn = false;\n  Serial.println(\"WL Medium\");\n} else if (wLevelLow == true &amp;&amp; wLevelHigh == true) \n{\n  waterState = 3; \/\/ Water is between high and low\n  \/\/isPumpOn = false;\n  watering = false;\n  Serial.println(\"Fucked\");\n}\n\n}\n\nvoid drawScr1(int dt) \/\/Draw Screen One\n{\n  display.clear();\n  display.setFont(ArialMT_Plain_10);\n  display.setTextAlignment(TEXT_ALIGN_RIGHT);\n  display.drawString(128, 54, String(millis()));\n\n  if (isPumpOn) \n  {\n    display.drawString(128, 42, \"W.Pump:on\");\n  }\n  else \n  {\n    display.drawString(128, 42, \"W.Pump:off\");\n  }\n\n  if (isPHUpOn) \n  {\n    display.drawString(128, 30, \"PH U. Pump:on\");\n  }\n  else \n  {\n    display.drawString(128, 30, \"PH U. Pump:off\");\n  }\n\n  if (isPHDownOn) \n  {\n    display.drawString(128, 18, \"PH D. Pump:on\");\n  }\n  else \n  {\n    display.drawString(128, 18, \"PH D. Pump:off\");\n  }\n  display.setTextAlignment(TEXT_ALIGN_LEFT);\n\n  display.drawString(0, 54, \"IP:\" + String(IPaddress) );\n\n  if (isNutrientOn) \n  {\n    display.drawString(0, 42, \"N. Pump:on\");\n  }\n  else \n  {\n    display.drawString(0, 42, \"N. Pump:off\");\n  }\n  display.drawString(0, 30, \"W.S: \" + String(waterState));\n\n  display.displayOn();\n  display.display();\n}<\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"<p>fter I had become somewhat comfortable with programming for Microcontrollers using Arduino\/PlatformIO I decided that I&#8217;d make something that&#8217;d be a little practical, something that&#8217;d water some plants for me whenever the soil was too dry!<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[48,45,4,8],"tags":[26,16,15],"post_series":[53],"class_list":["post-322","post","type-post","status-publish","format-standard","hentry","category-arduino","category-c-coding","category-coding","category-project","tag-arduino","tag-c","tag-project","post_series-project-overviews"],"_links":{"self":[{"href":"https:\/\/trewest.dev\/blog\/index.php\/wp-json\/wp\/v2\/posts\/322","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/trewest.dev\/blog\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/trewest.dev\/blog\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/trewest.dev\/blog\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/trewest.dev\/blog\/index.php\/wp-json\/wp\/v2\/comments?post=322"}],"version-history":[{"count":1,"href":"https:\/\/trewest.dev\/blog\/index.php\/wp-json\/wp\/v2\/posts\/322\/revisions"}],"predecessor-version":[{"id":325,"href":"https:\/\/trewest.dev\/blog\/index.php\/wp-json\/wp\/v2\/posts\/322\/revisions\/325"}],"wp:attachment":[{"href":"https:\/\/trewest.dev\/blog\/index.php\/wp-json\/wp\/v2\/media?parent=322"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/trewest.dev\/blog\/index.php\/wp-json\/wp\/v2\/categories?post=322"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/trewest.dev\/blog\/index.php\/wp-json\/wp\/v2\/tags?post=322"},{"taxonomy":"post_series","embeddable":true,"href":"https:\/\/trewest.dev\/blog\/index.php\/wp-json\/wp\/v2\/post_series?post=322"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}