ESP8266 Debug

Light bulb momentsSo – I’ve learned quiet a lot in the past few days..

Watchdogs on the ESP8266

Since Espressif SDK 1.01 or thereabouts, watchdog timer operation seems to have changed – delays of any length even in init()  are out – or so it would seem? So I got onto Stanza Wang, the business Development Director of Espressif Systems who as always was most helpful and got a quick reply from one of the development engineers on this subject.

It turns out they’d prefer the use of these function:

void pp_soft_wdt_stop();    // close software watchdog
void pp_soft_wdt_restart();    // reset software watchdog

So for example use void pp_soft_wdt_stop();    before your delay and then use void pp_soft_wdt_restart(); 

Delays are not a good idea full stop if you’re using background processes such as WIFI – but you might not be!

Maximum use of FLASH

Thanks to Richard Burton, it is now possible to start programming right down at the bottom of memory – leaving LOADS of FLASH free (though iRAM continues to be a precious commodity – someone needs to do a “how to” on saving RAM. I’ve blogged on this elsewhere with a link.

Debugging and those Pesky Messages

ESP12-EAnd now…. debugging.  I have had it up to here with odd strange output from the Espressif SDK. TuanPM implemented a simple macro called INFO for outputting to the serial port for debugging and general information – but there was no way to control what came out – worse, the SDK kept throwing status information out which wasn’t wanted – so we’ve written our own.

If you’re outputting info to the serial line the best way as we’ve found is to disable Espressif’s messages using


in your user_init function. This turns off all output to the serial – unfortunately that means that os_printf() no longer works – so you’re dead in the water. We wrote our own printf equivalent for the serial port and we call it iprintf().

You don’t HAVE to make it this complicated… if you want you can simply, having disabled os_printf() use ets_printf() where you want text to the serial port - but we wanted more control.

Here are a couple of definitions – bit masking – you might choose to make your own.

This is in our DEBUG.H file

extern int enable_debug_messages; // Needed by debug.h

#ifndef USER_DEBUG_H_
    #define USER_DEBUG_H_

    #define DEBUG 1
    #define INFO 2
    #define RESPONSE 4

#endif /* USER_DEBUG_H_ */

So somewhere in the init, set a variable (see above) to the level of debugging you want….

//int enable_debug_messages = INFO | DEBUG | RESPONSE;
int enable_debug_messages = INFO | RESPONSE;


And here is the function to use that…

void ICACHE_FLASH_ATTR iprintf(uint16_t debug_type, char *fmt, ... ){
  char buf[128]; // resulting string limited to 127 chars inc arguments – change if you like
  va_list args;
  va_start (args, fmt);
  ets_vsnprintf(buf, sizeof(buf), fmt, args);
  va_end (args);
  if (debug_type & enable_debug_messages) uart0_tx_buffer(buf,os_strlen(buf));

So basically you have a function you can call with a debug level..

iprintf(INFO,”Starting up the program”);

You can pass parameters to it just as you would with printf….

iprintf(RESPONSE,”The answer is %d”,answer);


Depending on the level of debugging you want you could expand on this greatly.   Someday we’ll convince Espressif to optionally turn off that start-up 78k rubbish and we’ll have totally clean output – for now this is a great start.

A shame you often have to hunt around for this stuff!


18 thoughts on “ESP8266 Debug

  1. Another uglier sollution to the debug print is to define a macro like so:

    #define debug_print(...) system_set_os_print(false); \
    os_printf(__VA_ARGS__); \

    Not as nice, and the system might print during the call to os_printf, but simpler in lines of code.

    1. It certainly is simpler Martin and thanks for that - I really struggle with that whole passing variable arguments thing so you've given me another tool to play with! However your solution has an issue - it turns the system printing back on when it is done - which means we're back to getting all sorts of system info coming out - hence in my version we disable normal system messages full stop. I also wanted a variable message sytem.

      So in theory - one might replace my code with something like..

      void ICACHE_FLASH_ATTR oprintf(uint16_t debug_type, ... ){
      if (debug_type & enable_debug_messages) ets_printf(__VA_ARGS__);

      What do you think.. would that work? But in practice - the compiler will have nothing to do with __VA_ARGS__ so for now that's a non-starter 🙂

      (I tried ##__VA_ARGS__ and that's not having it either.)

      1. Got a few minutes. The __VA_ARGS__ is a preprocessor thing, it is done differently when working with source processed by the compiler.

        My actual code is more like:

        //Macro for debugging. Prints the message if debugging is enabled.
        #ifdef DEBUG
        #define debug(...) system_set_os_print(true);
        #define debug(...)

        If ets_printf works regardles of how the state of system_set_os_printf, you might delete both of the system_set_os_printf lines, and replace os_printf with ets_printf. Then if you define DEBUG somewhere, debug("This is a debug message..."); will get printet, if DEBUG is not defined, nothing is printet. I will try this when I get home, this might be the final piece of the puzzle, I needed to shut up the ESP8266 SDK in a graceful way. But you have to turn on C99 support, flag is: -std=c99.

        Have a nice day, I will report back, if I get around to testing this before you do. 🙂

        1. And I'm back, sorry last one, until I can test things at home.

          To turn on C99 support, find the line in your Makefile, that reads CFLAGS = (-stuff -more -stuff), and at -std=c99 just after "=".

          1. Sadly Martin - that is already in there... and it still won't entertain __VA_ARGS__ symbol __VA_ARGS_ could not be resolved.

            CFLAGS = -w -Os -ggdb -std=c99 -Wpointer-arith -Wundef -Wall -Wl,-EL -fno-inline-functions \
            -nostdlib -mlongcalls -mtext-section-literals -D__ets__ -DICACHE_FLASH -D_STDINT_H \
            -Wno-address -DESPFS_POS=$(ESPFS_POS) -DESPFS_SIZE=$(ESPFS_SIZE)

      2. I've made a macro that does about the same as the code in your post, it goes like this:

        #define IPRINT_LEVEL INFO

        enum debug_level

        #define iprintf(dlevel, ...) if (dlevel >= IPRINT_LEVEL) ets_printf(__VA_ARGS__)

        The level is set in IPRINT_LEVEL, I've uploaded an example on github, but it is just another way of doing what you are doing. I work in Linux, but hope that the code will build on Windows as well. .Thank you for a good blog, that I'm reading with much joy.

        Github link:

    2. No matter what I do Martin - the compiler won't accept __VA_ARGS__ and I do have compiler flag
      -std=c99 set.

      CFLAGS = -w -Os -ggdb -std=c99 -Wpointer-arith -Wundef -Wall -Wl,-EL -fno-inline-functions \
      -nostdlib -mlongcalls -mtext-section-literals -D__ets__ -DICACHE_FLASH -D_STDINT_H \

    3. After ALL of that, I realised that _VA_ARGS__ only works in MACROS - and so I went through and replaced my solution with yours and...

      Out of .TEXT space.

      Sometimes you can't win. I'm desperately close to the limit on .text space (0x7c26) despite using ICACHE_FLASH_ATTR just about everywhere.

      When I crack the memory use, I'll return to this - meantime my solution does the job - but thanks for that Martin.

  2. Thanks to SSH, I look active in class, when in reality I am having fun with a shell at home. It works for me, just tested it, and you are right ets_printf works regardless of system_set_os_printf setting. I don't know exatcly what goes wrong on you end, but the code you postet earlier is processed by the compiler (second gcc pass), and variadic stuff works differently. What I do is use the preprocessor, that turns this into C code, in the first pass of the gcc compile phase. is GCC's info on variadic functions, that are different from the macros I'm using. I have used them years ago, but I can't wrap my head around them just now, which is why I did it with macros, which seems a lot easier to me.

  3. Where are the pp_soft_wdt functions? They don't show up in the SDK docs for 1.1.2, are they hidden?

    1. Hi Bill. Neither (unless I missed something) do the ETS_ functions appear in the docs - but they work. The short answer is - Espressif advised using these and not to use the earlier watchdog function (which ALSO unless I'm mistaken are missing from the docs).

      1. I built a modified MicroPython with the delay function surrounded by calls to pp_soft_wdt as you described, and sure enough it works fine now, no more crashes for long delays. I don't know what the maintainers of MicroPython will think of this hack - they'll probably say that it's a hack 😉

  4. Hi,

    I try to use them in SDK 1.1.2 without success.

    my file contains this;

    extern void pp_soft_wdt_stop(); // close software watchdog
    extern void pp_soft_wdt_restart(); // reset software watchdog


    // My time consuming operation
    void ICACHE_FLASH_ATTR motor_rotate(
    Direction direction,
    unsigned long microSteps,
    unsigned long pulseDelayHighuS,
    unsigned long pulseDelayLowuS,
    unsigned int mstep1,
    unsigned int mstep2,
    char* microstepResolutionDescription )
    ETS_INTR_LOCK(); // If i do not disable interrupts i get memory exception

    ... delay that make up more than 5 seconds


    when disabling interrupts i get a "beacon timeout" on my debug output....

    Any suggestions?

Comments are closed.