Graphics library for NodeMCU (ESP8266), ESP32 and serial+parallel TFT displays

Update on 15/8/18: The TFT_eSPI library can now be loaded via the library manager in the Arduino IDE.

Update on 11/3/18: The libray now supports 8 bit parallel TFTs with an ESP32. For example UNO style TFT displays made by mcufriend.

UPDATE on 22/2/17: The TFT_ILI9341_ESP library has now morphed into a new library that supports multiple display drivers. The new library TFT_eSPI can be found here. See also post #63. The library currently supports the ILI9341, ILI9163, ST7735 and S6D02A1 based TFT displays with SPI interface.

I have ported my TFT_ILI9341 graphics library to the NodeMCU. A copy can be found on GitHub.

The library support use of the hardware SPI interface, example connections for a NodeMCU board with a 2.2" 320x240 display are:

Display SDO/MISO to NodeMCU pin D6 Display LED to NodeMCU pin VIN (5V) Display SCK to NodeMCU pin D5 Display SDI/MOSI to NodeMCU pin D7 Display DC to NodeMCU pin D3 Display RESET to NodeMCU pin D4 Display CS to NodeMCU pin D8 Display GND to NodeMCU pin GND (0V) Display VCC to NodeMCU pin VIN (5V)

Performance is quite reasonable with a 40MHz SPI clock when running the graphicstest sketch:

Benchmark Time (microseconds) Screen fill 161973 Text 49593 Lines 320578 Horiz/Vert Lines 17827 Rectangles (outline) 13985 Rectangles (filled) 333103 Circles (filled) 187493 Circles (outline) 210565 Triangles (outline) 70579 Triangles (filled) 212988 Rounded rects (outline) 88425 Rounded rects (filled) 429151 Done!

The library is based on (but does not use) the Adafruit_GFX library. Extra fonts have been added that are encoded for fast rendering. Text and numbers can also be right/left/centre justified to assist in creating neat displays.

Examples are included which show how to use the features.

Report any bugs here.

Amazingly the the ILI9341 based display I have seems to work OK with a 160MHz SPI clock (not recommended though!) ...

If you want to try this change the CPU frequency in the IDE to 160MHz and set the SPI frequency in the library's User_Setup as follows:

define SPI_FREQUENCY 160000000

Results then are that the ported UTFT_demo completes in 1.2s and graphicstest completes in 1.12s:

Benchmark Time (microseconds) Screen fill 81895 Text 28177 Lines 180730 Horiz/Vert Lines 9173 Rectangles (outline) 7297 Rectangles (filled) 168457 Circles (filled) 101677 Circles (outline) 121312 Triangles (outline) 39352 Triangles (filled) 111733 Rounded rects (outline) 50062 Rounded rects (filled) 219447 Done! Total = 1118586

Ah-ha. I generally find that ARM M3 or M4 go faster than the TFT on 8-bit parallel interface.

Are you really going at SCK = 80MHz? One byte is going to take 100ns.

18.3.4 Display Serial Interface Timing Characteristics (4-line SPI system)

TFT Instruction write cycles are 100ns. Read cycles 150ns.

18.3.1 Display Parallel 18/16/9/8-bit Interface Timing Characteristics (8080-Ⅰ system)

Write cycle = 66ns. Read cycle is 450ns.

In practice, I have found that read cycles are often slower than the datasheet suggests. Yes, ILI9341 is a lot faster than some "other" models.

David.

@david_prentice

Yes, it dawned on me that the SPI frequency is set to 80MHz even though the CPU clock has been upped to 160MHz.

So the results in post#1 are with 160MHz CPU and 80MHz SPI clock. Trying to set the SPI clock to 160MHz actually only sets it to 80MHz.

Very aggressive tests indicate that 80MHz SPI clock is marginal and pixels etc can get "lost" at that frequency, so 40MHz SPI is the highest my setup works reliably in this situation.

First I got a Christmas Tree, now I get even a BIG Christmas Present! Thank you.

Would it be easy to adapt it to support 9 bit SPI transfer? There seem to be quite a few TFTs without C/D pin.

Merry Christmas and a Happy New Year to all of you.

Jean-Marc

I don't understand how and where the member variable:

SPIClass *_SPI;

gets initialized.

Somehow it works, and it works extremely well, speed is impressive.

I tried to enhance the SPI class of the ESP8266 package. But adding additional member values does not work, I got exceptions on access to these member variables. So I created a new class SPI9Class, and modified a copy of TFT_ILI9341_ESP to use this class:

SPI9Class * _SPI9;

but it looks like the pointer gets set to the standard SPI instance of SPIClass.

Strange.

The library needs a license file. The code says it is a derivative work from the adafruit code. If so, then it must retain the original copyrights and license of that code. I'd recommend putting the removed license file back in the repository as well as the source code file copyright and license information back at the top of the source modules. Even better would be to do a fork from the original adafruit code and then apply your changes to your repo. That way everything is properly tracked back to the original work. It also might create the potential to do pull requests or merges back to the original adafruit code.

--- bill

bperrybap: The library needs a license file. The code says it is a derivative work from the adafruit code. If so, then it must retain the original copyrights and license of that code. I'd recommend putting the removed license file back in the repository as well as the source code file copyright and license information back at the top of the source modules. Even better would be to do a fork from the original adafruit code and then apply your changes to your repo. That way everything is properly tracked back to the original work. It also might create the potential to do pull requests or merges back to the original adafruit code.

--- bill

That's why I like the concept of "Help Yourself Software", that I try to introduce.

It is more open than Open Software, but with its true meaning, give away and take, without obligations.

Jean-Marc Zingg, 27.12.2016

@bperrybap Here's a link to the Adafruit library that this is derived from, no license file has been removed. The original Adafruit MIT licence text has been retained in the main .cpp and .h files, see header for location.

It appears to me that this library is a mushing together of two adafruit libraries (Adafruit-GFX-Library and Adafruit_ILI9341) with some tweaks & modifications to create a single library that does not depend on either original library. If so, it makes it a derivation subject to both library's copyrights and licenses.

Your original post says:

The library is based on (but does not use) the Adafruit_GFX library.

but given the similarity of the code and and a few of the comments in the .cpp and .h files like:

This library has been derived from the Adafruit_GFX library and the associated driver library. See text at the end of this file

and

// Fill a triangle - original Adafruit function works well and code footprint is small

it appears that it is based on (as in uses code from Adafruit_GFX library) but then does not depend on that library.

Was any code taken from Adafruit-GFX-Libary ? (It does look like it)

If so, the library is missing the required copyright notice from the Adafruit-GFX-Library .cpp file. It would also be helpful to provide links back to the repositories for the original code.

Regardless of the library's heritage, it is always best to have a clear license file included in the top level directory. Adafruit-GFX has one, but Adafruit_ILI9341 doesn't (I'll go file a bug to get LadyAda to correct that).

--- bill

Uff, this is ugly. I don't give credit to Bjarne Stroustrup for every line of C++ code I produce, or have copied from somewhere.

I thought the whole Arduino World is based on Open Software. This openness is what makes it live and evolve, so rapidly.

Lets continue in this attitude.

Jean-Marc Zingg

I don't think it is getting ugly at all. s/w is considered private copyrighted work that is protect against copying unless otherwise noted; so, some sort of copyright and licensing must be stated and granted if it is to be shared and used by others.

Open source is not the same as FreeWare. Open source usually comes with copyright and licensing requirements/restrictions that allows it to be copied and shared and often does not allow it to be used in certain ways. So you can't just copy open source software and use it anyway you want. That is the price you pay for being able to start with someone else's s/w code vs having to start from scratch. People have different views on the s/w that they create and the license they choose allows them to choose how their s/w can or cannot be used by others.

In this case it looks like it might be easy to clean things up by adding some missing notices and copyrights. The tricky part will be if the the library is using code from both GFX and and ILI9341 since they have different licenses. It may be that the combined work will have to become BSD vs MIT since I think BSD is a bit more restrictive, assuming the MIT licensing actually allows that. (Although I just created an issue in the Adafruit ILI9314 library asking them to clarify the licensing)

btw, I faced a some licensing & copyright issue when I created my arduino hd44780 library. This was mainly because the Arduino team (Including Adafruit) was so sloppy with their licensing and copyrights. This was particularly an issue on the LiquidCrystal library. I pushed hard on both Arduino and Adafruit to clean up their copyrights and licensing on their code & libraries and for the most part they have. In my case I was having to dance around a bit since they originally didn't have any licensing or copyright notices for LiquidCrystal when I started hd44780. They had a very vague and general license file for Arduino code but that license file was never included with the Arduino IDE distribution - it was only in the repository - so nobody that had the IDE would ever be able to see it - which does not comply with the GPL/LGPL requirements. My eventual solution was to look at their intent and went with that until they updated their licensing. I now also include a licenseInfo.txt file that provides a detailed description of the licensing and the history of the licensing of the original sources. You can see in my repo here: https://github.com/duinoWitchery/hd44780

I am a big believer in s/w copyrights and licensing and believe that ALL s/w should be very clear as to its licensing and the lineage of the code, including arduino libraries and sketches.

One of the worst licenses of all is Creative Commons CC-BY-SA 3.0 as that license is not compatible with anything but itself. It was never intended to be used on a work that is piece of something else. So legally you can't ever create a public/shared project that uses it in something like an adrduino project (even if the CC-BY-SA 3.0 work is just a library) since CC-BY-SA 3.0 is not compatible with other licenses like GPL/LGPL code due to some of the attributions required by CC-BY-SA 3.0 that are forbidden by GPL/LGPL. CC-BY-SA 4.0 was created to work around this by allowing the work to be re-licensed as GPL/LGPL.

--- bill

@bperrybap

Ah! Missed the BSD reference, it seems that Adafruit have different license and license versions sprinkled through their various libraries.

I've have updated the repository.

The NodeMCU has an ESP8266 at heart which makes a networked sketch for the library a must, so here is a starter sketch that will (after some more tweaks) find it's way into the examples

If anyone has some cool ideas for another networking sketch that needs a TFT then post on this thread.

Work on this library will cease now for a while until I can persuade Alexa (i.e.and Amazon Echo) to talk to and read some networked sensors, that may take some time as she is proving to be a bit obstinate :-)

bodmer: Ah! Missed the BSD reference, it seems that Adafruit have different license and license versions sprinkled through their various libraries.

They have been and continue be sloppy with their licenses. And in some past cases they have violated licenses and copyrights by claiming a different license for their derivative work that is simply not allowed. It has gotten better in recent times, but as you have seen, there are still some "gotchyas".

bodmer: The NodeMCU has an ESP8266 at heart which makes a networked sketch for the library a must, so here is a starter sketch that will (after some more tweaks) find it's way into the examples

Have you seen the WifiManager? It is really cool. You might consider adding the WifiManager to the code so that it is usable "out of the box". i.e. users attached to it to set the ssid & password rather than have to hard code them in to the sketch code. But that does add more complexity and the addition of another library so you may not want to go that route. - but is very cool.

--- bill

@bperrybap

Had a look at WifiManager, briefly hoped it would allow me to upload sketches remotely (OTA) and securely, but that is not the purpose...

Have been playing with a fork of the JPEG decoder library here for the ESP8266, it runs quite fast and a neat feature is that it uses the SPIFFS library so JPEGs can be easily stored as file copies in the FLASH chip using the SPIFFS data upload tool. SPIFFS saves a lot of tedious mucking about with arrays or SD cards.

Also found the ESP8266 can be programmed to emulate a WeMo mains switch which can then be controlled with on/off voice commands via the Amazon Echo or an Android AP.

bodmer: @bperrybap

Had a look at WifiManager, briefly hoped it would allow me to upload sketches remotely (OTA) and securely, but that is not the purpose...

WiFi manager is just to set up the ssid and password so you can do it with a browser vs having to re-compile the code. But there is a s/w bundle for doing OTA updates as well. You have to include some extra code in your code just like with wifi manager but it is pretty cool.

If you google around you can find information on tutorials on Arduino OTA updates.

--- bill

I have updated the TFT_ILI9341 graphics library for the NodeMCU (and ESP8266 variants). A copy can be found on GitHub.

The 49 Adafruit_GFX Free Fonts have all been added plus two new examples.

The free fonts (as wells as the other resident fonts) can be drawn onto the screen with a background colour and can be drawn relative to a chosen datum, so it easy to right justify text or centre text about a point on the screen (the Adafruit_GFX library lacks these abilities). The examples show how this works.

I just tried this out, It works perfectly. Thanks for your hard work and for sharing it!

I have made some performance tweaks to the library, latest version is on GitHub.

With a stock 320x240 display the NodeMCU can now execute all the timed "graphicstest" functions in 0.91s (totalled) and a compatible UTFT_Demo finishes in 0.92s.

Considering these NodeMCU boards sell for only $3 (and have Wifi built in) they are exceptional value if a small powerful processor with wireless connectivity is needed. As they operate at 3.3V they can be wired straight to the display. Typically the backlight LED benefits from the extra brightness if powered driven from 5V. (Note: some variants of the NodeMCU provide the USB 5V on the Vin pin which is handy).

I have a higher performance ESP32 on order so when the Adruino core libraries are in place for that chip I will see if I can adapt the code for that 240MHz processor though the performance is more dominated by the SPI frequency than the CPU frequency and 80MHz is the limit with the 2.2" ILI9341 display I have.

These are the times reported by the "graphicstest" for different CPU and SPI frequencies:

CPU 160MHz, SPI 80MHz
Benchmark                Time (microseconds)
Screen fill              81880
Text                     17484
Lines                    110823
Horiz/Vert Lines         8784
Rectangles (outline)     6683
Rectangles (filled)      168311
Circles (filled)         79768
Circles (outline)        65434
Triangles (outline)      25870
Triangles (filled)       100298
Rounded rects (outline)  30082
Rounded rects (filled)   212347
Done! Total = 0.907055 s


CPU 80MHz, SPI 80MHz
Benchmark                Time (microseconds)
Screen fill              82850
Text                     26730
Lines                    162672
Horiz/Vert Lines         10251
Rectangles (outline)     8617
Rectangles (filled)      170695
Circles (filled)         121724
Circles (outline)        97799
Triangles (outline)      37727
Triangles (filled)       137253
Rounded rects (outline)  43787
Rounded rects (filled)   236197
Done! Total = 1.135728 s


CPU 160MHz, SPI 40MHz
Benchmark                Time (microseconds)
Screen fill              157817
Text                     20258
Lines                    130615
Horiz/Vert Lines         15264
Rectangles (outline)     10667
Rectangles (filled)      323955
Circles (filled)         100700
Circles (outline)        74626
Triangles (outline)      30953
Triangles (filled)       151077
Rounded rects (outline)  36637
Rounded rects (filled)   384025
Done! Total = 1.435837 s


80MHz, 40MHz SPI
Benchmark                Time (microseconds)
Screen fill              161944
Text                     29219
Lines                    179507
Horiz/Vert Lines         16988
Rectangles (outline)     12709
Rectangles (filled)      332815
Circles (filled)         142200
Circles (outline)        106428
Triangles (outline)      42014
Triangles (filled)       189318
Rounded rects (outline)  50262
Rounded rects (filled)   414494
Done! Total = 1.677213 s


160MHz, 20MHz SPI
Benchmark                Time (microseconds)
Screen fill              312993
Text                     26787
Lines                    173766
Horiz/Vert Lines         28570
Rectangles (outline)     18852
Rectangles (filled)      642021
Circles (filled)         145685
Circles (outline)        98588
Triangles (outline)      41397
Triangles (filled)       255954
Rounded rects (outline)  52092
Rounded rects (filled)   735513
Done! Total = 2.531470 s

80MHz, 20MHz SPI
Benchmark                Time (microseconds)
Screen fill              315007
Text                     35582
Lines                    221512
Horiz/Vert Lines         30092
Rectangles (outline)     20778
Rectangles (filled)      646544
Circles (filled)         186471
Circles (outline)        129292
Triangles (outline)      52337
Triangles (filled)       292740
Rounded rects (outline)  65204
Rounded rects (filled)   761167
Done! Total = 2.756191 s