It’s been a while since my last post, but I have been working hard on improving the WeatherStation Color. Read here the engineering challenges I had to overcome and how I solved them
The first version WeatherStation Color was a big success and I got a lot of feedback of hackers all around the world who enjoyed putting it together. While this was great there were still quite some issues which I wanted to resolve. The first issue was the flickering of the screen when you would change the screen or update the time. As an additional side effect there would be sometimes pixel artifacts close to the time where I had not been careful enough to delete the previous image. The second frequently mentioned problem was that the icon downloader which downloaded the icons from my web server sometimes would fail to actually download images to a plethora of reasons. And the third problem was that my implementation was a bit lazy when it came to daylight saving time (DST); I just ignored it and assumed the user would be OK to reprogram the WeatherStation once the DST would change. In this post I will focus only on the first problem, the flickering.
Getting rid of the flickering was the problem which consumed the most part of the last few months. I decided to write a whole new library to address the issue. In order to understand why I thought this step was necessary we first have to look at the problem I tried to solve. After all you might think that even wasn’t such a big issue!
The Adafruit GFX library which I used for the first version of the WeatherStation Color writes every pixel you alter directly to the display, in my case a 240×320 pixel TFT screen based on the ILI9341 chip. While this is reasonably fast it is not fast enough to keep your eye from seeing an in-between frame when you delete the screen before drawing the next one. Let’s say you draw some weather icons to the screen you want to remove all traces of the old icon before you draw the next one. So you clear the screen and fill it for example with black. Once this operation has finished you continue by drawing the new icon. Since all operations are directly drawn to the screen your eye can see a very short phase where the whole screen was black. You see a flicker! So how could this problem be addressed?
It turns out that I wasn’t the first one to face this issue. This problem is usually addressed by drawing first to a memory-online (virtual) screen. Once all drawing operations of a iteration have been completed you write everything at once from the virtual screen to the physical screen and your eye will not see the clearing of the screen as a separate step. This virtual screen is often called “frame buffer” or “double buffer” and it has one big drawback: it consumes a lot of memory. When I first set out to rewrite the OLED library I wrote for the classic WeatherStation I quickly realized that the available heap memory of the ESP8266 would not be enough to accommodate a frame buffer with 240x320x2 = 150kb. Each pixel would require 2 bytes of memory since the ILI9341 has a 16 bit color scheme. 5 bits for red, 6 bits for green and 5 again for blue would together build up for the 16bit color information. Internally the ILI9341 uses a palette to expand this 16bits to the full color range available on its TFT screen and this brought me to the solution of my memory problem. If I could somehow reduce the memory consumption per pixel to a few bits the ESP8266 would have enough RAM to hold my frame buffer!
Reducing the bit depth per pixel is commonly implemented with color palettes. Instead of describing the pixel colors in values of red, green and blue you use a limited set of colors and give each of this color a alias number. So when changing the color for drawing you don’t say r:127,g:55,b:255 as you would to in a HTML file. You just tell the graphics library to use color 5 from the palette. Which color represents index 5 is defined in a data structure called the palette. Before the graphic library writes the buffer to the screen it looks up the real color for each index and replaces the index with the RGB value. This is actually a very old trick. I just recently read this article by Aaron Bell which nicely explains how the Commodore C64 and other 8bit computers used this trick to get 16 colors onto your CRT screen and how some games used fast color switching to create colors beyond the hard wired palette. If you are interested please read his article. I can recommend it!
So now I had my concepts together: frame buffer plus a palette color scheme would solve the flickering problem and still leave enough memory for the program code and the weather information.. Well, it was still a long journey from the concepts to a actually working implementation. I didn’t just want to write a graphic library which could only be used with one type of display. I wanted to write a versatile and easy to use library which would use some kind of hardware abstraction layer (HAL) to separate drawing routines from the hardware commands. It should also be possible to adjust the bit depth (or bits per pixel) depending on the used display or availability of heap memory.
So I had to do some heavy bit math in order to get it all right. The first real demo was a cube rotating with ~17 frames per second (fps) and using the frame buffer with 16 colors or 4 bits per pixel (2^4 = 16):
I think this is quite impressive for a micro controller! If you like to try it out it is part of the MiniGrafx library as a demo.
Once again I thought: almost done, now I “just” have to port the old WeatherStation Color code to the new MiniGrafx library. Turns out that I hadn’t realized how bad the WeatherStation library, which fetches the weather data from Wunderground, was in respect of memory consumption. The WundergroundClient class had become a big monolith which would reserve a lot of memory no matter if the application would use all available methods.
I realized this after my first attempts to run the WeatherStation color. Even before reaching the setup() method the ESP8266 would crash with a strange memory access error message. After a while I figured out that a part of the ESP framework tried to allocate memory and couldn’t do that because the frame buffer had already allocated too much. So I had not only to reduce the color palette from 16 colors to 4 but also re-design the Wunderground client. I broke it up into one class per method (current weather, forecast, astronomy and alerts) and more importantly changed the way the fetched data was stored. The old WundergroundClient class allocated all memory when it was instantiated for all methods. It stored the data as members in the class. After the redesign you have to allocate the structs in your own code and then pass in the objects by reference. This way the application code can control much better how much memory it can and needs to allocate.
I am quite happy with the new version of the WeatherStation color! Even if it only has enough memory to display four different colors (black, white, yellow and blue) it looks very colorful. Sadly I had to kick out the photos of the moon phases and replace it with a font based approach. The moon phases are now icons rather than photos but I think that is not a huge loss for the weather station. On the plus side the WeatherStation Color can now display a 6-day forecast on a side scrolling slider (or carousel), which is quite cool (IMHO).
The ESP8266 WiFi Color Display Kit
When I first published the WeatherStation Color many of you approached me since they had problems to find the right display. To make things easier for you my friend Fred helped me to put together a starter kit containing a Wemos D1 Mini Pro (with 16MB!), a ILI9341 with resistive touch screen and a PCB to connect the display and the Wemos module. I am very happy to offer you this kit for a reasonable price in my shop. It will be shipped from a warehouse in Dongguan (Perl delta in China) to almost all countries in the world. By buying the kit you are also supporting further development of the WeatherStation and other applications running on this platform.
Touch screen demo
A ridiculous amount of coffee was consumed in the process of building this project. Add some fuel if you'd like to keep me going!