Introduction: How to Make a Self-Watering IoT Planter With a Mason Jar and an ESP32!

About: Hi! I'm Geoff! I'm an electrical engineer with a passion for making things. I believe that creating is for everyone, and I love being able to share what I've learned to enable others to pursue their passions. …


On one of my weekly pilgrimages to Lowes (as any devout Kansan does), I found a sad little plant in the Gardening Department: a Janet Craig Compacta. The poor little guy looked lonely and dehydrated. We clicked right away and immediately I knew I had to take him under my wing. There was one major problem.

I *suck* at gardening. I am a disgrace to my home state’s agricultural prestige and heritage — I frequently forget to water the plants in my care, countless dying from my neglectful hand. I knew I had to save this little guy, but I could not lead him to the same demise. I was determined to atone for my past sins.

With my engineering skills and technical knowledge, I could create a solution that could keep my plants alive and redeem myself as a Kansan while wholeheartedly indulging my habit of short-term memory loss.

Determined, I brought home that plant. He was overjoyed to be chosen (I could tell). I named him Plonty, short for Plonty Mython, and I quickly got to work on designing his brand new smart home, an IoT Mason Jar Planter.

This is how I made it.

This Instructable was sponsored by the Urban Farming Guys, in association with the Green Thumb Project. Huge thanks to these guys for helping me fund this tutorial, aiding in an effort to encourage and educate about more intelligent gardening.

Step 1: Gathering the Tools and Materials

Step 1a: Preamble

This project has a long list of Tools and Materials. Because of this, I've divided the list into sections so you can see what materials are required, depending on the part of the project you are building. I've also provided Amazon links to each product, so you can easily find the parts you need.

Let's get into it!


The Pointy Bits: (please be careful!)


Hot Stuff: (also be careful!)

For the Electronics:

For Staining:


Jar Planter:


Wooden Base:


(Note: some of these tools can be very dangerous, supervision is recommended for kids completing this project.)

Step 2: Prepping the Base

Step 2a: Drilling the Mounting Hole

Once we have all of our materials, it's time to begin! We will start by creating the wooden base for the Mason Jar to sit on.

First, we need to drill a hole for the lamp to mount later on. To do this, we will make a small ink mark using a Sharpie. This marks where to drill the hole. We want it as close to the corner as possible!

Don't worry about making a few mistakes, the wooden ring will help to hide these.

Now it's time to drill. Make sure the base is securely mounted/clamped down, and then begin to drill the hole. Don't go too fast, or you'll risk splitting the wood.

I also did this in my carpeted apartment living room... maybe try doing it outside instead?

Step 2b: Gluing the Base Together

After the hole has been drilled, glue the pieces together. Apply a moderate amount of glue to the bottoms of the pieces, and place them onto the base. Now we wait for the glue to dry...

*Jeopardy music begins to play*

Step 2c: Staining the Base

Now that the glue has dried, we can stain the wood!
Carefully use a flat head screwdriver or prying tool to open the can of stain. After the can is open, stir the stain slowly to make sure it's ready for use. Lay down paper towels to protect your work surface. Then we will use a foam brush to apply a coat of stain to the wood. Make sure to get all the faces, as well as the little corners and cracks.

Step 3: Attaching the Lamp

Step 3a: Disassembling the Clamp

Depending on the lamp you purchase and the base you use, this step will most likely change, but I am adding it here to fully explain my process in the event that someone follows exactly the steps I took. For me, my lamp had a clamp on the end, with a hex bolt holding it together. I used my Allen screwdriver to loosen this (while also snapping my finger with the spring in the process. Would not recommend).

During disassembly, always remember to pay attention to where compressed energy might be hiding.

Once I had the clip apart, I had to remove a 14mm nut from the base, which required me bending part of the clip down so I could access the nut.

Step 3b: Removing the Switch

After the nut was gone, it was time to open up and remove the power switch.

We will be using the relay in its place later on.

This will require using the prying tool again, and then a soldering iron to detach the switch. Now that the switch is gone, we can feed the wire through the hole in our base.

Step 3c: (Actually) Attaching the Lamp

The lamp should be completely stripped away with just a cable running out of the bottom. Slide this through the hole we drilled earlier, and slip the nut onto the wire. Press the lamp neck as far into the wood as possible, and begin to tighten the nut onto the bottom. Do this until it's nice and firmly mounted.

It should be able to stand up on it's own. (as shown in the first picture)

Step 4: Making the Planter

Step 4a: Adding Rocks

Time to build the planter! Remove the lid from the Mason Jar and cut open the bag of rocks. Pour in some of the rocks until you get it to a level you like, mine was just about two layers high.

Step 4b: Forming the Mesh

Once the rocks have been added, it's time to make the mesh. For this I used some craft wire form, which I found at Hobby Lobby. Open up the packaging and unfold the mesh. Then, using scissors, cut the mesh so that it's about three times as wide as the opening of the jar. Begin to push it down into the jar, forming it with your fingers. Just fit it in as best you can and use the provided images for reference. Once the mesh is formed, use scissors to trim excess material.

Watch out for sharp bits!

Once you have cleaned up the edges, remove the mesh from the jar.

Step 4c: Making Water Wicks

It's time to make this planter self watering!

Open up the twine and unravel it. We need to start cutting it to length.

This part is a bit subjective depending on what size jar you use, but you want it to be two times the length from the bottom of the mesh to the rocks. Mine were eight inches long.

Cut anywhere from 6-9 of these. Now we need to add some knots. Tie each piece with a knot in the center, a simple loop will do the trick. Thread each piece of twine through the bottom of the metal mesh, with the knot on the inside of the mesh.

Option: I chose to braid the wicks because I thought it made them look nicer, but you don't have to! Just an artistic choice! (I'm knot very good at braiding)

Once you have added the wicks, place the mesh back inside the jar. Make sure the wicks are almost touching the rocks.

Step 5: Prepping the Sensors

Step 5a: Waterproofing the Sensors

Because our two sensors will be submerged in water (and planted in wet soil) it's going to be important to waterproof them. This isn't the best solution, but hot glue seems to work pretty well for waterproofing, while still allowing us to see the LED status light. Make sure to cover up any exposed metal with glue, and apply generously. We want to make sure no water can get on the electronics.

Do not cover the sensors' pads with glue, otherwise the circuits will not work. Just cover the electrical components.

Step 5b: Adding Heat Shrink

The second part of this step is adding heat shrink. I am using two jumpers to get the wires long enough, so you may not need to do this if you are using regular wires.

This is what will keep the jumpers from disconnecting while the sensors are in place.

Cut a short section of large heat shrink and slide it up the jumper. Push it over the three pin connector slowly, be careful as the sharp corners of the pins could puncture the heat shrink. Using a lighter, apply heat to all sides of the heat shrink, slowly turning the wires. Keep the flame about an inch away from the heat shrink to prevent burn marks.

Your sensors are now ready for wiring!

Step 6: Wiring the Electronics

Step 6a: Placing the Breadboard

It's time to start hooking everything up! First thing we need to do is layout the components. We'll start with the breadboard.

This breadboard is actually two separate boards I stuck together. I needed a wide enough board for the ESP32 to fit on. You can make one by removing one power trace from a breadboard, and then snapping it together with the second breadboard

Once the breadboards are connected, remove the adhesive backing and place it firmly in the corner of the planter. Then add the ESP32.

Step 6b: Wiring the Relay

Now that the breadboard and ESP32 are in, it's time to setup the relay. First, solder the ground leads of the grow light together. We'll cover this with electrical tape. Then plugin one of the leads to the middle Ground terminal of the relay, and the other lead will go to the NO (Normally Open) screw terminal.

This configuration will effectively make a closed circuit whenever the relay is powered.

Place the relay beside the breadboard (because of height limitations). Give it a little push to stick it into the soft wood of the base.

With the relay now placed, it's time to wire it up to the ESP32. Take three jumpers and connect the pins as such:

Positive -> 3.3V

Negative -> GND

Signal -> D15

The relay is ready for use!

Step 6c: Wiring the Components

After the relay is installed, place the buzzer onto the breadboard. Use diagonal cutters to snip the ends off of the sensor jumper wires, and then strip the ends with the wire stripper. Slide these wires through a slit in the wood base. Using the wiring diagram provided for reference, connected the pins as such:


Positive -> Unused

Negative -> 100Ω Resistor -> GND

Signal -> D25

Water Level:

Positive -> 3.3V

Negative -> GND

Signal -> VN

Soil Moisture:

Positive -> 3.3V

Negative -> GND

Signal -> VP

Step 6d: Drilling the Routes for power cables

Now we will need to cut channels for the power cables to run through. This will ensure the base of the planter sits completely flat. Use a Dremel to cut out two grooves, each a bit smaller than the power cables. (So they fit snuggly.)

After this, plug everything in, push the cables into their slots, and place the planter upright. Plug both power cables into the wall.

Step 6e: Testing!

It's time to test!

Verify that all of the sensors are plugged in and powered by looking for the red LED on each sensor. All LEDs should be lit in solid red. The ESP32 should also have a red/blue light that is on.

The only light that should not be on yet is the relay.

Step 7: The Code!

Step 7a: Using Arduino IDE with ESP32

So we've already wired up the ESP32, but what exactly does it do? In short, an ESP32 is very similar to an Arduino, but it has WiFi and Bluetooth built in. Because of this, we can actually use the Arduino IDE to program our ESP32. Before we can start, we do have to install the board, so I've provided a link on how to set that up, here.

Step 7b: Installing the Required Libraries

After the board is installed, let's get the libraries installed. For this project, we will need the ESPAsyncWebServer and ESPAsyncTCP libraries. Download both.

To install a library in the Arduino IDE, go to Sketch -> Include Library -> Add .ZIP Library... and then select the zip files of the libraries you just downloaded. Restart the IDE once you are done.

Step 7c: Pushing the Code

Download the attached .INO file and open it up in the Arduino IDE. It will prompt you to move it into a folder that has the same name as the project. You should see all the code, and we will want to modify a couple lines.

For both the SSID and Password, you will want to replace them with your own networks credentials:

const char* ssid = "Network SSID";<br>const char* password = "Network Password";

Once these values are updated, you can hit the check mark to verify everything is working. Once you have verified it, plug in the ESP32 to your computer via a Micro USB cable. Go to Tools -> Serial Monitor. (We will need this open for the next step) Click the arrow button to begin pushing the code.

This part is very important. When the console text turns red and begins to print out a pattern of "....___....___", hold down the BOOT button on the ESP32. If you do not do this, you will receive a header error.

Step 7d: Testing it!

Now that the code is pushed, look at the Serial Monitor. It should print the IP Address of the board, as well as current readings of the sensors every few seconds. Open up a web browser, either on your laptop or phone, and type in the IP Address

Make sure you are on the same WiFi network as the ESP32.

You should see an interface load. If you plug in the lamp and hit the Toggle Light button, you should see the light switch on and off. You can also see the sensor values, although they should say "Empty" and "Dry" at the moment. The buzzer will also occasionally beep, as a warning that there is no water detected.

Step 7e: In-Depth Breakdown

For anyone interested in the actual code, here's the nitty gritty:

We'll start from the top:

// Import required libraries<br>#include "WiFi.h"
#include "ESPAsyncWebServer.h"

// Replace with your network credentials
const char* ssid = "Network SSID";
const char* password = "Network Password";

#define relay_pin 15
#define soil_moist 36
#define water_level 39

//Variables for Buzzer 
int freq = 261.63; //Set frequency of the buzzer, this is a C note
int channel = 0;
int resolution = 8;

//Boolean for light status
bool light = true;

//Set thresholds for sensors (Tweak depending on plant/soil type)
float highWater = 1000;
float lowWater = 700;
float highMoist = 1200;
float lowMoist = 500;

// Create AsyncWebServer object on port 80
AsyncWebServer server(80);

This section is all about declaring the variables and including libraries. Pretty basic stuff, but one thing to note, you may want to change the threshold values for the water sensors depending on your planter. Another interesting thing is the frequency of the buzzer. If you change this value, it will change the pitch of the buzz. Right now it's set to C5.

//Returns soil moisture reading<br>String readSoilMoisture() {
  float m = analogRead(soil_moist);
  if (isnan(m)) {    
    Serial.println("Failed to read from Soil Moisture sensor!");
    return "--";
  else {
    //Sets text depending on values
    if(m > highMoist){
      return String("High");
    } else if (m > lowMoist) {
      return String("Low");
    } else {
      //Beeps the buzzer for 250ms
      ledcWriteTone(channel, freq);
      ledcWriteTone(channel, 0);
      Serial.println("Buzzer Activated");
      return String("Dry");
//Returns water level reading
String readWaterLevel() {
  float l = analogRead(water_level);
  if (isnan(l)) {
    Serial.println("Failed to read from Water Level sensor!");
    return "--";
  else {
    //Sets text depending on values
    if(l > highWater){
      return String("High");
    } else if (l > lowWater) {
      return String("Low");
    } else {
      //Beeps the buzzer for 250ms
      ledcWriteTone(channel, freq);
      ledcWriteTone(channel, 0);
      Serial.println("Buzzer Activated");
      return String("Empty");
//Toggles the light status
String toggleLight() {
    light = false;
    //Activates relay
    digitalWrite(relay_pin, LOW);
    return String("OFF");
  } else if (!light){
    light = true; 
    //Deactivates relay
    digitalWrite(relay_pin, HIGH);
    return String("ON");

These three sections are all functions that return a string value. This is how we derive our sensor data, and also how we will toggle the light.

After this is the HTML bit. I won't get into how HTML works, but I want to mention that the button tag calls a JavaScript function, and this is what toggles the light. You could possibly implement this to do more complex interfaces, like adding/setting timers. Another note, two of the JavaScript functions in this code are within a SetInterval(); This means that they will repeat every "interval" which we currently have set to 1000ms. These functions are what constantly load the sensor values and refresh the page.

//Updates variable text with current values<br>String processor(const String& var){
  if(var == "SOILMOISTURE"){
    return readSoilMoisture();
  else if(var == "WATERLEVEL"){
    return readWaterLevel();
  return String();

Further down the line, we get this code, which is what sets the sensor values every time the web page is reloaded.

And lastly, we have our setup function, which really just starts up the Asynchronous Web Server, and handles the page navigation (routing):

//Runs at the start<br>void setup(){
  // Serial port for debugging purposes
  Serial.begin(115200);  //Setup the buzzer
  ledcSetup(channel, freq, resolution);
  ledcAttachPin(25, channel);  //Setup the pinMode for the relay
  pinMode (relay_pin, OUTPUT); 
  // Connect to Wi-Fi
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    Serial.println("Connecting to WiFi..");
  }  // Print ESP32 Local IP Address
  Serial.println(WiFi.localIP());  // Route for root / web page
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send_P(200, "text/html", index_html, processor);
  server.on("/soil-moisture", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send_P(200, "text/plain", readSoilMoisture().c_str());
  server.on("/water-level", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send_P(200, "text/plain", readWaterLevel().c_str());
  server.on("/toggle-light", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send_P(200, "text/plain", toggleLight().c_str());
  // Start server

For more information on using the Asynchronous Web server, be sure to check out their GitHub page. Also, huge shout out to Rui Santos, who wrote the tutorial this code is based on. (He explains all the code much better than I can.)

And make sure to check out my GitHub for all of the files.

Step 7f: Future Possiblities

Using the ESP32 gives us a huge range of optional functionality. I tried to leave it a pretty basic template so that you can go in and modify the code yourselves, add features, and hopefully show me what you've made!

Some possible features I considered were:

  • Email notifications (when the planter is out of water)
  • Custom buzzer noises/tones (plays Africa by Toto when dry)
  • Using the built in RTC to set timers for the light

I'd love to see what you come up with for your planter!

Step 8: Adding the Plant

Step 8a: Picking a Plant

For this project, we used a Janet Craig Compacta, but you can use many other plants if you prefer.

Important Warning!!! The Janet Craig Compacta is a member of the Dracaena species, which is known to be poisonous to cats and dogs (and possibly humans) DO NOT use this plant if you have animals or small children! Keep the little floofers safe! :))

Tropical plants, e.g. plants that need lots of water, do well in self-watering planters. Plants that need dry soil, such as succulents, do not.Do not put succulents in this planter.

Plonty, being a tropical plant that needs lots of sunlight, should thrive in this planter, with the extremely moist soil and the added benefit of a grow lamp. Other plants that would enjoy this type of planter include (but are not limited to):

  • Hostas
  • Lobelias
  • Cherry Tomatos
  • Silantro
  • Other Herbs

Step 8b: Installing the Plant

Once you have a plant picked out, it's time to get it moved into the jar. Remove the temporary pot from the plant. Gently break away some of the extra soil, being careful not to damage the roots. We just need to get the plant small enough to fit in the planter. Once the excess dirt is removed, gently push the soil inside of the wire mesh.

Do not pack in the soil too tightly.

Now, slide the lid of the Mason Jar onto the wire mesh (See pictures) Take your time, so as not to damage the plant or smash the wire mesh.

Fill the Mason Jar up with water, and place the mesh back inside the planter, but do not fasten the lid just yet.

Step 8c: Setting up the Base

The planter has been made, now it's time to get it setup on the base. Place the mason jar on top of the base. Slide the water level sensor inside the mason jar, and tighten down the lid.

Make sure the sensor is completely submerged in the water.

Then carefully skewer the soil moisture sensor into the soil, avoiding damage to the roots. Once you have all of the sensors connected, the Planter should be completely done!

Step 8d: Finishing Touches

Lastly, we will want to test our device. Connect to the IP Address we used earlier, and see if the sensors are recording. The threshold values may need to be tweaked depending on your setup. Also test to see if you can turn the light on and off.

When you first load the page, it may take a couple button presses to get the light toggling.

And that's it. You've made the planter! Congratulations!

Step 9: Conclusion

Final Thoughts

So overall, I think Plonty loves his new digs! (Don't you?) I had a great time designing this planter, and I hope you enjoyed coming along for the ride. Thank you for checking out my Instructable, and be sure to post a comment below if you have any questions, or maybe you just want to say hi! I'll continue updating this Instructable when I make modifications to the code, so hopefully we will see some more features in the future!

Once again, thank you so much to The Urban Farming Guys for sponsoring this project.

Lastly, this Instructable was made as an entry for the Planter Contest, so if you liked it, please vote! It is greatly appreciated. :)) Have a wonderful, well-hydrated, day!

Planter Challenge

Runner Up in the
Planter Challenge