Rummys Blog An world of endless Monday

Friday, 30 November, 2018

An IoT Mousetrap Part 3 – wake-stub polling

Filed under: ESP,ESP32 — Andrew.Rowbottom @ 1:00 pm

The current investigation state of my IoT Mousetraps, iteration #3.


My mousetraps are currently

  • running “directly” off 600mAh batteries
  • using a “replaced” Voltage Regulator that draws ~70µA
  • sensing the status of the actual trap using an input pin in “pull-up” mode (drawing ~70µA)
  • waking fully up at a configurable interval to send quite a lot of status and debug information via espNow to an esp8266 connected to a RaspberryPi and Node-Red
  • fully on for approx 300ms (excluding time-to-wake-up and re-enter deep-sleep)
  • lasting only about 3 months between recharges

Ignoring the 70µA drawn by the Voltage Regulator for the moment, about half of the current drawn during deep-sleep is from using the inbuilt pull-up capabilities of the GPIO used for sensing state.

Now, I could use external pull-up resistors, and that is probably the most sensible approach! But for the hell of it I am investigating “polling”.

Using a WakupStub

There are quite a few things required to use a wakeup stub, for reference see deep-sleep-stub.rst, I’m sure I’ve not covered all of the requirements here, but some of the major points are

  • If you call it void RTC_IRAM_ATTR esp_wake_deep_sleep(void) then you don’t need to explicitly set it as the wakeup stub
  • It needs to be marked as RTC_IRAM_ATTR (or in a specially named source file) so the code is stored in the RTC RAM.
  • The RTC fast (and probably slow) RAM need to be kept enabled when entering deep sleep
    esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_FAST_MEM, ESP_PD_OPTION_ON)
    esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_SLOW_MEM, ESP_PD_OPTION_ON)
  • Everything you want to call needs to be either in ROM, or in RTC_IRAM, and that happens to be a *LOT*, so you’ll have to find the equivalent code and copy it into your project (renamed and marked RTC_IRAM_ATTR)
  • You can re-enter deep sleep, but the calculation is moderately complex, though I have simplified this by computing a “per-second” scale factor in my “setup()” and storing it for easy use in the wakeup stub.
  • If you want to wake up fully you must call esp_default_wake_deep_sleep()
  • If you want to go back into deep sleep you do not need to call esp_default_wake_deep_sleep()

Current Draw and Timings

Playing around with excessive delays in the wake-stub so that I can use a simple multimeter to measure milli-amps gives me a current draw of a little over 12mA (with the built-in LED off).

Comparing that with a constant 70µA means that my polling code can be “awake” in the wakeup stub for approx 0.07/12 of the time .. i.e. 0.5% of the time .. or roughly 5ms every second at a 1 second poll rate, more at a slower poll rate.

Using my oscilloscope initially I had some very disturbing timings, my wake stub was taking 47ms!

The output was:

ets Jun 8 2016 00:22:57

set from pre-set=0x70A72
calling my_rtc_sleep_set_wakeup_time with 3053670 + 461426 Wake count 2
with GPIO22 LOW GPIO15 is 0
with GPIO22 HIGH GPIO15 is 1
about to call ledStateIRAM with 1

gpio15 and 22 are connected
about to sleep from wakeup!
sleeping, awake between 3053650 and 3056738 rtc ticks, expected to wake at 3515096
sleeping, awake between 18882309 and 18901404 micros, expected to wake at 21735670

I noticed that it was going back into deep-sleep immediately after the serial output was complete, so I massively reduced the output and had a wake time of about 10ms. Still Too Large!

ets Jun 8 2016 00:22:57

Wake count 2

Then I completely removed the code that was flushing the buffer and I now have 0.5ms (Different scale!)


ets J

This is approx 0.5 ms! Tidily under the 5ms computed budget, and this could be improved dramatically by polling at an even slower rate.


For sanity checking rather than just monitoring the serial and state of an output pin I also tagged the current through a 1 Ohm resistor. This gave a 1.5ms “on time” though admittedly a fair bit of that seems to be die down.

I have a very noisy scope, but I think you can see that the current starts to be drawn a reasonable time before the serial output starts.

I’ve measured from the increase to the end of the current draw, you can see the exponential decay on the serial line which would match the deep sleep starting I think.

The Sensible Solution!

Obviously the sensible (easy) solution would be to do a full wake-up every 1 hour, and accept that the trapped mouse isn’t going anywhere. That would have been sensible!

Monday, 8 October, 2018

An IoT Mousetrap Part 2

Filed under: ESP,ESP32,World of Warcraft — Andrew.Rowbottom @ 11:35 am

The Mousetrap code has gone through several iterations, this iteration Number 2  has changed the code quite a lot..

The aim of this iteration was to significantly reduce the “ON” time from 3seconds to reduce the consumed watts in this phase.


I have switched from connecting to WiFi (about 3 seconds on time!) to using espNow sending to a “receiver” permanently connected to my Raspberry Pi, this is to reduce the “On” time during the regular status updated. I’ll try to post some information about using espNow in another post.


The “On” time is now reported as approx 300ms (mainly due to the huge number of status messages I send), well down from the roughly 3 seconds connecting to WiFi and sending using MQTT/HTTPS takes.

Unfortunately this has not made a huge difference to the overall battery life which is still running at around 3 months for a fully charged “600mAh” LiFePo4.

Further investigation is showing 2 major current draws:

  1. the LDO at approx 70µA
  2. the pullup/down current approx 70µA

Iteration #3 will look at how to reduce this .. until I un-solder the LDO Voltage-Regulator I’m limited to removing the pull-up/pull-down current. There are a few options:

  1. switch from using the internal pullup/down to an external resistor
  2. use the wakestub and poll the switch/mousetrap state regularly but not too frequently
  3. use the ULP and poll using that.

An IoT Mousetrap

Filed under: ESP,ESP32 — Andrew.Rowbottom @ 11:23 am
NOTE: this has been sat waiting to post for many months. I am posting it because I have updates incoming.


IoT! Who needs a mousetrap on the internet! Turns out I do.

We occasionally have some mice visiting our cupboards, so we tucked a mousetrap in a dark corner of a cupboard.

But the old adage “out of sight out of mind” really applies to us! And, after checking daily for a week only to find nothing we got lazy and started checking only when we remembered. Predictably one day we found a particularly smelly little corpse! Finally a potential use for those ESP boards I’ve got lying around!

Thus is born yet another IoT mousetrap!


  • Mousetrap
  • ESPea32
  • A replacement LDO voltage regulator
  • A bit of Soldering skill to replace the LDO
  • Edge Connector
  • Wire
  • Tin Foil salvaged from a tea-light
  • Battery Box
  • Internet

Modding the trap.

My mouse trap comes in three parts a white top and a base and a red “tongue” which is the lever that triggers the top part.

When its armed the top is tilted “backwards” and makes contact with the lever.

To wire it up for IoT all I needed to do was to put a contact on the top part and and one on the tongue, and then detect when the two are (or aren’t) in contact.

For the contacts I have used the tin-foil cup from a tea light, originally I used cooking foil, but it proved too thin, the foil cup on a tea-light is thick enough to hold its shape and still easy to trim with scissors.

The top contact is cut to fit neatly onto the top section. I left a couple of tabs of foil to fold over and tuck under the metal spring. I shove a piece of wire under the spring to complete the connection between the ESP and the top contact.

The bottom contact is nothing like as tidy, it’s a strip of foil, soldered at one end, and placed lightly over the tongue so it makes contact with the upper plate when the trap is armed. A bit of tape at one end stops it moving around too much without interfering with the action.


Using an ESP32ea (any ESP32 should do) I wire one plate to GND, and the other one to a GPIO, it doesn’t matter which way around, the voltages are low and it’s only “contact” that is important.

The GPIO is set to “INPUT_PULLUP”, so when the two plates are in contact it is pulled “LOW”, and when separated it goes “HIGH”.

The ESP is put into Deep Sleep (to conserve battery), set to wakeup (esp_sleep_enable_ext0_wakeup) when the GPIO goes into the opposite of what it currently is (ESP32 feature) It will wake up when the trap is closed, or when I open it ready for the next mouse.

When the ESP wakes up (either GPIO state change, or Timer), it checks the wakeup cause, and if the GPIO state is different from what it was when it went to sleep, it sends a whole bunch of messages.

I have a timer wakeup so I can sample the battery and send a “battery needs charging” message.


Modding the ESPea32 for low deep-sleep current

I only have a couple of esp32 boards, several cheap ESPea32’s and a more expensive FireBeetle-32. The firebeetle, although it has much better deep sleep current draw as supplied is slightly too long to fit in an AA battery box, so I’m using an ESPes32.

The ESPea32, though nice, is not really good for running off a battery in deep sleep.

The CP2104 seems to be correctly wired so that it is only powered when the USB is connected, something I checked on the schematic before I bought it.

But the voltage regulator is a bog standard AMS1117 with a quiescent current draw in the order of several mA, far too much for a deep-sleep battery powered device. Fortunately it’s a nice large (for SMD) SOT-233 package than can be reasonably easily un-soldered and replaced with a much more efficient device. Once that is done my ESPea32 now draws under 0.2mA in deep sleep, a 100 times improvement, though a “naked” esp32 can draw even less. NOTE: the LDO draws approx 70­µA

Headers / Terminals

I didn’t use standard headers for this project, instead I’ve used 2.54mm screw terminal blocks. This is the first project I’ve done this way, and so far I’m liking it! The wires don’t come loose as easily, the overall height (8.5mm, 11.5mm including pins) remains small enough to fit in the battery box. I can squeeze 2 wires into one terminal (for example GND to battery + a wire to the mousetrap), reducing the component count.

I did think about using the supplied PCB headers and dupont jumper wires, but the overall height is way too large to fit in the box. A standard PCB header is about 11mm overall, including through hole pins. The jumper wire pins add an extra 14mm onto that . And then the wire has to be bent. Lets call the overall height 25mm. An AA battery is only 14mm in diameter.

Wiring up the Mousetrap

My mousetrap is not your traditional wire one, which complicates things a little. Its a plastic module, so it needs some sort of conductive layer applying. I originally used cooking foil, but its thin, tears easy, doesn’t solder well and looks really horrible. The current version uses tin-foil taken from a “tea-light” which is thick enough to retain it’s shape with minimal gluing.

One piece is folded around the top, with a “tag” slid under the spring. Also slid under the spring is one of the “detector” wires.

Another piece is a lightly bent strip placed over the lever, and stuck to the bottom of the trap, a “detector” wire is soldered onto this strip.

Hardware choices


I have a stock of LiFePo4 batteries lying around, they’re not your usual LiIon batteries, they have a lower voltage, between 3.6 and 2.8v with a long flat stable period at 3.2v. A perfect voltage range for powering ESP devices directly without needing a voltage regulator. They’re less prone to catching fire, though I doubt they’re 100% safe in that regards – certainly you can short circuit them, and 10A even at 3.2v can cause a fire in thin wires.

In theory this means I should be able to remove the Voltage Regulator from the circuit, and if I had a supply of “naked” ESP32, that’s the path I’d have gone down.


I like these boards, they’re not ideal for this kind of project by a long shot, but they’re not bad. I managed to snarf some cheaply, and they fit tidily into my switched 4x AA battery box. They also seem to have a “correct” implementation of the USB/UART which reduces battery drain. Unfortunately they use the horrible LMS1117 in a comparatively large SOT-233 that has to be replaced or unsoldered if you want low deep sleep battery life. Perfectly you would unsolder it completely but then programming via Arduino would be a pain, unless you add a jumper.

My ideal board would look pretty much like the espea32 have a disableable/bypassable Voltage Regulator to remove quiescent current drain when powered by a LiFePo4. And had a Gnd pin immediately alongside the 3v3 connector .. perhaps a layout like 5v,GND,3v3 (or a second GND). And had an ADC pin connected to a voltage divider (and also 3v3 via a breakable PCB wire)… yes I know that would lose an ADC channel, but you’d gain a battery meter!

I’m actually a bit 50/50 about a LIPO connector/Charger, they’re extra cost, and you have to check carefully to see if they’ll fit in a cheapo project box.. 4xAA switched boxes are dirt cheap 🙂 !


Setting up the hardware

Break the dividers in the battery box so there’s room for the board and 1 battery.
Solder in the 3 screw connector so it covers 5v, Gnd, and a GPIO. I’d rather have used 3v3 direct, but it’s not besides the GND connector. Screw in the battery connectors, and the 2 detector wires.
Job done.. no extra components required


This isn’t a traditional “looping” arduino program, it does all of its work in setup, and goes to Deep Sleep before it enters the loop.

It will
Store the boot count in RTC memory, not strictly needed, but “tidy”.
Store the last state of the GPIO in RTC Memory, so it can tell if something has changed between wake-ups.
Store whether it successfully sent the notifications (in case there’s a WiFi or internet connectivity issue) [Distributed Computing Fallacy: The network is reliable]
Sample the battery voltage, so I can get low battery alerts.

Set the ADC to input (it’s input only anyway)
Set the GPIO to INPUT_PULLUP so it goes high unless it’s being pulled to ground by the mousetrap wiring.
Read the wakeup Cause
Read the GPIO state
Read the ADC
Set the deep sleep trigger to wake if the GPIO goes to the opposite of its current state.
Set the deep sleep to wakeup in 30 minutes in case the GPIO doesn’t trigger or it fails to send.

Decide if a status message should be sent, this will be sent if
it’s first boot (PREVIOUS GPIO level is -1).
the GPIO level is different from what it was last time it was checked.
the message didn’t get sent successfully last time.

Store the GPIO State for the next wakeup.


Current consumption in deep sleep is approx 0.12mA, NOTE: I *did* replace the abhorrent AMS1117 with a much more efficient LDO. I think that having WiFi connectivity enabled at all raises the deep sleep current by about 0.04mA, but I guess that’s something I’ll have to investigate further. Forgetting to turn off WiFi before going into deep-sleep [WiFi.disconnect(true /*means wifiOff*/)] changes the deep sleep current by a LOT more I measured 1.33mA!

Powered by WordPress