Hello everyone,
I wanted to share with you my experiences with the Waveshare 4.3'' ePaper display - and how to connect it to an ESP8266 Arduino. I had a simple project in mind that should do the following:
- Show the weather forecast for the next few hours
- Run on batteries for at least one week
There are only a few ePaper displays out there that are ready to be interfaced with an Arduino controller. The by far cheapest option with a medium sized display and good resolution is the Waveshare 4.3'' ePaper display. It has a 800x600 resolution (230dpi) and can display up to 4 colors: light gray, medium gray, black and white. I was able to get it for around 50€.
For the wifi enabled Arduino board, I went with the Adafruit Feather Huzzah with integrated ESP8266. This board is compatible with the Arduino IDE, has a wifi chip integrated and is well documented. I got it for around 15€.
Both the display and the Arduino board can be operated at 3V, so I simply purchased a 1400mAh lipo battery for 6€ to power both of them. I'll probably need to upgrade the battery, but that's the one I had available.
In total I paid around 75€ for the whole project. Here is how the three components look like when connected to each other: [u]http://imgur.com/a/60wYP[/u]
The wiring is pretty straight forward. I found an example on the Waveshare Wiki on how to connect to an Arduino Uno and adapted it to the Adafruit Huzzah:
3V | GND | RX | TX | D2 |
---|---|---|---|---|
Red | Black | White | Green | Yellow |
Remark: We are also connecting the RST Pin to Pin 16 on the Arduino in order to wake it from deep sleep. I found out about this on the Adafruit Product page.
As you might be able to tell, we are interfacing the display via UART. This created some problems down the road which I will cover at the end of this post.
Now on to the coding. Fortunately there is a library available for the UART serial communication with the display available here: [u]https://github.com/sabas1080/LibraryEPD[/u]. Among other functions you can put the display in sleep mode, set the drawing color, draw individual pixels, draw primitive shapes like lines and circles or display text with a font that is integrated on the display controller.
For my project, I didn't want to use simple text to show the weather forecast and the built in shapes weren't much use either. I wanted to display icons, nice fonts and even some graphs for rain probability and so on.
Because the Arduino board is not capable of rendering a full 800x600 4 bit image (remember, the display has 4 colors) I'm doing the rendering on my Webserver. If you are interested in this part I can share more information and even my code with you. For this post let's just say I am pulling weather information from forecast.io, display it in a HTML layout and render it to a png picture with PhantomJS.
PNG files are super small but also compressed. My Arduino board does not have the power to decompress a png so we'll need something simpler: the PBM format. You can look up the format on Wikipedia but for a black/white Image it's really simple. Each pixel is represented by one bit that can either be 0 or 1. 8 pixel get packed into one byte of data. That's 800 x 600 x 1bit / 8 = 60kbyte per image. If we wanted to use the four colors the display can handle this would be 4x the data, so 240kb. Nothing a good wifi couldn't handle but as it turns out more data than you can easily transfer over UART. We'll cover that in a moment.
In order to convert the png created by PhantomJS to 1bit PBM I use ImageMagick. I have a cronjob running on my server that pulls the weather information every 15 minutes and creates the PBM when finished.
The Arduino now only needs to connect to the internet every 30 minutes or so and download the PBM. Of course the Arduino does not have enough storage to save the image for later processing. Instead we are going to stream it from the internet byte by byte and create draw commands for the display while we do that.
We could do this one byte at a time, so 8 pixel on block, and make a draw_pixel command for each pixel that comes in. That however would be super slow. In order to tell the display to render one pixel at a given location, our library has to create a serial command that tells it to do so. This command is 13 bytes long for a single pixel draw command. Example:
"A5 00 0D 20 00 0A 00 0A CC 33 C3 3C 88" -> draw pixel at position 10 (x), 10 (y)
For a full black image we would have to call 800 x 600 draw_pixel commands, each taking 13byte traveling over serial from the Arduino to the display. That's a whopping 6mb of data. The UART communication of our Arduino and display run at 115200baud (bits per second) so around 14kb/s. At this speed it would take 440 seconds to get the image across.
Of course, our image won't be fully black. It's going to have plenty of whitespace as well. After resetting the screen to white, we only need to transmit black pixels. That reduces our data significantly but not enough. To really cut the data we are going to send draw_line commands instead of draw_pixel commands. The draw_line commands takes 17 bytes because it needs to specify 2 x/y coordinates. Even if we can batch two neighboring pixels like that, we'll save 9 Bytes. A whole 800px wide black line gets compressed to 17 Bytes instead of 10kb.
A checkerboard pattern would ruin our benefits, but in a real life scenario this is going to save our ass. The image you see above takes around 10 seconds to travel from the Arduino to the display. That is still a lot but since we're only updating the screen every 30 minutes it's not a big deal.
I have attached the whole Arduino code for this project to this post.
All in all I'm pretty happy with the result but there a few things I still need to do/find out:
- Sometimes a few lines won't be displayed at all
- Find out how long the battery will last
- 3D Print a case to mount it on the wall
- Find a way to use the four colors available to me
Especially the last point is giving me headaches. Setting the draw color takes 11 Bytes. It would be super inefficient to do that within my line compression. Multiple passes for each color would make more sense but since I am streaming the image from the internet I can't do that right now. I would have to store the image on a SD card and then read from that one time for each color.
Speaking of the SD card, the Waveshare display does have a SD card reader to store bmp images. Maybe I can find a way to share the SD card reader between the Arduino and the display so I can save the image from the internet to the SD card and then simply tell the display to show the image when it has finished downloading.
I hope this was a help to someone out there and if you do have any ideas of improvement I'd be happy to hear them.
ePaperWeather.ino (2.31 KB)