AT24c32 for the ESP8266 (C code)

The AT24C32 (24c32) is a small eeprom that comes on popular, dirt cheap, RTC boards (but of course is also available separately). Using the datasheet it’s easy enough to get working on the ESP8266. All the AT24C series chips work the same, except for an extra address bit in the 1Mb version, so the example code below can be used with any model (see the note in the header file about the 1Mb chip).

The chip basically has two operations:

  • Read from current position.
  • Write to specified position.

Technically there is no read from specified position. To do this you must make a dummy write, as the datasheet refers to it. This basically means starting a write operation, which begins by setting the address, then not sending any actual data (or an I2C stop). Instead you perform an I2C start again and perform a read as normal.

When reading you can read as much data as you like. When writing you can only write up to 32 bytes at a time. A write operation is restricted to a single 32 byte page (or part of one). You do not need to start at the beginning of the page. Regardless of where you start in a page, if you continue to write after reaching the end of the page you will wrap back to the start of it and continue writing there. This means you need to keep track of how much you are writing and where the next page boundary is.

The example driver code attached, written for the C API of the official Espressif SDK, handles all the issues above. You can write as much as you like, wherever you like – it will perform multiple write operations across pages as you’d expect. You can can also read from specified locations and the driver will perform the dummy write for you to set the starting address. You can still read from the current position and write inside a looping page if you like. You should be able to drop this code straight into your ESP8266 project, set the I2C address in the header file (according to your address pins) and start reading and writing to your eeprom with ease.

Code now on GitHub: https://github.com/raburton/esp8266

Feeding the watchdog on ESP8266

I wanted to do some waiting in a tight loop on the ESP8266, but that can lead to a watchdog timeout and a reset. I figured there must be a way to stop that but there isn’t anything in the documentation. Grepping the libs I found a couple of candidates and, after trying them all out, I found exactly what I was looking for – slop_wdt_feed. Declare the following in your header:

extern void slop_wdt_feed();

Now just call it in any long running loops or functions and it’ll keep the  dog off your back. Note this isn’t necessarily good practice!

At time of writing google returns exactly one result for this function name (just a listing of functions in the library) so I don’t think this is currently widely know.

Update: As of SDK v1.1.1 (possibly a little earlier) this no longer works. You can now use these (from Pete Scargill’s blog):

extern void pp_soft_wdt_stop();
extern void pp_soft_wdt_restart();

Mutex for ESP8266

I don’t know what can interrupt user code on the ESP8266 or what might do the interrupting, the documentation is lacking in that area, but something seems to be causing me a timing condition. With a really low time out on an NTP call I seemed to end up in the receive and the timeout code at the same time. The timeout kills the connection and the receive disarms the timeout, so in theory only one should ever be run, but what happens if the timeout occurs between the receive code being called and it actually disarming the timeout? Will the timeout interrupt the receive, kill the connection and then allow the receive code to continue? That is what it looked like, so I did the obvious thing and searched the API for some kind of mutex. No joy, but the whole API isn’t documented so I grepped the SDK libraries for any function name that might be relevant, still no joy. I’ve come to the conclusion the SDK does not provide any such functionality, so I had to get my hands dirty with some assembler…

Armed with a copy of the Tensilica Xtensa Instruction Set Architecture reference manual I tested out the S32C1I operation. The GCC compiler doesn’t know this instruction or the special register it uses (SCOMPARE1), so I wrote something similar and patched the right opcodes into the assembled object file. Unfortunately trying to write to this register reset the device, so it looks like the ESP8266 does not support the ‘Conditional Store Option’, darn.

So, I’m not sure how I ended up at it but I found some Linux kernel code with what appears to offer the solution and forms the basis of my esp8266 mutex code. The code is easy enough to use – declare a mutex_t, call CreateMutux against it, then GetMutex and ReleaseMutex as required.

Code now on GitHub: https://github.com/raburton/esp8266

Simple timezone support for the ESP8266

I suggest you run your RTC on GMT (aka UTC, if you’re French). If you’re just using it for data logging or the like you can probably run entirely in GMT. If you want to display it to a user you probably want to do that in localtime. The ESP8266 C API doesn’t have support for timezones, so you’ll need to add your own. Adding or subtracting a few hours is easy enough, but applying daylight savings time (DST) during the summer is a little more tricky. A quick google search found a few examples but they all assumed the adjustment happened at midnight, so I’ve created an extended version that changes at 2am, as happen here in the UK. This is an example and will need to be modified if you’re not in the UK. Ideally the function would parse a string with timezone specific definitions rather than having hard coded rules, if you want to do this hopefully this code will serve as a starting point.

Code now on GitHub: https://github.com/raburton/esp8266

Real time clock (DS1307/DS3231) for the ESP8266

Cheap DS1307 "TinyRTC" board

Continuing the time theme, I’ve recently been playing with the ESP8266 wifi soc and the common real time clocks (RTCs) DS1307 and DS3231. It seems to be popular to program these devices with LUA using NodeMCU firmware, but I can’t work out why. LUA seems to be pretty awful, but I guess if you’re new to programming it’s an easy way to get started and you aren’t likely to notice how odd LUA is. Real men use C (or assembler, if they’re showing off).

(TL;DR – skip to the bottom of the post for the attached C code.)

So you can program for them in C, using the SDK supplied by the manufacturer (which is what NodeMCU itself is built with). Actually they supply two SDKs: The first is the one they are actively pushing for development and offer bug bounties for. The second one (FreeRTOS based) seems to use more standard APIs and appears to have a few more features, but it’s future isn’t clear. It’s a bit confusing, but I’ve decided to stick to the first SDK.

So, back to the RTCs… These are easily obtained from China for about 53p from AliExpress, shipped to the UK! Amazing when you consider you can’t even send a letter inside the UK for that price. You basically have a choice of two boards, one based on the DS1307 (pictured above) and one with the DS3231 (below). The DS1307 has less features than the DS3231 and the boards, generally labelled as TinyRTC, seems to be pretty flaky – my advice would be to avoid them. Lots of people find they don’t work without modification. Mine works (after mods), but only with power applied, it doesn’t maintain it’s timekeeping on battery.

Cheap DS3231 board

The DS3231 is more accurate, has more features and the boards seems to work fine. As they’re the same price I’d go for these every time. Note that both boards are designed to be used with a rechargeable lithium cell battery, and if you put in a normal CR3032 it will try to charge them and they might explode! On the DS3231 board you can simply remove the diode to disable the trickle charge circuit. Also of note, both boards come with an AT24C32 i2c eeprom on board, which might be handy for something.

So why do you need a RTC? The device has wifi so why not just get the time from an NTP server? Well you might not want wifi enabled all the time, especially if you intend to run the ESP8266 from battery. Getting the time from the RTC is quicker than NTP,  and easier to do synchronously. You can also do other neat things, like use the alarm on the DS3231 (not available on the DS1307) to wake the ESP8266 from sleep, it could then connect to the network check in with a server to send data/ receive instructions, then go back to sleep again. The DS3231 can even tell you the temperature.

I found a few simple examples of using these RTCs for LUA, but nothing much for the C API, so I’ve written drivers for both. I think they are pretty comprehensive in their support for the features of the chips and the code is thoroughly commented so you should be able to work out how to use/ modify it easily.

Code now on GitHub: https://github.com/raburton/esp8266