Debugging with an ESP8266 NodeMCU V3 dev board is like pulling teeth

I will preface this entire post by saying that this is only my experience and only confirmed for this specific board. I haven’t had the opportunity to test any others, so it truly could be a faulty board and joke’s on me for not replacing it immediately.

UPDATE: I tried out another NodeMCU board and the smaller footprint ESP-01 and posted an update at the bottom

The board

I recently bought a dev board (pictured above) for the ESP8266 chip, which I’d guess is the most widely used IoT microcontroller. It runs the ESP-IDF firmware as a base and comes preloaded with the NodeMCU firmware which is built off of ESP-IDF and written in Lua. I didn’t utilize the NodeMCU high-level FW, but wrote all of my code based off of the ESP-IDF SDK. I was pleasantly surprised by the simple documentation and APIs within the SDK.

Programming and debugging my code was a whole different story. First things first, I am breakpoint-and-step-through debugger through and through. Even when print-statement debugging is vastly the norm, like in Python, I still prefer to drop an import pdb; pdb.set_trace() in to be able to examine variables, memory, and single-step through the code. The ESP8266 does not support GDB debugging out of the box. There exists a gdbstub ‘component’ (ESP-IDFs version of libraries), but after fighting to get it working for 3 full days I gave up. Looks like I’d be stuck with purely printfing when I wanted to debug.

Okay though, not the end of the world. It slows down the debug cycle and is a bit annoying to deal with, but still manageable. Which brings me to my next point.

Code flashing

My ESP board would throw tantrums. For reasons I cannot begin to fathom, it would randomly refuse to receive any commands from the host computer tools. By randomly, I mean I would successfully flash a build, make a tiny change in the code (maybe adding another print statement), and on the next flash I would get the dreaded timed out waiting for packet header. From searching around, I couldn’t find any consistent answer to why this would happen. My FLASH button onboard did not seem to do anything at all. I tried manually tying GPIO0 to ground, putting a decoupling capacitor between multiple of the power output and ground pins, and any remotely related solution on a random forum, to no avail. Half the time it would start working again after a few unplug/plug back into the laptop tries, but the other half I got so frustrated that I would just give up. Miraculously, the next day it’d almost always work. Needless to say, that’s quite a hit to productivity.

Sketchy serial connection

The final anvil-sized straw that broke the camel’s back (i.e. my temper) was how inconsistent a serial monitor connection to the board was. I gave up on the default monitor used with make monitor after maybe two days, it just never worked. I added my own make target for debugging with miniterm.py that would attach to the correct /dev/cu.usbserial-XXXX port at the right baud rate. After flashing code and then running my monitor, one of 4 things would happen:

Heaven forbid my code throw an exception. That would result in any of the first three options from above, but no indication that it was different than normal code running. I might get lucky and have a partial core dump printed at some point, but only with register values and a backtrace solely in hex, no function names.

The best part about this was that disconnecting and reattaching the monitor would result in different behavior almost every single time. This means my debug cycle went something like this:

I will pay 4 times as much for a working one next time

Like I said at the beginning, I truly hope this is just a board issue and I can buy another for ~$6 and throw this one in a dumpster. I completely acknowledge that wallowing in this problem is entirely of my choosing, but it does feel very nice to be able to rant about it even if no one reads it. If it’s not just a specific board problem, then I hope someone comes across this one day and at least can resign themselves to the horrid dev cycle they have ahead of them rather than trying to fight it like me.


My better-informed opinion

Even though I ended up getting the code that I needed working on the first board, I ordered a second just to make sure I wasn’t going crazy. I still stayed in the relatively inexpensive range, but I made sure I ordered from a different seller. When it arrived, I hooked it up and was immediately met with the same error trying to connect to it to flash the already-working code. After some fiddling and finagling that I’ve learned from the first board, I got it talking with the toolchain on my laptop and the binary uploaded.

Once again, I immediately met with the exact same serial connections issues: blank terminals, garbled or frozen data, and a wholly inconsistent experience. Unless for some reason there’s something wrong with this laptop specifically (I also recently reinstalled macOS to clean out some cruft so it would have to be hardware), I’m going on record to say that the cheap NodeMCU boards for ESP8266 chips have some really bad design when it comes to their serial communication. The only way I’d use one of these boards in the future is if I had code I had somehow determined was fully operational, and I would load it up and forget about it. No chance I’m trying to do any debugging with the serial port.

I also finally bought a simple USB to serial TTL board since I’ve had a few of the small ESP-01 boards in my possession for a while. These also center around ESP8266 chips, but with the majority of the external microcontroller functionality stripped away. There are only 8 pins broken out, with only 2 usable for GPIO if you also want serial transmission (which you kind of have to keep if you want the opportunity to program it again in the future).

The smaller, more reliable ESP-01 dev board

The smaller, more reliable ESP-01 dev board

Honestly, this should have been my next step before trying another NodeMCU board but oh well. I got the simple programming circuit hooked up (you have to wire in your own reset and flash buttons to pull the pins correctly) and lo and behold, it loaded up the code perfectly on the first try. I opened up a serial monitor and once again, perfect and continuous serial output.

I’ve been using this ESP-01 as a part of a breadboard circuit and also socketed on a custom PCB and the flashing / monitoring has been flawless. Moral of the story? Don’t waste your time or money on a LiLo NodeMCU board unless you’re desperate for lots of GPIO, and even then I’d probably still choose something else.