PWM problems identified

People have been reporting problems with the PWM function on the ESP8266 causing resets for months, with others claiming rock-solid reliability for virtually identical applications.  There’s been a lot of back and forth on the site regarding whether this was a real issue, or just a problem with layout/decoupling or perhaps just inadequate power supplies.

Unfortunately, the SDK API Guide from Espressif hasn’t helped matters too much with, as Pete Scargill pointed out, things like the “duty” parameter being documented initially as an 8-bit integer (page 178, “pwm_init()”) and then later as 32-bit (page 179, “pwm_set_duty()”), guaranteed to trip up the unwary (ie:- me!).

Finally, user “anszom” seems to have identified the issue causing the real problem, which is a 32-bit integer containing the address of an interrupt handler routine.  Apparently the address is not tagged as being 4-byte aligned (which it needs to be), which means it’s a matter of luck as to whether the value is aligned, or not, when your application is compiled.

Thankfully, “anszom” also provides a manual fix for the linker script to work around this problem until Espressif provide an update to the SDK (and, hopefully, the documentation).

More SDK Strangeness

Recently I read a post on the ESP8266 forum where the author was attempting to use the powf() function call, but running out of memory during compile.  That sounded very familiar to me, as I’ve been experimenting with the DS3231 RTC clock module recently (inside an MQTT application) and found that if I tried to use any of the standard “time.h” library functions, such as gmtime() or localtime(), my compile would also bomb with the infamous “iram1_0_seg” out-of-memory errors.  This would happen no matter how many struct tm‘s worth of memory I freed up in the code, to the point where using time.h calls from within MQTT seemed to be an unworkable combination, even with the modifications for .literal. and .text. storage.

I don’t have a “silver bullet” answer to this problem (and, so far, according to the thread referenced above, neither does anyone else), but the latest SDK (V1.2 as of the time of writing) does seem to help, although not enough for most people, including me (see page 2 of the thread).  In the end I modified a cut-down version of an Arduino C++ time.h lookalike (mem’s Arduino Playground Time.h) to add the functionality I wanted and still have memory left over for other things.

Kudos to mem for the original Arduino Time.h and my apologies for the terrible,  slash-and-burn mess I’ve made of it.  Gomen-ne!  My exceedingly ugly versions are available here for anyone who is really, really desperate:-  ESP8266/time.h_hacks



Another day, another SDK fubar…

So, you’ve upgraded to SDK 1.*, you do”make” in your application directory and…  the world explodes (which is pretty much normal when you do an SDK upgrade).  Sigh!

CC modules/config.c
CC modules/wifi.c
AR build/app_app.a
LD build/app.out
/opt/Espressif/ESP8266_SDK/lib/libmain.a(app_main.o): In function `user_uart_wait_tx_fifo_empty':
(.irom0.text+0x340): undefined reference to `user_rf_pre_init'
/opt/Espressif/ESP8266_SDK/lib/libmain.a(app_main.o): In function `user_uart_wait_tx_fifo_empty':
(.irom0.text+0x45c): undefined reference to `user_rf_pre_init'
collect2: error: ld returned 1 exit status
make: *** [build/app.out] Error 1

Luckily this one is easy. It is now mandatory that you have a user_rf_pre_init() function somewhere in your code and, if you’re recompiling an existing project which was created with a pre v1.* SDK, you most probably won’t have one.

The solution is simple. Just create a dummy, empty function in the top of your user_main.c (or whatever file your user code happens to be in). The function should look something like this:-
void user_rf_pre_init(void) { return; }

Now re-run make and you should get a clean compile (please note that I didn’t say  it will actually work …just compile!).

SDK I2C Code. Today’s “Duh!” Story.

This entry is here as a helper (hopefully) for any other poor soul banging their head against the wall of Espressif’s I2C “master” implementation.  <TL;DR Spoiler>  When using the i2c_master.c code supplied with the IoT SDK, you must call i2c_master_gpio_init() before any other I2C functions in your code (and, obviously, your GPIOs need to be correctly defined beforehand).

ESP8266-03 with DS3231 module and DS18S20
ESP8266-03 with DS3231 module and DS18S20

I’ve been trying for days to get I2C working with an ESP8266-03 and a DS3231 RTC module (the el-cheapo, middle-kingdom one with the AT24C32 memory on the same PCB).  This was starting from scratch with I2C on the ESP8266 for me (I’d previously put together a trio of PICs talking to each other over I2C without too much trouble, so I naively thought that the DS3231 should be a doddle).

Hardware wise, I had the DS3231 hanging off a 4v5 supply and the ESP8266 running from a 3v3 linear regulator, supplied by the same 4v5 source.  The reason for this is that I intend to make the unit into a self-contained, battery-powered data-logger and I want the DS3231 to be able to control the power to the whole shebang through a small MOSFET driven from the alarm output.  The I2C bus is run through a MOSFET-based level shifter to ensure that the ESP8266 pins aren’t over-driven.

As normal, on the software side I started off with TuanPM’s totally excellent MQTT package (and if you’re not using this already, you should be!) as a starting point for my new project.  I simply added the i2c_master.c and i2c_master.h from the Espressif (IoT) SDK into the tree, modified the GPIO settings in i2c_master,h to point to the ESP8266 default I2C pins (GPIO2 for SDA and GPIO14 for SCL), added a tiny calling function into user/user_main.c and compiled.  I flashed the ESP-03 with the RTC module attached and… nothing!.   Okay, quick check… Duh, my SDA and SCL connections on the stripboard were the wrong way round.  Okay, resoldered the jumper wires, double checked with the meter and we’re good to go.  And… nothing.  Again!  My output data resolutely remained at all zeros, but the DS3231 calls (using Richard Burton’s ESP8266-ready code) didn’t throw any errors.  Double duh!

I certainly had no reason to doubt Richard’s code.  He’d only just recently done this himself and has a blog about his ongoing projects that is definitely recommended reading for anyone playing with the ESP8266.  I did have reason to suspect that the hardware might be a touch on the dodgy side though, as the DS3231 module already has a pull-up resistor pack on the I2C lines and the MOSFET level-converter also uses 10k pull-ups to both the 4v5 and 3v3 sides.  Thus the 4v5 side had the DS3231 pull-ups and the level-converter pull-ups in parallel and I suspected that the bus might not be being driven hard enough to a logical “0” on that side because of it.  Back to the soldering station, off with the SMD pull-up pack (and, while I was at it, off with the red-LED and the coin-cell “charging” diode, too…  I don’t need an LED sucking extra current from the 4v5 battery pack and the charge diode, as has been noted in lots of places on the ‘net, is a positive danger when using a normal, non-rechargeable CR2032 in the RTC).  Reconnected and yes, I know you’ve already guessed it, bugroll again.  Sigh!

Back to the soldering station.  Ripped out the original RTC module and slapped in another one.  Bugroll, repeated.

At this point I was considering popping one of the RTCs into a PIC-based board, just to check that I didn’t have two dodgy ones, but on reflection, decided that the quickest and easiest next test was to use another I2C implementation instead.  I had already seen Nathan Chantrell’s ESP8266-enabled OLED project and remembered reading that he’d used Zarya’s I2C implementation to drive the display.  Nathan’s blog is another one of my favourites and he certainly doesn’t seem to get things wrong as often as I do, so off I went to GitHub again and pulled Zarya’s I2C code.  It’s basically just two files, the i2c.c and i2c.h, which replace the i2c_master files from the SDK implementation.  First glitch… that missing word, “master”.  Richard’s code calls the i2c_master_whatever() functions, a couple of which are not compatible, argument wise, with Zarya’s implementation.  Okay, “Cheat” is my middle name, so taking the laziest course of action I just rolled half a dozen, one-line wrappers to encapsulate Zarya’s code into i2c_master_whatever() functions.  The only minor change was to make the ack and nack separate function calls (Zarya’s code sensibly uses a single ack function and passes either a 0 or a 1 depending upon whether it’s actually an ack or a nack).  Flashed and… Hurrah!!  Three cheers!  Two pints of Guinness and a packet of crisps, please!  The module burst into life and sent real data.

Mostly it sent really bad data, but at least not just those boring old zeros any more.  The memory module obviously wasn’t writing data at all and the RTC was returning some really weird values for everything except the seconds value, but the internal temperature sensor in the DS3231 was sending sensible, accurate temperatures (instead of the previous, monotonous “Bit chilly in ‘ere, innit”).  Hmmm, verrry interestink!

Unfortunately though, this still left me in something of a quandry.  Now I knew my hardware was capable of working to some extent, but was the bad data a result of my inept programming of the wrappers, or did I still have a hardware issue of some sort, or (perish the thought) was Zarya’s I2C implementation incompatible with the SDK (which has been updated several times since Zarya originally published his code) or Richards DS3231 driver?  Okay, there are a couple of other I2C implementations out there and one of them, by EADF, implements a replacement i2c_master with a significantly easier method of specifying the GPIO defines than the original, SDK master (EADF is the guy who also brought us “easygpio”, so this should be no surprise).  Off to GitHub again.  “”. Download. change out the files. compile, flash and… bugroll again.  Aaaargh!  Now I’m starting to doubt my own sanity.  I trust EADF like a brother.  He’s never let me down where the ESP8266 is concerned.  What’s going on here?

I spent more time looking at the two implementations (Zarya’s and EADF’s) side by side to try and work out what was different and it eventually struck me that there was one extra function in EADF’s code which, while missing from Zarya’s, was also present in the SDK version —  i2c_master_gpio_init(). So I finally check the much maligned and utterly useless SDK documentation and, yes, there it is, labelled as “Function: set GPIO in i2c master mode“, nothing else.  Looking at the code itself, it’s obvious that the function does pretty much nothing, other than the all-important job of setting the GPIO pins to the correct mode for I2C (and Espressif have even helpfully commented their code with “//open drain” on those lines), before calling the I2C initialize function.  So, the documentation, despite it’s shortcomings, isn’t so utterly useless after all (just bloody useless!) and a quick edit of my calling code to replace i2c_master_init() with i2c_master_gpio_init() caused it all to spring back into life again… this time with sensible data coming from the RTC and the AT24C32 memory chip, too.  Yip-finally-ee!

Bottom line (same as the top one)…  When using the i2c_master.c code supplied with the IoT SDK, you must call i2c_master_gpio_init() before any other I2C functions in your code (and, obviously, your GPIOs need to be correctly defined beforehand).  Failure to do so will cause hours of frustration (this seems to be my standard state when trying to do almost anything with the ESP8266 nowadays) and will also cause your significant other (and possibly the neighbours) to complain about the unrestrained use of Anglo-Saxon and unwarranted cat kicking.

The right hand giveth….

First, the good news… Espressif have released an updated (as of June 1st 2015) documentation package on their web site:-

My initial impressions (from the datasheet for the ESP8266EX chip and the hardware guide) is that the English language versions are very much improved from the early releases and definitely worth your time to download.  The datasheet especially now has several very clear, easy to understand tables, showing the pins by function.  This very much helps to reduce confusion as, for instance, if you want to use I2C you only have to scroll down to that section and deal with a table listing two pins (all you’re interested in).

Espressif ESP-WROOM-01 Module (Photo courtesy of Espressif)

The new revision of the datasheet certainly seems to have fewer instances of ambiguous wording and is generally much easier to read.  There seems to be a lot more useful information in there, too (but that may be because I’ve failed to keep up with the earlier published revisions).  The hardware guide has quite a bit of useful information on layouts and PCB design issues, as well as information on the ESP-WROOM-01 (that fabled beast with the normal-sized header pins).

And next, the not quite such good news… Espressif have released SDK 1.10 (and patches to it, already) and, as we are all now becoming increasingly and frustratingly used to, have broken just about everything in sight.  Again.

Initial reports are that existing code fails to compile with errors referring to the PULLDWN register settings for GPIOs.  These have apparently gone away completely.  The Espressif posting on the change states that, “Note: There are no pull-down functions on GPIO pad now, so we should never use these registers.  Add external resistance to pulldown the pin.“.  It is unclear to me whether the wording refers to all GPIO pins, whether the pull-downs were there originally and have been removed in later versions, or whether they were there originally and have just decayed over time.  The updated datasheet for the ESP8266EX chip says specifically, “Each GPIO can be configured with internal pull-up (except XPD_DCDC, which is configured with internal pull-down),…“, so there almost certainly is at least one pull-down, maybe, possibly, perhaps.

As usual, Pete Scargill is doing a great job of disseminating the latest information on what’s happening on the issues around this latest SDK (and his site is always worth a regular visit for ESP8266-related updates, anyway).