ESP32-CAM Automated Water Tank Controller
by Jumpor in Outside > Backyard
1277 Views, 2 Favorites, 0 Comments
ESP32-CAM Automated Water Tank Controller
Living in India for some time, I realized we needed a better way to control our water tank filling. At least in major Indian city centers, water is delivered to underground tanks outside the house. From there, it is pumped up to tanks in the attic, from where it flows to the consumer. Switching the pump on and off is a manual task done from the basement, which comes with several annoying disadvantages:
- No visibility: You never know how full the tank is. When filling it, you only know it is full once the overflow pipe starts releasing water.
- Water waste: With each filling, the overflow releases at least 20 to 30 liters. That is a massive amount of wasted water when multiplied across millions of homes in India.
- Constant supervision: You always have to stay close by to hear the overflow and turn off the pump in time.
- Bad timing: Running out of water unexpectedly is highly inconvenient—especially when you are covered in soap under the shower or while running the washing machine. Besides, a higher water pressure would be much better for the washing machine anyway.
- Safety hazards: Since the tank is on the roof and the stairs are quite dangerous to climb, you really do not want to go up there just to refill or check the water level.
To solve this, I built an automated water tank controller. An ultrasonic sensor measures the water level inside the tank. If it falls below a certain threshold, a relay automatically switches the pump on and turns it off again once the tank is full. I also added a camera to take pictures from inside the tank.
The original manual mode is still available but no longer needed for daily use. We now only use it when leaving the house for an extended period or in case of an unexpected system failure.
Despite the challenges I faced (which I will cover in the "Lessons Learned and Outlook" section), I hope this project inspires people in India to build their own solutions. It is a great way to prevent water waste and add a little more comfort to everyday life.
There are two youtube videos Explanation and Demo.
Supplies
Supplies
- ESP32-CAM Wi-Fi / Bluetooth Board (with ESP32-CAM-MB micro-USB programmer)
- Electrolytic Capacitor 1000uF 25V
- RC-Snubber 0.1uF K X2 275V/310V AC Polypropylene Film Capacitor
- Wago Connectors Series 221
- JSN-SR04T Waterproof Ultrasonic Distance Sensor
- Project Box / Enclosure
- Female-To-Female Jumper Cables
- 5V 1 Channel Relay Module (high/low-level trigger with optocoupler isolation)
- Legrand Mylinc 6A 2-Way Switchs (model 675502, 2 pieces))
- 6dBi RP-SMA LTE Flat Whip Antenna (2.4 GHz / 5 GHz dual-band Wi-Fi)
- 100 Ohm (Ω) 5 Watt fusible ceramic cement power resistor
Additionally, I purchased two submersible cables to run power to the roof and wire the pump to the relay, as they are highly resistant to UV radiation. Some standard parts, such as plugs, sockets, and resistors, were reused from previous projects.
Since we are renting and do not own the property, I also bought a separate water tank lid. I drilled and cut holes into it so the ultrasonic sensor, camera, and LED could "look" inside. Finally, I used insulation tubes for weatherproofing, along with an old 5V phone charger and cable for power.
Tools
- Hot-melt glue gun
- Soldering iron & magnifying glass
- Wire stripper
- Screwdrivers
- Electrical insulation tape
- Multimeter
- Measuring tape
- Utility knife / cutter
- Wrench
- Forceps / tweezers
- Power drill
- Smartphone (for taking reference photos)
- Google AI (in standard mode, for assistance)
- PC (running Ubuntu Linux)
Attaching the External Antenna
The ESP32-CAM comes with a built-in Wi-Fi antenna, but its signal strength is quite weak. To attach an external antenna, I first removed the tiny 0 Ω resistor using a soldering iron. I simply touch the resistor with the iron and gently slide it away. To avoid overheating the board, the iron must not be held to long in one place. It is a bit minuscule!
Next, I need to bridge the connection to the external antenna terminal. To help me see the details I used a soldering magnifying glass. Once the new connection is established, I plug the external antenna directly into the board's connector.
Preparing the Charger
I used an old 5V mobile phone charger for the power supply. First, I cut off the phone connector plug and stripped the wires. Since the charger converts AC to DC, I needed to test the wires to determine the polarity.
I carefully plugged the charger into a wall socket and set my multimeter to DC mode. Connecting the red multimeter probe to the pink charger wire and the black probe to the bright green wire showed a steady 5V reading. This confirmed that the pink wire is the positive terminal (VCC) and the green wire is the ground (GND).
Next, I prepared the connection for the board:
- I took two jumper cables of different colors and cut them in half.
- I connected one end of these cables to the 5V and GND pins on the ESP32-CAM.
- I wired the other ends into a connector block to bridge them with the pink and green charger wires.
With these connections secure, the ESP32-CAM is ready to be safely powered by the 5V supply.
Testing the External Antenna
To avoid assembling the entire project only to find out that something wasn't working — without knowing which part failed — I decided to test the components incrementally.
First, I connected the ESP32-CAM board with the attached external antenna to the ESP32-CAM-MB programmer module. I then connected this assembly to my computer using a USB cable. The onboard LED lit up immediately.
Next, I opened the Arduino IDE and installed the "esp32 by Espressif Systems" board manager. From the menu, I selected Tools > Board > esp32 > AI Thinker ESP32-CAM.
After that, I selected the correct COM port (see picture). If you don't see any port listed, you might be using a charging-only cable that only has two wires instead of four. Make sure to use a thicker data-transfer USB cable.
Next, I loaded the test code into the IDE, ensuring I replaced the placeholders with my actual Wi-Fi credentials, and uploaded it to the board.
Once the upload was complete, the connection status appeared in the serial monitor log. To test the external antenna, I cupped both of my hands around it. The signal attenuation increased, causing the reported Wi-Fi strength (RSSI) to drop. When I removed my hands, the signal strength went back up, confirming that the external antenna was working perfectly.
Voltage Divider for Wiring the Ultrasound Sensor AJ-SR04M
Let’s look at the wiring for the ultrasonic sensor. The AJ-SR04M is the more modern successor to the older JSN-SR04T and operates with a wide supply voltage range from 3.0V to 5.5V. It automatically adjusts its logic voltage to match the supply voltage. For this project, it is connected to GPIO pins 12 and 13 of the ESP32-CAM for the ECHO and TRIG signals. However, if the AJ-SR04M is powered at 5V, it will also output a 5V signal to the ESP. This is a problem because the GPIO pins of the ESP32-CAM can only handle a maximum of 3.3V.
One workaround would be to power the AJ-SR04M from the 3.3V pin of the ESP32-CAM instead of the 5V pin. This would drop the ECHO signal down to 3.3V automatically. However, powering the sensor at 3.3V often leads to inaccurate readings and a reduced measurement range.
To avoid this drawback, I decided to build a voltage divider. Below, I will explain the circuit setup, how to test it with a multimeter, and the electronics theory behind why it works.
The Setup
As shown in the picture, the assembly is straightforward:
- Jumper 1 connects to the ECHO pin of the sensor. The other end of this jumper is soldered to a 1 kΩ resistor.
- Jumper 2 connects to the other side of this 1 kΩ resistor and runs to GPIO 12 of the ESP32.
- Jumper 3 branches off from that same junction (between the two resistors) and connects to a 2.2 kΩ resistor.
- Jumper 4 connects to the remaining end of the 2.2 kΩ resistor and will later be attached to the GND (ground) wire of the charger.
Testing the Setup with a Multimeter
Before connecting it to the microcontroller, I verified the circuit by switching my multimeter to the 20 kΩ resistance mode:
- Test 1: I placed the red probe on Jumper 1 (ECHO) and the black probe on Jumper 4 (GND). Result: The multimeter displayed 3.2 kΩ, which is the exact sum of both resistors in series.
- Test 2: I placed the red probe on Jumper 2 (GPIO 12) and the black probe on Jumper 4 (GND). Result: The multimeter displayed 2.2 kΩ, confirming the value of the second resistor.
Why This Works
In a series circuit (where resistors are connected end-to-end), the total voltage divides proportionally to the resistance values. The resistor with the higher value drops the larger share of the voltage.
The current flows from the ECHO pin (5V) through the 1 kΩ resistor, and then through the 2.2 kΩ resistor directly to GND (0V). Because GPIO 12 branches off exactly between the two resistors, it only measures the voltage drop across the second resistor (2.2 kΩ) relative to GND.
Using the voltage divider formula, the resulting voltage at the GPIO pin is approximately 3.44V. This is well within the tolerance range of the ESP32 (as its 3.3V pins can typically handle peaks up to 3.6V). Additionally, under a real-world load, the sensor pin usually outputs slightly less than a perfect 5.0V, meaning the actual voltage will sit almost exactly at 3.3V. The 1 kΩ resistor also acts as a current limiter, providing excellent protection for the GPIO pin.
To finalize the build and prevent short circuits, I insulated all exposed joints with heat-shrink tubing.
Wiring the Ultrasound Sensor AJ-SR04M
Pin Mapping and the SD Card Conflict
The AJ-SR04M sensor features four pins: ECHO, TRIG, 5V, and GND. They are connected via jumper cables as follows:
- 5V and GND run to the main power connector block (sharing the 5V mobile phone charger supply with the ESP32-CAM).
- ECHO connects to the voltage divider circuit described in the previous step, which then routes to GPIO 12 of the ESP32-CAM.
- TRIG connects directly to GPIO 13 of the ESP32-CAM.
Note that this project does not use an SD card. This is crucial because GPIO 12, 13, and 14 are hardwired to the ESP32-CAM's onboard microSD card reader data lines. If an SD card were initialized, the ultrasonic sensor would interpret the SD data traffic as signal inputs, resulting in completely corrupted distance readings.
Why Choose GPIO 12 and 13?
Selecting the right pins on the ESP32-CAM requires caution due to their dual functionalities:
- GPIO 13 is perfectly safe to use because it has no impact on the microcontroller's boot process.
- GPIO 12 is a strapping pin. Upon power-up, the ESP32 checks the electrical state of this specific pin to determine the internal voltage for its flash memory.
If GPIO 12 is LOW (0V) during boot, the flash memory runs at its standard 3.3V (normal operation). However, if GPIO 12 is pulled HIGH during boot, the ESP32 switches the flash memory supply to 1.8V. As a result, the flash memory receives insufficient power, causing the board to crash into an endless boot loop.
This dictates our wiring choice. The TRIG pin of the AJ-SR04M usually behaves neutrally at startup or pulls slightly toward ground, which is fine. However, if the sensor were to pull the TRIG line even slightly HIGH during idle or startup, the ESP32-CAM would fail to boot. Therefore, it is much safer to connect TRIG to GPIO 13.
Conversely, ECHO can be safely connected to GPIO 12 thanks to our voltage divider network. Because the 2.2 kΩ resistor in the divider is permanently tied to the charger's GND, it functions as a highly effective pull-down resistor. It forces GPIO 12 to a solid LOW (0V) state during the boot sequence, ensuring the ESP32 always starts up flawlessly in 3.3V flash mode.
Electrolytic Bypass Capacitor
Preventing Brownout Resets with a Buffer Capacitor
Standard ESP32 development boards are quite forgiving when it comes to power supply stability. The ESP32-CAM module, however, has a unique and challenging characteristic: when the camera is streaming or capturing a photo, its power consumption spikes dramatically for a few milliseconds. Similarly, when the relay coil is energized, it suddenly draws a significant amount of current from the 5V rail. Because the ESP32-CAM is extremely sensitive to even millisecond-long voltage drops, these spikes can instantly trigger a brownout reset, causing the microcontroller to crash.
Without a capacitor acting as a "mini-battery," the camera module frequently loses its network connection, or the entire chip reboots. To prevent this, an electrolytic capacitor is required to absorb these brief voltage dips on the 5V rail, acting as a local buffer and smoothing out any sudden power fluctuations.
A decoupling or bypass capacitor should always be placed as close to the load as possible. In my first prototype, I used two shortened jumper wires to connect a 1000 µF 25V electrolytic capacitor directly to the ESP32-CAM pins. This kept the pins clear so I could still attach the ESP32-CAM-MB daughterboard for computer programming. The power delivery cables from the charger were soldered directly to the legs of the capacitor and then heavily insulated.
In the final assembly, I switched to slightly longer jumper wires because I needed to mount the bulky capacitor securely inside the project box, which wasn't possible with the short-wire version. To stress-test the stability, I took about 20 pictures in rapid succession to see if the ESP32 would crash. The system remained perfectly stable, confirming that the decoupling solution works reliably.
Relay
Solving the Relay Boot-Up Conflict (GPIO 15)
To ensure safe operation, the physical trigger jumper on the relay module is set to HIGH, and the relay's IN pin is connected to GPIO 15 of the ESP32-CAM. This configuration resolves a critical hardware conflict that occurs during the initial boot sequence.
The Problem: Hardware Traps on GPIO 14 and GPIO 2
During boot-up, the ESP32 undergoes a 300-millisecond hardware initialization phase controlled by its internal ROM. When using a standard, low-level triggered relay (which activates when it senses a LOW/GND signal), most available pins cause a dangerous conflict:
- GPIO 14: This pin is internally hardwired to the onboard microSD card slot. During the boot sequence, the ESP32 automatically activates an internal pull-down resistor (GND) on this pin.
- GPIO 2: This pin is hardwired to the onboard red SMD activity LED. The current path through this integrated LED and its current-limiting resistor acts as a physical pull-down (GND leakage) during power-up.
The Consequence: Because a low-level triggered relay activates on a LOW signal, both of these pins cause the relay to instantly click ON the microsecond power is applied. As a result, the water pump would forcefully jump-start before your custom C++ setup() code even has a chance to execute its protective digitalWrite(RELAY_PIN, HIGH) command.
The Solution: GPIO 15 and High-Level Triggering
To prevent the pump from unintentionally turning on during boot, we must use a neutral pin and invert the relay logic:
- Using GPIO 15 (HS2_CMD): This is the only remaining data pin on the ESP32-CAM header that does not have any onboard SMD components, transistors, or LEDs attached to it. It remains completely floating and uninfluenced by the board's internal circuitry during the boot process.
- Flipping to High-Level Triggering: Even on a neutral pin, the ESP32 chip naturally pulls its GPIOs slightly toward ground during the very first fractions of a second. By physically moving the jumper on the relay board from L (LOW) to H (HIGH), the input logic of the relay's optocoupler is completely inverted. Now, a LOW signal (0V) represents OFF, and a HIGH signal (3.3V/5V) represents ON.
The Ultimate Result
When you flip on the main power switch downstairs, the initial boot-up ground state of the ESP32 is now interpreted by the relay as a strict "Keep OFF" command. The relay's status LED stays dark, the internal contacts remain open, and the pump stays absolutely silent. The pump will now only activate when the ultrasonic sensor genuinely detects a low water level and explicitly drives GPIO 15 to HIGH.
Power Connections
To complete the module's wiring, the VCC and GND pins of the relay are connected to the 5V charger in parallel with the other components. The IN pin connects directly to GPIO 15. The high-voltage AC wiring on the load side of the relay will be covered in the upcoming section, "Connecting to AC."
RC-Snubber
Adding an RC Snubber for Arc Suppression
An RC snubber (also known as an RC circuit, Boucherot cell, or spark suppressor) is essential for this project. While the manual wall switch can handle the pump's switching load without issues, the small electronic relay controlled by the ESP32-CAM cannot.
A water pump is a highly inductive load. When the relay turns off the pump, the magnetic field in the motor’s copper coils collapses, forcing the current to keep flowing. Because the relay contacts open at that exact millisecond, the current jumps across the tiny air gap, forming an electric arc (spark). Without protection, this arc has two destructive consequences:
- Microcontroller Crashes: The electric arc generates a massive electromagnetic pulse (EMI) and high-frequency interference on the power line. Since the ESP32-CAM shares this power supply, this pulse almost always causes the microcontroller to immediately crash, reset, or freeze permanently.
- Relay Contact Wear: Over time, the spark burns away the paper-thin contact layer of the relay. Within weeks or months, the contacts can weld together ("stick"), causing the pump to run endlessly and overflow the tank.
Since commercial, pre-assembled snubbers were unavailable, I built my own using a 100-ohm resistor and an AC-rated capacitor. The snubber is wired in parallel across the relay's switching contacts (one side to the COM terminal, the other to the NO terminal).
Assembly and Wiring
Because the relay terminals are too small to accommodate two thick wires simultaneously, I used a clever wiring workaround:
- The Snubber Circuit: I soldered one lead of the 100-ohm cement resistor to one lead of the X2 polypropylene film capacitor.
- The Splice Joint: To connect this to the main pump cable without overcrowding the relay terminals, I stripped about 3 inches of insulation from the main wires. I then used a secure knotted tap joint to splice in two short helper wires, soldering and heavily insulating the connections with heat-shrink tubing.
- The Final Connection: The free ends of these two helper wires were soldered to the remaining legs of the resistor and capacitor. This allowed me to run just a single wire into the relay’s NO and COM terminals, while the RC snubber effectively sits in parallel to them.
The Result: When the relay opens, the pump’s destructive inductive energy is redirected at lightning speed into the snubber's capacitor and dissipates harmlessly as minimal heat through the resistor. No electric arc is formed, and the ESP32-CAM runs completely smoothly without resetting.
Assemble All Parts Around the ESP32-CAM
Enclosure Preparation and Pin Mapping
The project box was prepared by drilling holes to accommodate the standoffs, cables, and the external antenna. The lid of the box is opaque, which is actually ideal: I only needed to drill a single small hole for the camera lens to look into the tank, as the onboard flash LED is bright enough to shine right through the translucent plastic.
Initially, I mounted the ESP32-CAM using strips cut from an old credit card, which I fused to the lid with a soldering iron and secured with hot glue. While this worked for a while, the plastic strips eventually softened due to the warmth, so I replaced them with proper mounting screws for a more durable solution.
In India, cable glands are widely available, but usually only round ones for the retail market. However, submersible pump cables in India are flat. To seal the gaps, I used a combination of insulation tape and hot glue. This sealing is crucial for another reason: a warm electronic box like this would otherwise become a favorite nesting spot for insects. I also applied insulation tape tightly on the inside of the box to act as a basic strain relief, preventing the cables from being pulled out from the outside. Since the box remains stationary, the risk of wire chafing is minimal.
ESP32-CAM Pin Allocation Summary
- 5V: Connected via a jumper to the electrolytic capacitor, which has the positive power wire soldered directly to its leg.
- GND: Connected via a jumper to the electrolytic capacitor, which has the negative ground wire soldered directly to its leg.
- GPIO 12: Connected to the voltage divider circuit leading to the ultrasonic sensor's ECHO pin.
- GPIO 13: Connected directly to the ultrasonic sensor's TRIG pin.
- GPIO 15: Connected directly to the relay's IN terminal.
Why Choose the ESP32-CAM Over an Arduino?
As the central controller, I chose the ESP32-CAM board alongside the ESP32-CAM-MB daughterboard for easy USB programming. The ESP32-CAM features a built-in OV2640 camera, a powerful flash LED, and integrated Wi-Fi and Bluetooth. It is tiny, consumes less power than a standard Arduino setup, and can host its own web server, allowing me to check the water level from my smartphone.
I decided against using a standard Arduino because it lacks native wireless connectivity. To achieve the same goal, an Arduino would need to be paired with an external Wi-Fi module like the ESP-01. The Arduino would read the sensor data and send it via serial communication (TX/RX) to the ESP-01 for broadcasting.
However, this dual-chip setup is notorious in the maker community for being highly frustrating. The ESP-01 operates at 3.3V, while the Arduino Uno runs at 5V, requiring additional logic-level shifters. Furthermore, debugging the communication code between two separate microcontrollers is highly error-prone. By using the standalone ESP32-CAM, I avoided these compatibility headaches entirely.
Connecting to AC
Analyzing the Switch Panel and Implementing the Mode Selector
The manual pump switch is part of a larger wall panel containing several switches and a power socket. When I first opened it, the chaotic wiring I found (see pictures) was so terrifying that my original plan was simply to build a system to look inside the tank and check the water level. This explains why I chose a microcontroller with a camera module in the first place! I hoped to at least get a visual on the water level. Later, I realized that relying solely on the camera was difficult because the image quality inside a dark tank isn't great (you can see some sample photos in the "Lessons Learned and Outlook" section below).
Refusing to give up, I did a deeper analysis of the circuit. The original pump switch features four terminals. Using my multimeter, I discovered that the live AC wire (phase) enters at the bottom-left terminal, from where it bridges to power other switches and sockets in the panel. Similarly, the neutral line enters at the bottom-right terminal.
When the switch is turned ON, the bottom terminals bridge to the top terminals (bottom-left to top-left, and bottom-right to top-right). The top-left terminal sends the live phase up to the pump, while the top-right terminal receives the neutral line returning from the pump motor.
To safely toggle between manual and automatic operation, I installed two additional 2-way switches. I routed two cables out of the original switch panel: first, the original top-left live wire that feeds the pump, and second, a new bypass wire connected to that same top-left terminal. These are wired to the two new switches according to the wiring diagram (see picture).
The attached matrix outlines the switching combinations, but two specific positions are relevant for daily use:
- Automatic Mode:
- The original manual switch must be left DOWN (ON).
- The two new 2-way switches must be flipped UP.
- In this configuration, the pump is fully controlled by the ESP32-CAM; it turns on when the relay closes and stops when the relay opens.
- Manual Mode (Override):
- The original manual switch must be kept UP (OFF) during standard automatic standby.
- The two new switches must be flipped DOWN.
- To run the pump manually in this mode, you simply flip the original wall switch DOWN (ON), bypassing the microcontroller entirely.
Code
Code Insights and Sensor Calibration
The full source code for this project is available on GitHub.
During the initial testing phase, I wrote a version of the code that allowed me to edit and store the threshold configuration values directly onto the board's non-volatile flash memory. However, once I found the ideal settings, I decided to hardcode all values. Standard flash memory is usually rated for about 10,000 to 100,000 write cycles. Since my ESP32-CAM is a budget clone rather than an original module, its write cycles might be significantly lower—potentially failing after fewer than 1,000 writes. To protect the chip from premature wear, hardcoding was the safest route.
The necessity for extensive testing came from the physical limitations of the AJ-SR04M sensor, which has a blind zone of about 25 cm. Any object closer than this distance will yield highly unreliable readings. Because of this, I set the automatic shutoff threshold to 30 cm away from the sensor. To ensure the tank still gets fully filled despite this safety margin, the code features a post-run timer. This timer keeps the pump running for a predefined number of seconds after the threshold is reached, maximizing the tank's capacity safely.
How the Ultrasonic Signal Trigger Works
The code itself is well-commented and easy to follow. Below is a deep dive into how the getFilteredDistance() method triggers the ultrasonic measurement pulse:
- Line 1 & 2: Pulling the trigger line LOW for 2 microseconds clears the channel, grounding the line to guarantee a clean, interference-free start.
- Line 3 & 4: Pulling the line HIGH for exactly 10 microseconds initiates the measurement. This 10-µs window is a strict requirement from the sensor's datasheet; if the signal is sustained for less or more, the internal electronics will not trigger.
- Line 5: Turning the signal LOW again creates a falling edge. This tells the AJ-SR04M that the command is complete, prompting it to immediately emit a burst of eight ultrasonic pulses into the water tank.
Calculating the Distance
Once the sound wave is sent, the code captures the echo and converts time into distance:
The pulseIn() function listens to the ECHO_PIN, waiting for it to flip to HIGH, and measures the exact number of microseconds until it drops back to LOW. I added a timeout of 30,000 microseconds (30 ms) so the microcontroller doesn't hang indefinitely if an echo is lost.
Since the sound wave has to travel to the water's surface and back again, the total duration must be divided by two. Sound travels through air at approximately 343 m/s, which converts to 0.03432 cm per microsecond. Multiplying half the duration by this constant provides the final, highly accurate distance in centimeters.
Test
Sandbox Testing and Initial Verification
To minimize the risk of installing the entire system only to discover hardware or software bugs later, I followed a strict incremental testing routine:
- Software Simulation: I built a dedicated simulation mode directly into the code. This allowed me to rigorously test the core logic and state machine before deployment.
- Hardware Validation: The Wi-Fi antenna performance and the effectiveness of the electrolytic decoupling capacitor were verified during the earlier modular steps.
The Desk Test
Since the ESP32-CAM hosts a local web interface, I was able to perform a complete functional test right at my desk. I placed the ultrasonic sensor facing upward toward the ceiling, which simulated a completely empty water tank.
To test the logic, I held a book about 30 cm above the sensor. The web interface immediately registered the change, and the code seamlessly transitioned from standard sensor-based filling to the post-run timer phase for the configured duration. Best of all, hearing the satisfying, physical "click" of the relay at the exact start and end of the cycle confirmed that the control circuit worked flawlessly.
Real-World Refinements
However, even the most thorough bench testing cannot catch every edge case. Once the system was fully installed on the roof, I still discovered a few minor bugs in the code. This meant I had to take down the tank lid with all its components, open the enclosure, remove the ESP32-CAM to upload the updated firmware via the programmer board, and reinstall everything. It took a few iterations of this back-and-forth process before the system ran perfectly in its environment.
Lessons Learned and Outlook
Planning and Architecture
Looking back, I approached this build much like an agile software development project, heavily relying on AI. However, what I should have done from the very beginning was establish a comprehensive, end-to-end blueprint. This would have required a deeper initial analysis of all components — especially the chaotic wall switch panel — to verify if each part truly added value and if their combination made sense.
Furthermore, I should have discussed my initial design on maker forums or with peers to refine it before buying parts. Had I done so, I would have realized much sooner that the camera module didn't actually help solve my core problem. Even if I had installed a labeled, physical measuring pipe inside the tank for visual reference, adjusting the camera's angle and maintaining lens clarity in that environment would have been incredibly difficult. Ultimately, a more thorough upfront planning phase would likely have led me to choose a different, camera-less microcontroller — and certainly a much more spacious project box!
The Use of AI in Hardware Development
AI helped me a great deal to complete this project. However, as most AI users know by now, its outputs must always be treated with skepticism.
This became glaringly obvious when figuring out how to safely wire the system to AC power and implement the manual-to-automatic mode selector. At that stage, the AI completely hallucinated, providing fundamentally flawed circuitry solutions. Instead of admitting its limitations, it repeatedly adjusted a broken design over and over again. It wasn't until I actively proposed alternative circuit ideas that we were able to nail down the final, working solution.
Additionally, I noticed that starting a new chat session without pasting the complete project context led the AI to suggest seemingly helpful paths that turned out to be technical dead ends later on.
Outlook
Moving forward, I plan to install a Raspberry Pi to act as a centralized smart home server, which will allow me to log and monitor all water tank consumption parameters over time. Once that is in place, I intend to replace the ESP32-CAM with a more efficient microcontroller and repurpose the camera module for a project where it is actually useful.