ESP8266-DHT22-SR501 kitchen lights thingamabob PCB (#P7F1)

Alright, software aside, back to hardware…

The ESP module shown in #P7 slowly gathers dust in my kitchen, which is tolerable for a few weeks, but that’s not the solution I want long-term. Also, it’s on a separate power brick, meaning there are two microcontrollers running in my kitchen for doing fancy light switches and some air quality logging for twice the standby current. Nope, has to go, needs to be consolidated into one unit.

So a while back I designed this PCB and ordered it (once again) at OSH Park, together with some teeny-tiny DIP adapters that cost next to nothing.

As I do count six different strips of LED lights on five pairs of wires in my kitchen, I just routed any digital pin of the Wemos D1 Mini to a (N-Channel) MOSFET footprint. Now these need to be populated with something LV-TTL compatible like the NTD4809N that I ranted about, and these should be used as low-side switches. Well, high-side switching with 3.3V is possible, too, but let’s just forget about it until someone invents a practical use for it… :lol:

I also made two fixed connections to accommodate the DHT22 temperature/humidity and the HC-SR501 PIR motion detection modules directly on the board. They still have parallel tracks running to MOSFETs, but as I knew I wanted them, it was easiest to just make room for them. Moreover, as OSH Park always delivers three PCBs, these might be used in either configuration in future projects.

PCB is here:
p7f1 KiCad zipped

Schematic is here:
p7f1 PDF

Although I’d recommend reading the rest first, as there are quite a few pitfalls in using all of these “G”PIOs…

Note there are two pins for external power, as the ESP8266 board does host some USB (5V) to 3.3V converters, but you can also feed directly into the ESP. As I decided for some DC-DC boards (to be reviewed) in order to just hook up to the general 12V supply, I made these pins fit the DC-DC footprint.

Here’s the not-so-final unit:

Lessons learned…

1) Read datasheets and anticipate bottlenecks

Wemos has a wiki-like reference page for the unit. It clearly states “All of the IO pins have interrupt/pwm/I2C/one-wire support except D0.”. Well, the original design on an Arduino Micro (and later a DigiSpark unit) used polling at twice a second for the motion detector. Which is fine if your micro does nothing else and is low power anyway. The ESP however does quite a bit on the side (WiFi, ahem), and I decided for a 5 second polling interval on the DHT sensor. Which is a pretty bad idea when you want your lights turn on as soon as you enter the room.
So I implemented an interrupt for the PIR sensor, probably the first one in my life. And guess which pin was used on the PCB? THE ONLY FRIGGEN ONE THAT DOES NOT SUPPORT INTERRUPTS :mrgreen: Yeah, wasn’t planned on the time of PCB design, but Murphy gets you every time. I ran a botch wire from another port, disconnected the original trace, that’s it. Oh, and the A0 pin is hard wired to an ADC, so no interrupt on that one. Digital pins only.

2) Use sockets on prototypes
Seen the photo above? That ESP is soldered directly onto the PCB. That’s fine, except I am an idiot and I’ve assembled my very own PCB in the wrong order.

#1: Rp1 in the left bottom corner is the pull-up resistor for the DHT sensor. Yep, that is required, not just optional. Yep, I did not populate this before covering the area with the now fixed in place ESP8266. D’oh. Well, I’ve added the SMD part between the pins on the bottom, so that’s a very inconspicuous botch/fix.

#2: See the J1 pin pair just above Rp1? Yep, that’s the inputs that feed DC-DC’d power from the 12V input. Yep, that wasn’t populated either. Yep, that can be fixed later on as well, but it is a royal pain in the butt to solder these two wires.

#3: Someone was thinking and added MH1, an M3 mounting hole in the center of the board. Well…good luck inserting a screw after the ESP8266 was soldered in place…and then tightening it :suspect:

3) Interrupts are eternal

Pretty specific, so not on the product page at Wemos. Being the interrupt newbie that I am, I wasn’t aware of this – maybe this is common, maybe not. I use the PIR interrupt to fire up the lights as quickly as possible. That just takes a few commands and we’re done. After that one kinda-sorta worked (see below…), I used another pin for a touch button input. And again, that one was read out before in polling mode, but if you already got a working interrupt, why not use another one for the button. What could possibly go wrong? Well, let me tell you: I need some kind of indicator about what it does. See, this button toggles the “always-on” mode for when you watch your cake in the oven or do unspeakable things to veggies that do not require much movement of your entire body. I would need another LED or something if that button just toggles the modes. Instead, I do two different fading moves of one entire LED strip, depending on which mode got enabled. And here’s the problem: That takes time.

See, the delay() function is frowned upon in the ESP world because it stops everything. The micro is told to just wait and not do anything, even if it could e.g. handle background WiFi stuff while stopping the main thread. Furthermore, if you stop the entire thing for too long, the software watchdog (WDT) kicks in and resets the machine. You can disable the software (not the hardware) watchdog for brief amounts of time, but that’s not the point of that feature, right?

So I switched from delay() to the millis() method, which is a bit more tedious (get the millis() time before, then do funky while() loops until some time has passed). Inside those loops, you can tell the ESP to do some housekeeping with the yield() instruction. Great. Except that doesn’t work with interrupts, and it took me an hour or two to realize.

Thing is: millis() does not increment during an interrupt. You get the millis() time when the interrupt fires, and that’s it. Now let’s do a loop while millis() is smaller than the point in time when the interrupt happened plus a few milliseconds…and that never finishes. Well, it does, as after a short amount of time the watchdog kicks in and resets everything. But it is utterly impossible to do delays that way.

Luckily there’s another system time function: micros(). Yes folks, that’s the very same thing with better resolution. Except it is actually better suited for the task, because micros() still works during interrupts! Now how do I do a tiny 3/4s intensity dip?

for (i = 1023; i > 256; i--) {
analogWrite(Lright1, i);
timenow = micros();
while (micros() < timenow + 500) {} timenow = micros(); while (micros() < timenow + 500) {} }

Big ol' for() loop with 700-something steps and doing two while() wait cycles of 500µs each inside. One could do one large while() cycle, but there are limits to that as well. Note that micros() overflows every so often, so there's probably some additional stuff that needs to be checked in order to make it long-term stable.

Boot modes
Uh yeah. Special boot modes are common in embedded systems, like those that I use at work. But I never realized that this is a thing on the ESP8266, too. I would have never guessed, but as I obviously had some problems with rebooting when the touch button is attached, I had to find out.

Touch is connected to GPIO 0, so that's not reliably in high state when power is applied. Well, it probably shouldn't, as it is only going high when touch is detected. Bummer. That still needs to be fixed in case there are unexpected reboots.

In summary: GPIO 16 (D0) is a bad choice, GPIO 0 (D3) is a bad choice, and now I do not have enough digital pins to run 5 LED strips, one temperature/humidity sensor, one PIR movement sensor and more than one touch button at the same time. I can get away with just one button for all lights, but I would have preferred two. More sensors will have to move to different ESPs that will be scattered around the apartment. The one with a display pretty much has to be in the living room near an existing 5V/9V supply, as that will not go well with batteries. The others...still undecided.
Or should I up the game and run an ESP32 with all the bells and whistles in the kitchen? Let me know...

That's it for now, final version with reboot-safe pin mapping and all of the code will follow in another blog post.

Leave a Reply

Your email address will not be published. Required fields are marked *