A key part of the puzzle for this project is energy efficiency. After all, there’s no point in this if I just replace watering every day with recharging the batteries every day. There’s a whole bunch of techniques that you can use to reduce power consumption on the ESP32 such as reducing the clock speed and minimising the WiFi usage, but I decided to focus my efforts on just one – deep sleep.
You can see the complete code for this project at: hsmag.cc/VmEzta.
On the ESP32, deep sleep isn’t really like putting your computer to sleep; it’s more akin to turning it off and back on again. Everything stored in RAM is lost (there are ways around this for small amounts of data), and the sketch starts from the beginning again. For our purposes, this isn’t a problem as we just want to read the sensor values and send them off to the internet. When it’s in deep sleep, it uses very little power (around 10 microamps), so we don’t need to worry too much about this level of current consumption when we have 3600 mAh to play with. When the unit is taking readings, it’s a short amount of time so power usage isn’t a problem.
Putting an ESP32 into deep sleep is fairly straight forward in Arduino. First, you need to set a wakeup source with:
esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
This is done with two defined values: the amount to sleep in seconds, and the multiplication factor to take this into microseconds, which is what the ESP32 uses. When we want to go into deep sleep, we then use:
esp_deep_sleep_start();
In principle, that should be enough, but for some reason, I couldn’t persuade my device to sleep for more than 25 minutes. No matter what value I put in the TIME_TO_SLEEP definition, the board would wake up after a maximum of 25 minutes. After searching for a solution for a while, I decided to go with the simplest hacky solution, and that is to count how many wake-ups there have been and only read the sensors / do some processing after a certain number of wake-ups. In other cases, I’ll just shut the device down again.
This does cause a slight problem because variables aren’t held between boots, so how can we know how many times it’s booted? The solution is in the real-time clock (RTC). This is the part of the ESP32 that wakes up the main core after a certain amount of time. There’s a small amount of memory on the RTC that we can use to store bits of information like this.
In the Arduino language, we can declare a variable that will be held in the RTC memory with the following:
RTC_DATA_ATTR int bootCount = 0;
Then, we can reboot the machine every time we don’t need it with the following at the very start of the setup:
++bootCount; sleepus = TIME_TO_SLEEP * uS_TO_S_FACTOR; esp_sleep_enable_timer_wakeup(sleepus); if(bootCount !=3) { esp_deep_sleep_start(); } bootCount=0; // this will only run on the 3rd boot.
This is a pretty hacky solution, but it did give me a chance to play with the RTC variables. If you can spot what the bug is in my code to make it necessary, please get in touch.
How long will it last on a battery? We’ve no idea! The current consumption of the unit is very low (under 1 mAh per hour), but the batteries aren’t necessarily their stated capacity, and even if they are, they won’t necessarily respond particularly well to this style of use. The one feature that this board doesn’t have, that we’d like, is a way of checking the battery level. It’ll just suddenly stop working at some point. Hopefully, it’ll take several weeks (or ideally a few months) before I find out how long it lasts. The final part of the puzzle will be calibrating the moisture level. That will just be a case of seeing what readings it gets at different levels of moisture, and seeing what corresponds to unacceptably dry.