ESP32 Greenhouse Monitor

Collect temp/humidity, soil moisture, sunlight data to be stored on local web server

Public Chat
Similar projects worth following
My own version of what others have done... This project will be built around an ESP32 and a BME280, using a soil moisture probe and a common photoresistor.

The goal is to use solar power and deep-sleep mode to create a self-sufficient sensor cluster and monitor conditions in my greenhouse as Winter draws near. The collected data will be sent over WiFi to a web server and stored in a mySQL database. Using PHP libraries I hope to render graphical web pages showing the historical data.

Due to requiring AC power to operate devices such as heaters and lights, I am dropping my original plan to make this project solar-powered.  The new plan is to have a device which will always be on, sending periodic updates to a web server so that the greenhouse conditions can be logged and viewed online.  The code will support up to 16 devices controlled by a bank of relays and allow setting unique conditions to trigger each device.

The initial greenhouse sketch, including Adafruit driver for BME280, a custom font file, and the icons used on the display

Zip Archive - 17.82 kB - 04/02/2018 at 15:56


  • 1 × ESP32
  • 1 × BME280 Temperature, humidity, and barometric pressure sensor
  • 2 × Soil Hygrometer Reads the ground moisture
  • 2 × Photoresistor Detect daily sunlight
  • 1 × 20A relay board Control heaters, fans, water lines, etc.

View all 6 components

  • Fall is here again

    Jeff Taylor10/06/2018 at 06:31 0 comments

    The cold weather started moving in so I set up the greenhouse again.  I completely re-covered all the roof panels due to bad wind damage this Spring.  I'm trying out a different idea which has more plastic wrapping around the underside and fewer staples that can rip holes through the plastic when the wind whips up.  This also included an idea to close the gaps between the roof and top of the walls, which I'm happy to say was a great success.  I haven't felt any wind blowing inside the greenhouse now, so it won't suck away all my heat.

    I got the ESP32 set up again, but still had trouble with the relays not wanting to kick over.  This was especially bad the day the sun came back out and the greenhouse temps hit 120F but the fan never came on.  However I finally found the problem, and a temporary solution!  The ESP32 board I'm using has 3.3v and 5v pins around the edges, and I was using the 5V pins to feed the relay.  However it appears there is a regulator on the ESP that severely limits the current being fed through it, and thus starving the relay board of power.  As a temporary solution I plugged in a second 5V power pack and fed this straight to the relay board (keeping common grounds between both boards.  Success!  All of the relays have been reliably tripping for the last three days.

    To permanently resolve this problem, I have some small regulators that will take a 5V source and drop it to 3.3v @ 800mA.  I think I'll wire the 5V components directly from the power supply and then power the ESP from this regulator, unless I can figure out a way to also power the ESP from a direct 5V source.

    Today I've been trying to work out some code to rebuild the routine that interprets the math strings used for the relay rules.  There's a technique called the shunting-yard which converts a formula into Reverse Polish Notation.  From there it's supposed to be easy to work the formula.  I might just do the conversion server-side and let the ESP solve the RPN string.  I think that could cut out a serious chunk of processing time.

    I also added in the code to perform over-the-air sketch updates.  I tested it on the ESP on my desk and it works great, so I'll get it flashed to the greenhouse computer one of these days.  The next thing I want to tackle is being able to store variables (such as the device name and rules table) to the ESP's flash space.  Having the device name stored will allow me to have a single code image for all devices.  Having the rules stored means it can take off running as soon as it boots, even without a wifi connection to the server.  After a power outage or some other issue I want it to be able to kick the heaters or fans on immediately as needed, but new rules could still be updated from the web server.

    So that's about it for now.  I still need to work on getting the hardware into a box so everything is tidy.

  • A Quiet Summer

    Jeff Taylor08/10/2018 at 18:20 0 comments

    Haven't worked much on this project over the Summer, however I have been ordering parts to transfer the circuit onto a soldered breadboard and mount it in a case.  I picked up a 10m 3.5mm stereo extension cord and cut that in half to solder to the soil moisture probes.  The idea was to have them plug into standard sockets mounted in the case.  Unfortunately it appears that the Chinese sellers on ebay of what a "female stereo jack" actually means... the last two orders I received only have pins for MONO plugs!?!  Silly me for thinking that ebay sellers have any concept of the standard industry terminology.

    Beyond that, I have a waterproof case with a clear lid... and a nice casting cutoff point right in the middle of the clear lid.  Again, are any of these people paying attention?  Of course the sales pictures show a perfectly clear lid with no blemishes.  I think if I'm careful I can clean up the spot and use the dremel to polish the plastic out again.

    I have some circuit boards, various switches, a bunch of different 2.54mm headers and plugs, and headers to plug in the ESP32 module and allow it to still be replaceable.  I also got a power supply with a larger current output, hoping that will fix the problem with the relays not reliably switching.  Before I get everything put together I still need to update my code to allow OTA programming.

    In September I'll be re-covering my roof panels.  The way I wrapped the plastic around last time turned out to work very poorly and the Spring winds tore the plastic from several panels.  This time I'll cut the plastic much larger to allow for a lot more wrapping around the sides.  I also figured out that if I leave an extra 6 inches hanging from the bottom edges, I can use that to tuck inside the greenhouse framework and completely seal off the large gap I've always had between the roof and wall panels, eliminating a huge source of heat loss.  Maybe this year I'll be able to keep things going beyond December!  Thanks to other changes, the tomatoes and cucumbers are growing like mad this year so it would be great if they continue to produce for awhile.

    The greenhouse walls will go back up around the beginning of October, and with that I will be setting up my ESP monitor again (hopefully with all the new hardware and sealed in a case).

  • Code updates and web page

    Jeff Taylor04/26/2018 at 17:06 0 comments

    I finally finished the code updates to allow storing the rules on the web server.  Unfortunately I had no luck working with char* types so I ended up writing it using strings, but at least it works.  I have added a new variable to the code to name each device.  This is used for storing the data in the database, but can also be used to define custom rules for each device.  If there is no custom rule file for that device name then it falls back to a common rule file.  Each time a data packet is sent to the server it also checks the timestamp on the current rule file, and if that timestamp has changed then it will reload the new file.

    As an example of what the rules look like...

    // Heaters
    f>56 : R0=0
    f<54 : R0=1
    f>48 : R1=0
    f<46 : R1=1
    // Fan control
    (R0=1) | (R1=1) : R2=0
    (R0=0) & (R1=0) : R2=1
    // Lights
    (t<0600) | (t>2020) | (L1>90) : R3=0
    (t>=0600) & (t<=2020) & (L1<60) : R3=1

    Starting from the top, the first rule says that if the temperature in fahrenheit is greater than 56, turn off relay 0 (heater 1).  The second rule says that if the temp is less than 54, turn relay 0 on.  The fan controls turn the fan on if both heaters are off (my heaters also have fans in them) ensuring something is always running that keeps the air moving.  The rule conditions are fairly simple but they do have access to a number of variables including all of the sensors.  I would also like to add a math function for % which lets you check for incremental conditions.  For example (m%10) would let you turn a fan on and off every 10 ten minutes.  It would also be helpful to know the month, day, and day of the week.  I've read there is now a full time/date function available on the ESP32, so if I find that maybe I can pull out all of my own functions performing timekeeping and have access to full date lookups.  But all of that is just features in addition to what is already working.

    I have added a new image to the gallery showing the web page.  To build this I'm using PHP and pChart.  I still need to work on the labeling for the hour of the day, but this graph will show results from the past three days.  You can see a gap in the data from yesterday where the ESP32 did something odd... it stopped reading ALL of the sensors (no results at all from the BME280 and zero values for sunlight and moisture) however it continued to send out sensor data every minute.  This resulted in f being zero which turned on both heaters, however since it was still reading the rules file I was able to add more conditions to turn off the heaters and turn on the fan, preventing my plants from being roasted until I could get back home.  Whew!

    So obviously my watchdog code still isn't working quite right.  During yesterday's odd occurrence the OLED display locked up as well.  Since the watchdog timer is connected to updates to the display, I would have assumed the watchdog would cause the system to reset, but it didn't.  It is critical that I get the watchdog to do resets any time something fails, so I'll have to re-think my strategy.  Maybe I'll connect it to the BME and reset if it fails to read twice in a row...?

    Someone I work with is planning on building a greenhouse this Summer and has been curious about the project.  Except he doesn't have access to all the servers like I do.  I think at some point I'll build an image for a raspberry pi that contains the web server, mySQL, and PHP.  You would still have to set the SSID and key for wifi access, and set a static IP address, but I would think most folks that could build this project could also handle that bit with some easy instructions.

  • A watchdog would be nice

    Jeff Taylor04/12/2018 at 20:23 0 comments

    I made a quick update to the code last night to implement better rules for the lights and heaters.  Basically I added a range, similar to how a thermostat works.  So now instead of turning the heater on when the temp drops below 55 degrees, and back off again when the temp goes above 55, I added a 2-degree buffer range.  Now when the temp goes below 54 the heater comes on, and when the temp goes back above 56 the heater is shut off.  This way the heater will actually stay on for a few minutes at a time rather than performing a rapid on/off cycle right around the target temperatures.  I think I mentioned a similar problem with the light controls, where the lights would be turned on, then the light sensor detected it was bright inside the greenhouse and turned the lights back off again, resulting in a 10-second cycle period.

    The next step is implementing an actual rule system that can be changed on the fly.  I built some code for this but unfortunately it makes heavy usage of strings, which of course are very badly handled in the arduino environment.  I don't really know much about working with chars in C though, and the rule interpreter makes heavy use of substring to break apart the rules and handle parenthesis within the structure.  I'm going to try and finalize my version of the routine and get it working with live data, but hopefully someone else will come along that can rewrite the routines in a more standardized (and memory-friendly) way.

    Had my first lockup since putting the ESP32 in the greenhouse.  I knew it would happen eventually, so I started looking at how to implement a watchdog today.  It seems that most methods require compiling under IDF rather than the arduino IDE, which I have no experience with.  I did find a bug report and some sample methods that should work in the arduino IDE however, so I'll try adding in one of those tonight.

  • And we're up and running

    Jeff Taylor04/10/2018 at 22:18 0 comments

    I finished building out the electrical box and hooking everything up this weekend.  The unit is now in place in the greenhouse, and the real-world is presenting issues I hadn't thought about...

    • When you control the lights based on a light sensor, the lights coming on indicate there is plenty of light and the lights are turned back off.  Yeah that worked out well.  I did find that simply tipping the sensor over by 90 degrees allows it to read indirect sunlight without being affected by the electric lights.
    • The light sensor itself still needs adjustment.  It seems to max out before the sun is even overhead.  I think it might be worthwhile to have two sensors tuned differently, so one detects the general daylight and the other is more sensitive to low-light conditions.  Finding a sensor with an exponential rather than linear response might also be helpful.
    • Things spend a lot of time switching on and off.  I need to create a buffer to smooth out the information so that, for example, the heaters stay on for five minutes at a time instead of bouncing on and off for only a few seconds.
    • Code updates are now a major issue.  I had intended to set up the ESP for remote updates but never got around to it.  Still have to work on that.

    The data is being fed into a mySQL database now, but the current code is updating every 30 seconds.  That's way more data than I really need to store.  I wrote up a quick web page to display the last 24-hours of info and found it best to average out every 5 minutes worth of data to smooth out spikes.  That also made a huge difference in the time required to render the graphs... for each 5-minute period I now have a single value to plug in rather than 600.

    As  for the outlet box itself, that was as easy as expected.  I took a metal outlet box with a faceplate for four outlets, purchased one each of black and white outlets, and removed the tabs from one side to isolate each outlet.  I have two power cords, one black and one white, which feed power to the corresponding outlet, and then from all of this I feed eight wires from the outlets to the relays.  The socket colors allow me to keep track of the source of power for each device.  Since the heaters pull so much juice I have to run two extension cords to the greenhouse, and each one feeds one heater plus one other device.  It's not much of an issue right now because the heaters only run at 50% power, and only one of those stay on all night.

    So far so good... now I just have to work out the issues with the code...

  • The hardware is complete

    Jeff Taylor04/02/2018 at 15:32 0 comments

    Looks like it's been a couple months since my last update.  Too many simultaneous projects, however with the weather changing I thought I would try to get some plants going in the greenhouse despite the overnight lows being around freezing temps.  If only I had temperature controlled heaters in the greenhouse... :)

    I had to transfer the ESP32 to a larger breadboard to accommodate the 74HC595 chip and then check over the wiring.  The 20A relay board I got has a jumper on it to select if you wish to use active-low or active-high inputs -- in my case I chose active-high so that the default state at power-on will be for all the relays to be off.  I had to make some minor changes to the code including setting the pin numbers I'm using to talk to the 595, but then all the relays lit up.  So at this point it appears the hardware is functioning just fine.  Previously I had just randomized the changing of the relay status to show the outputs, but I wiped out that code and put in some requirements to trigger each relay (as a precursor to how I want to set up the interface for defining the relay settings).  For sitting at my computer desk the settings were fairly simple.  I plan to use two heaters, a fan, and a switch for the lights.  I coded the first heater to come on if the temp drops below 75F, and the second comes on when the temp drops below 72F (in the greenhouse I will probably use temperatures of 55F and 45F).  The fan is currently on all the time, but to save on electricity I thought I would cut that down to 50%, so I added a line to switch the fan on and off every 10 ten minutes.  Finally I programmed the light switch to be on between 6am-7pm, but only if the light sensor is below 25%.  This set of settings allowed the relays to change during the day and everything seems to be responding as expected.

    So the software is functional now, although not really complete.  I also added some code to include the relay statuses in the data sent to the server.  I think my next software focus will be on the server side, to actually collect and record the data being sent from the ESP32.  I have also noticed periodic lockups of the software so I'm thinking perhaps having the ESP reboot itself once per day, preferably in the late afternoon so if the reboot fails then I can reset it manually when I get home from work.

    The last step on the hardware side is the electrical outlets to plug in my actual devices.  I have seen metal junction boxes that will hold four outlets (to match my four relays).  I have two extension cords going to the greenhouse so I will wire it so that each pair of outlets plugs into one of the cords, however each socket will be individually controlled by a different relay.  I was trying to find cheap power cords online that I could use for plugging in to the cords, then realized I already have a source for that -- I have a ton of computer power cords laying around that should work fine.  They seem to only be 18AWG, although I would have preferred 16AWG, but since I'm splitting up my power requirements between two extension cords I think these will be fine.

    I will try to get the code and some schematics posted soon, in case others are interested in trying this out for themselves...

  • Relay control

    Jeff Taylor01/30/2018 at 04:00 0 comments

    A quick update tonight...  I've been looking at chips to handle the relay operations without the need for using 1 pin for every relay.  I thought something like a 74LS259, a 3-to-8 decoder with latching outputs, would be the way to go but adding more lines appears to be less than ideal.  I kept finding references to a 74HC595 and finally looked into it.  The chip takes a serial input using only three I/O lines, and there is already an arduino driver available to operate this chip.  Even better, you can add multiple chips together and they still only require the same three I/O lines to talk to all of them.  Now being a serial operation means that talking to these chips will be slower than other options, but we're talking about relays that will see cycle times in minutes or hours, so speed is not really a priority here.  I have a bag of these chips on order so once they come in I'll see how they perform at 3.3v.

    In the meantime I will start setting up the code changes.  I'm going to add a variable to hold the number of relays in use, then I just need to create a bit register to push out the on/off state of all relays each time there is an update and pad it out to an even multiplier of 8 bits (one set for each 595 that is being used).  Of course the real problem will be figuring out how to display a virtually unlimited number of relays on this screen.  I might have to write some code to vertically scroll that section of the display.

    If anyone is curious, there is a nice article here that describes the usage of this chip with a regular arduino:

  • Running out of space

    Jeff Taylor01/28/2018 at 19:59 0 comments

    This display is so tiny that it takes a lot of work to arrange all the required pieces and still be able to tell what they are meant to represent.  I just uploaded a new image with what I think will be the final layout.  Most noticeable from the picture are the relay icons, which now show all 16 potential slots.  To fit these I had to compress the icons to 6x7 pixels, and I still can't create an image for lights that I like.  I have added a boolean array to keep track of the status of the relays, and have the icons blinking when the relay is turned on.  Another bit you can't see from the picture is that the bottom row of text now scrolls to the left.  I print two overlapping text fields to make the scrolling marquee continuous.

    I'm still debating the idea of allowing this unit to try and auto-connect to any open wifi access points.  Realistically the device would not move around much (if at all) so it should only connect to a known SSID.  I added some code to re-check the connection and reconnect as needed every 30 seconds to make sure we stay online.  I could also create a rotating list of SSIDs and passwords here if I wanted to try connecting to several known access points.

    There is now a working clock displayed on the bottom status bar.  When it sends data to the web server, I reply with a timestamp and time zone, and the ESP32 keeps track of the offset between real time and the internal millis() time.  I added a function time() which returns the actual calculated unix timestamp and it seems to stay fairly accurate.  I dropped the wifi signal overnight and the displayed time still appeared to be accurate in the morning.  The time is updated as the status bar scrolls so you can see it updating each time it passes across the display.

    I've been working a bit on how to represent the triggers for each relay.  I think I can provide for all the possibilities with a 16-character string, but unfortunately arduino doesn't really handle string arrays.  I'll probably have to keep all the triggers in a single string, but if they are always the same character length then it is easy to read one trigger at a time.  Besides the current state of each sensor I also want to maintain an average and a trend for each.  The averages would be over the past hour and would be more reliable that the current value for some items such as the amount of sunlight.  The trends would represent rising, falling, or steady values and again would be calculated over readings from the past hour.  I'm also thinking it would be better to average out each reading over a 5-minute period, in between each time the data is sent to the server, to ensure a more accurate reading.

    Which brings me to another point... the idea of running this device from solar power.  It occurred to me that having a solar-powered device and a device that runs a bank of relays is mutually exclusive -- you can have one or the other, and if you are triggering AC-powered devices then you probably don't care to run the ESP32 from solar power.  However I still want the ability to collect data from other locations using solar.  So while I am currently focusing on a powered device running continuously and able to control relays, I will also develop a second device that is just the bare ESP32 without a display, which still has the same sensor inputs but uses a low-power mode in between readings and only powers up wifi as needed.  This second device then should be able to run from solar power and a battery to meet the originally-stated goals.

  • First pic

    Jeff Taylor01/25/2018 at 16:20 0 comments

    The display is nearing its final version, I believe, and I thought it would be a good time to grab a picture of it.  At the top-left is the temperature in Fahrenheit (also easily displayed in Celsius).  Directly below that is the current humidity.  To the right the four bars show the wifi strength, sunlight strength, and two bars for soil moisture inputs.  Underneath that are icons for heaters, and a fan, and a water valve (in operation these would blink while each device is turned on).  The bottom line is reserved for text showing such things as the current barometric pressure and the device's IP address obtained from the wifi network.

    It turns out that creating the bitmap for images which are greater than 8 bits wide is rather a pain in the...  The larger icons are actually 12x12, and after some experimenting I figured out that I have to take the last 8 bits first, and then the first four bits after that.  So if your bitmap for a line was 000011111111 then the actual line of code to hold it would be: B11111111, B0000.  Fun, eh?

    So at this point the hardware connections have been mostly worked out, it's just a matter of fine-tuning the divider resistor for the light sensor and possibly the soil moisture sensors.  There are enough pins exposed on this board to handle three analog inputs and eight relay outputs, however I would rather add in a TTL latch so I could control more relays and still have I/O pins left over on the ESP32 for more inputs.  An example here would be a 74LS259, which only requires a 3-bit input plus an enable line and would also control eight relays.  If I can find a similar chip with a 4-bit input then we could control 16 relays with only five I/O lines from the ESP32.

    Within the code, the next step is enabling it to try and auto-connect to any available open wifi and if that fails, set itself up as an access point so you could connect to it directly from a phone or tablet and use its web page to set up the proper wifi credentials.  As for the web page itself, I've had some ideas there...  I would like to be able to associate each relay with a set of conditions.  For example, turn on relay one (heater) if the temp drops below 50F.  And if soil moisture sensor 1 drops below 20% then turn on the water for 5 minutes, but don't allow it to come back on again for another 55 minutes.  Basically use the web page to set up multiple conditions for each relay based on the sensors we have available to try and regulate the greenhouse conditions.  The worst problem will be in keeping the web page small enough that it still fits in the ESP32's memory.

  • Analog grumblings

    Jeff Taylor01/24/2018 at 15:57 0 comments

    I spent most of my evening trying to get an analog input working on the ESP32.  The code is quite different from my previous experience with an ESP8266 in that the analogRead() command is no longer present.  On the new processor you set up the port bit width and attenuation first, then read the port with adc1_get_raw(pin).  Seems simple enough once you figure it out, but I had two things working against.  The first was that in searching through Google results, EVERY single page I found where someone was presenting their example of how to use an analog port on the ESP32 (and they all even showed pictures of their ESP32 on a breadboard!) had code that still used the analogRead command.  Yeah thanks guys, you obviously didn't actually test what you are presenting!  Which brings me to the second problem...

    After watching the temperature values yesterday I realized that even pulling data once every 20 seconds was still causing some heating in the BME280.  I know, I know, the manufacturer recommends once every 60 seconds, but it's hard to remain patient when you're testing code.  However since the temp functions and display are working well, I went ahead and shifted the data read to once every 60 seconds, which brought the measured temps down to almost exactly what my in-house digital thermometer was reading (not that I have much faith in THAT device's accuracy).  And then I forgot I had made this change...  While playing with the photoresistor I naturally dropped the code for taking measurements into the same function where I was reading the BME280 from, and then wondered why I wasn't getting rapid readings on the display.  It finally hit me this morning what the problem was, so I made a quick change in the code, and then was able to swap out resistors until I found something that seems to work well with the photoresistor I have.

    Another big difference in the analog ports on the ESP32 is that instead of accepting Vcc as the 100% level, these only require 1.1V to max out.  That required quite a shift in the resistor divider, so instead of using the common 10k resistor I ended up with a 220ohm.  Sitting here under room lights I am reading about 42%, but if I shine a light on the photoresistor it maxes out at 100%.  And I believe it is still sensitive enough to detect bright moonlight.

    I also ordered a 4-gang relay board yesterday.  They all claim to be 5v devices, however each port couples with an opto-isolator which means it really only requires a digital signal strong enough to trip the isolator (although the board itself still requires a 5V source to latch the relays).  I'm betting I will be able to trigger the relays reliably with a 3.3v signal, but if not I can just add some cheap transistors to switch the 5v source.

View all 12 project logs

Enjoy this project?



Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates