Close

Progress

A project log for One Tube Nixie Clock

It is time to take my nixie hardware testbed from the breadboard stage to something that doesn't have wires sprouting everywhere.

paul-andrewsPaul Andrews 12/12/2017 at 15:030 Comments

I am waiting for some parts to arrive before I can start laying out the board. The design is basically done, except for some tweaking I have been doing for things such as power sources, connector layouts, pin breakouts etc.

In the meantime I have been polishing the software using my breadboard prototype. Here are some of the things I think are worth sharing:

Development Framework

As you probably know, I am using an ESP8285 - this is an ESP8266 with 1M of on-board flash - so it reduces component count and frees up a couple of extra GPIOs. I use the ESP8266 arduino development framework. A good resource on this framework is the ESP8266 Arduino Core docs. The arduino development framework is kind of like the Java of the hardware world. It has many, many libraries that a developer can leverage, rather than having to roll their own for every different processor out there. Developing for this framework also means that my own code is more portable.

The ESP8266 boards are fully supported in the Arduino IDE, however I use Sloeber- an Arduino Eclipse plugin. It makes me way more productive.

Key Libraries

From the outset my aim was to leverage the WiFi connectivity of the ESP boards. I have developed a web interface for Nixie clocks before, and I used the ESPAsyncTCP library, rather than the libraries from the ESP8266 core for Arduino. In particular ESPAsyncWebServer. I wrote a small ESPAsyncHttpClient to go with it. Asynchronous communication places less load on the ESP8266 - it allows it to get on with other tasks, while the communication is happening in the background. I came across this library via fauxmoESP - a library that allows you to mimic WeMo devices, so you can control things using Alexa - it uses the ESPAsyncTCP library!

One problem that needs to be solved when using WiFi is the initial connection to a router. I had previously rolled my own solution to this before, but it is a finicky business and after I had implemented it I came across WiFiManager, which displays a captive portal when a device tries to connect to the ESP, and allows you to choose which router you want to connect to. However, it uses the synchronous web server and I wanted to use the asynchronous web server. Fotunately someone had already implemented that too: AsyncWiFiManager

Interrupt Handling

I implement various fading effects by turning power on and off rapidly to selected digits. I do this using interrupts to guarantee the timing - the ESP8285 has many other things to do, so the loop() method isn't called at consistent enough intervals to rely on that for timing. I had implemented this a long time ago using timer0 - the handler issued SPI calls (using the Arduino SPI library) to set the state of the HV5523, telling it which digits should be on and which should be off.

It all worked fine until I integrated the ASyncWifiManager and started serving up web pages. Then it started crashing randomly. When the ESP does that it spits out a stack dump as a bunch of numbers, so the first thing to do was to convert these to symbols. If you're using the Arduino IDE there is the EspExceptionDecoder for doing that. For me, I needed to use the EspStackTraceDecoder command line tool.

What it told me was that I was always crashing with an illegal instruction error. WTH? After some googling I found that various system calls can cause flash to be unmapped from the address space. If an interrupt happens at that time, you will get an illegal instruction error. The solution is to move your interrupt handler to RAM using the ICACHE_RAM_ATTR tag. For example:

void ICACHE_RAM_ATTR myHandler() {
// Do some stuff
}

However:

  1. Everything that is called from that interrupt handler also has to be in RAM.
  2. There isn't much RAM.
  3. SPI methods are not declared with that tag, so they are all in flash.
  4. There isn't much RAM (see 3).

So. I created my own copy of the SPI library and added ICACHE_RAM_ATTR to every method I called, and every method those methods called, etc, etc. All was well. At least until I ran out of space in my IRAM segement during a compilation because my interrupt handler had grown too big. Then I went back and figured out which methods I really needed to call and just tagged those. So far so good.

I guess you should also watch out for FStrings - strings stored in flash. This wasn't a problem for me.

Memory

The ESP8266 memory is split up into several chunks. At least:

  1. Flash
  2. Instruction RAM (aka IRAM).
  3. Data RAM (aka DRAM).

You likely know how much flash you have. However, you will also have 32K IRAM and a separate 80K of DRAM. Of that 32K of IRAM, plenty is already taken up by various libraries, so you might only have a couple of K to play with.

SPIFFS

The ESP Arduino core comes with a SPIFFS library - a flash-based filing system. This is where I store the files I serve from my web server. This saves on me declaring a whole bunch of string literals and also means that I can develop my web pages as separate files. In fact I wrote a quick node.js server to serve them up locally so I could test them.

Using SPIFFS also helps (dramatically) to cut down on strings being stored in DRAM.

One drawback of doing this is that I can't serve dynamic web pages - i.e. I can't fold variables into the document as it is being served. Enter, websockets.

Web Sockets

Web Sockets is a fairly recent protocol that is now supported by every major browser. You can use it to do AJAX, but it also has the advantage of being able to push data from the server to the client. To get around the problem of not being able to serve dynamic pages, I serve static pages that then open a web socket back to the server and request initialization data. The server then just formats that all up as a JSON string and squirts it back down to the web page.

Because the server can push data from the server to the browser (or all browsers) I also use it to dynamically update the browser with values that are changing on the ESP8266.

JQuery Mobile

I use JQuery Mobile to render my pages - my intention is that I use my smart phone to control and configure my clock. I might as well use a toolkit that is designed for that platform.

One possible issue with this is that my web pages reference external libraries - i.e. libraries stored on servers in the internet. This might cease to work if the specific versions I reference go away, or if there is no internet connection at the time I want to use it (and for this last reason, I can't use it in my captive portal). I could look at storing minimized versions on the ESP8285 itself. I'm not sure how big they are.

Another possible issue is if JQuery Mobile ceases to be developed and it becomes incompatible with current versions of browsers. It is showing every sign of not being developed any more. The last stable release was in 2014. The last alpha was in Jan 2017.

Some Tooling

As part of my everyday development with my ESP8285 board I needed to be able to do a few things repetitively:

Sloeber doesn't perform these functions for you (the Arduino IDE does). The key to getting this to happen cleanly with Sloeber is being able to access the configuration for the board you are developing for and find where the necessary tools are. This information is all stored in .settings/org.eclipse.cdt.core.prefs, so I wrote an ant script to parse it and implement the tasks above. You can find it in my Git repo.

Discussions