Introduction: Arduino Serial UART Scrolling Display Terminal Using a 2.2" TFT

Picture of Arduino Serial UART Scrolling Display Terminal Using a 2.2" TFT

I have been making projects based around a 2.2" TFT display which uses the ILI9341 driver chip, this display can be connected to and controlled by an Arduino UNO. As a bit of background reading you may find my instructable here useful.

During debugging of one of my battery powered Arduino projects I needed to have a portable terminal to look at the debug output being printed to the serial pins. To achieve this I built the small portable display terminal described herein.

The unique part of this project is that the sketch uses the built in hardware scrolling feature of the ILI9341 chip, this takes the processing burden off the Arduino AVR microcontroller and means that the display can keep up with serial text messages at 9600 baud.

The ILI9341 and GFX libraries featured in this instructable has been optimised for speed, some of the speed enhancing features use direct PORT access to the ATmega328 registers so it is important to use an Arduino board based on that processor chip. These speed improvements means that characters in the proportional font 2 can be printed to the screen at more than 1000 characters per second... which is a bit faster than I can read them!

Though the development has been done with a UNO, the final battery powered terminal will be based around the smaller Pro Micro, with a battery pack and 3 AA batteries this will be nice and portable.

Step 1: Connecting It Up

The connections between the display and UNO are not the same as my other instructable, for this project connect as follows:

  • UNO +5V to display pin 1 (VCC)
  • UNO +5V through a 56 Ohm resistor to display pin 8 (LED)
  • UNO 0V (GND) to display pin 2 (GND)
  • UNO digital pin 7 through a 1K2 resistor to display pin 4 (RESET), add a 1K8 resistor from display pin 4 to GND
  • UNO digital pin 9 through a 1K2 resistor to display pin 5 (DC/RS), add a 1K8 resistor from display pin 5 to GND
  • UNO digital pin 10 through a 1K2 resistor to display pin 3 (CS), add a 1K8 resistor from display pin 3 to GND
  • UNO digital pin 11 through a 1K2 resistor to display pin 6 (SDI/MOSI), add a 1K8 resistor from display pin 6 to GND
  • UNO digital pin 13 through a 1K2 resistor to display pin 7 (SCK), add a 1K8 resistor from display pin 7 to GND

It is important to include the 1K8 resistors to GND with this 2.2" display as otherwise it will not work. The 1K2 and 1K8 resistors are a "potential divider", acting as a logic level shifter so that the logic level at the display is reduced from 5V to around 3V. Pin 9 of the display does not need to be connected up.

Once programmed the Terminal can be connected to another Arduino to monitor the serial output:

  • Set the baud rate on the Arduino to be monitored to 9600 baud (or modify the Terminal sketch to match)
  • Connect the Terminal UNO GND to GND on the Arduino to be monitored
  • Connect the Terminal UNO Rx pin to the Tx pin on the Arduino to be monitored

Output text messages sent using Serial.print from the other Arduino to the Terminal will be displayed. At 9600 baud the Terminal manages to keep up with the data flow.

Step 2: The Sketches and Libraries

The libraries and sketches needed are attached as zip files. The Terminal sketch is included as one of the examples within the Adafruit_ILI9341_AS library.

The Adafruit_GFX_AS library attached contains some significant speed improvements that permit drawing font 2 characters at over 1000 per second, this is needed to keep up with with a full speed 9600 baud rate. This version of the library requires the use of the ATmega328 processor (as used on UNO) since direct PORT access is performed. To use the library with other processors, such as the ATmega2560, the sketch will need to be modified to the correct hardware SPI pins and the following line must be commented out:

#define F_AS_T

within the "Adafruit_ILI9341_FAST.h" header file located in the Adafruit_ILI9341_AS library folder.

No changes are needed when using the ATmega328 based Arduinos providing the pins listed above are used.

A speed demo sketch has been included here, this is the one I use to test the speed improvements, it is an adapted version of a sketch that comes with the Arduino UTFT library.

Step 3: Operating at Higher Baud Rates

In most cases characters don't get lost at 9600 baud but it is a good idea to increase the serial Rx buffer from 64 bytes to 512 or 1024 bytes, especially if higher baud rates are used. The sketch does not use much RAM so even a 1024 byte Rx buffer leaves RAM spare.

To increase the serial buffer space the method described here works well:

The TFT ILI9341 driver scrolls the display almost instantly, but it takes the Arduino about 13 milliseconds (ms) to erase a line on the display during the scroll process. In this time more characters may be arriving, so the buffer helps by storing the serial data until the ATmega processor gets around to taking more bytes out of the buffer.

The line erasure code in the sketch is optimised by storing the length of each line of printed text and then only over-writing that area, this means the worst case where 1 character is printed on every line it does not take 13ms to scroll and erase a line, but more like 1ms.

Step 4: Adapting the Sketch

Picture of Adapting the Sketch

Using the hardware scrolling feature of the display is not easy to understand, it is one of the more esoteric facilities the ILI9341 driver chip provides. The concepts of circular buffers and how the pixel y coordinate changes as the display scrolls will need to be understood, otherwise changes to the sketch are likely to give seemingly unpredictable results. If you are really keen then read the ILI9341 data sheet!

Just for fun I created a "Matrix" falling characters effect by scrolling the screen! Sketch is attached in case anyone may be interested... :-)

Step 5: The Future...

Any updates or bug fixes to the code will be posted here, so come back and check for updates if you are having a problem.


Dr_Acula made it! (author)2015-07-01

Brilliant project - thanks++.

Attached is a photo of your program dumping data from my arduino wireless mesh

This is using an APC220 wireless module on pins 2 and 3 of the arduino, and replace the serial object with softserial. With a few tweaks should be able to display your analogue meter project as well.

I'm using some cheap 5V to 3V modules from ebay instead of the resistors. I can feel a PCB coming on though - maybe with a CD4050 to do the level shifting, or maybe resistors.

Thanks for making these displays so easy to use :)

Bodmer (author)Dr_Acula2015-07-01

It's great to get the feedback and the photo of your setup.

I like your mesh project too, thanks for the link.


Dr_Acula (author)Bodmer2015-07-01

Hi Bodmer, thanks for the quick reply. Just to follow up my own thoughts, I did some research on the 4050 chip, and it appears that even though its pins are 5V tolerant when run from a 3V supply, you can't seem to use it for level shifting, as power flows through internal diodes on the chip die (parasitic power). I know there are other solutions out there - level translator chips, fets, even series resistors of around 3K3. But when designing a PCB, I've gone for your divider circuit exactly as it is though - simple, cheap, and I have the parts to hand. I sent off a PCB just now for the display plus APC220 radio module - your circuit is so cool it needs a PCB!

I don't know if you are stuck for clever ideas for instructables, but if you are, one nifty project would be able to graph values on these displays. eg a graph of values over 24h.

Cheers, James Moxham

Bodmer (author)Dr_Acula2015-07-02

The 4050 can be used for high to low voltage logic level conversion as the diodes at the input only clamp negative voltages (below GND/0V) and a typical spec is that for voltages up to +15V the logic input current will only be 1uA.

If the SD Card is also wired up then resistors can become problematic as some SD Cards like fast (<10ns) edges on the logic signals to work reliably. Having said that I have successfully used 1K2+1K8 voltage dividers with SD cards and they work OK as the effective source impedance is low enough.

As you say resistors are simple and cheap, I have 1000's that were donated to me free, so the cost to me is zero.

As it happens I will be developing some simple graph software to show historical information for things like temperature and barometric pressure so that may end up being a topic for an Instructable!

Thanks for your feedback.

intriq8 (author)2015-04-07

This is fantastic work! (this and your optimised libraries ) I haven't seen the scrolling feature of these displays used by anyone else. You mention the circular buffers are hard to use/understand, Is it possible that you could incorporate the functionality into your libraries, abstracted to a nice simple method like tft.Scroll(DIRECTION, DISTANCE); ? This would be incredibly useful. Can you scroll just an area? (Im guessing so, unless you're redrawing the title bar in this project..) if so, a function like scroll(x,y,h,w,direction distance), or one for setting the area, and one for scrolling it, would suit all purposes, and be greatly appreciated by many.

Your idea re RLE compression of fonts(and icons) is VERY exiting. I crave full hi-res font sets than fit comfortably on a 328.

Can't overstate how much I appreciate your work and the quality of your writeup.

Bodmer (author)intriq82015-04-08

Thank you for your kind comments. This Instructable is not a particularly popular one so I did not think there was much interest in adding the scrolling capability to a library! The displays are designed for use in portrait orientation so can only be scrolled up/down in that axis. A single area can be defined to scroll, it must span the width of the screen (in portrait mode) but can start and end at any point on the y axis.

The problem is the y coordinates change within the scrolling area so it is easy to get in a pickle when using this feature!

I did consider adding a scroll left,scroll right function to the library so pictures off SD Card can be drawn scrolling on and off screen in a wiping action. Other than a terminal or picture scrolling I haven't thought of an application for this hardware feature!

Thanks again for your comments.

padouet (author)2017-06-14

I try to transform your good job from ILI9341 for ILI9342 (320x240, inverted color, miroring text), but I can not.
Can you help me please ?

GerardoM1 (author)2016-03-17

Hello, I bought this TFT:

I would mount it on an Arduino Mega and recall the images uploaded to SD with a button ... can you help me?

Thank you and congratulations for the job!



Dr_Acula (author)2015-10-19

Some further experiments with the backlighting led. I'm not sure if this has a dropping resistor on the ILI9341 board and I suspect it may not. I measured the current by measuring the volts across a 1R resistor to the led pin and got a value of 200mV, ie 200mA. The display was also getting quite warm.

My guess is this is a white led with a forward voltage of about 3V and running it from 5V it may have a short life. I tried adding a 220R resistor in series - this would give about 10mA rather than 200mA. Certainly the display is not as bright, but it is running cold now.

100R would give 20mA which may be a better compromise for brightness vs life expectancy.

Your thoughts would be most appreciated.

Cheers, James Moxham

Bodmer (author)Dr_Acula2015-10-24

Hi, It seems that some of these displays come with resistors built in and others do not.

My display has a circuit on the back to switch power to the LED, I measured the LED current at 50mA, so for your display I suggest a value of 56R would be good.

Kenjutsu (author)2015-08-14

This is awesome, thank you for sharing. Would it be possible to adapt the program so that it works in landscape mode? I need to display longer lines.

Thank you!

Bodmer (author)Kenjutsu2015-08-20

Hi, unfortunately the hardware scroll feature only provides vertical scrolling in portrait orientation...

Kenjutsu (author)Bodmer2015-08-21

Thanks for the reply. It would have been cool though :-)

uhclem (author)2015-07-26

I ran into a problem with Adafruit_ILI9341_AS8::pushColors. I'm testing your ILI9341_draw_bitmap_v2.ino file from another instructable. The error from the compiler is:

ILI9341_draw_bitmap_v2.cpp.o: In function `drawRAW(char*, int, int, int, int)':
/Users/xxxx/ILI9341_draw_bitmap_v2.ino:398: undefined reference to `Adafruit_ILI9341_AS8::pushColors(unsigned char*, unsigned char)'
/Users/xxxx/ILI9341_draw_bitmap_v2.ino:405: undefined reference to `Adafruit_ILI9341_AS8::pushColors(unsigned char*, unsigned char)'
collect2: error: ld returned 1 exit status
Error compiling.

In the Adafruit_ILI9341_AS8.cpp file, you have commented out the function

void Adafruit_ILI9341_AS8::pushColors(uint8_t *data, uint8_t len).

The .ino file makes this call:

// Here we are sending a uint16_t array to the function
tft.pushColors(tftbuffer, tft_ptr);

tftbuffer is uint16_t:

uint16_t tftbuffer[BUFF_SIZE];

So there should not be an issue with the commented out public function.

This is with Arduino 1.6.5 and also Arduino 1.0.6, so it's probably not a GCC version issue.

Bodmer (author)uhclem2015-07-26

Hi, I hacked the library together rather quickly as I bought on of those UNO displays. Unfortunately I never got around to testing some functions like pushColors...

uhclem (author)Bodmer2015-07-29

I found the issue. You left off the * from the data in

void Adafruit_ILI9341_AS8::pushColors(uint8_t *data, uint8_t len)


// This is the byte array version for 16 bit raw images.
void Adafruit_ILI9341_AS8::pushColors(uint8_t *data, uint8_t len) {
uint8_t hi, lo;
while(len--) {
hi = *data++;
lo = *data++;

uhclem (author)uhclem2015-07-29

Ha! It may compile, but it will never work on my "mcufriend 2.8" display. The SD card is wired up completely wrong. I may be able to cut the traces and solder directly to the pins of the SD card slot and the 74vhc245 chip, but it's going to be micro-surgery.

Bodmer (author)uhclem2015-07-30

I looked at my mcufriend shield and I can see that the SD Card slot is wired directly to the UNO pins with no 5v<>3.3V level translation. This has to be a design error in the shield... as you say some hacking will be needed.

Alternatively you could just buy a cheap SDCard module interface on eBay and wire that in. (search for "Arduino sdcard module"). These modules have the level translator and a regulator to power the SDcard.

uhclem (author)Bodmer2015-08-02

The mcufriend shield level shifters have three unused pairs, so I modded the shield to use them. It works, but it was definitely NOT easy to solder wires directly to the TSSOP pads.

I was wrong on my code fix above. It may compile but does not work for getting data from the SD card to display. I had to go back to the old Adafruit TFTLCD library to get the SD bitmap test to work.

The parallel TFTs are best left to the Arduino MEGA, Due or other ARM boards.

Dr_Acula (author)2015-07-25

Technical question - does the LED pin on the display need a dropping resistor? I had an idea this would be on a schematic of the display but I haven't been able to find one, though I did see somewhere that someone mentioned a 15 ohm resistor. But maybe that was a different brand of display. Your thoughts would be most appreciated!

Bodmer (author)Dr_Acula2015-07-26

Whether a resistor is needed depends on the display being used. A picture of mine is attached. This has a transistor Q1 to switch on/off the LED, so the LED pin on the connector pin could be driven with a low current output logic pin to PWM the LED and hence control the brightness. Some displays do not have this transistor so I assume the backlight LED is driven straight from the pin. I have tried shorting out the transistor with a multimeter to measure the LED current, it was 50mA so I think my display must have some build in series resistor that cannot be seen and sets this current.

If you display does not have a Q1 transistor then I would measure the LED current with a series resistor to see if one is needed.

50mA through 15 Ohms would give a 0.75V drop.

Dr_Acula (author)Bodmer2015-07-26

Hi Bodmer, thanks++ for the reply. I can trace some of the circuit around Q1 - presumably there is a resistor to the base, and another resistor in series with the led? Anyway, if there are some displays without this transistor, what I might do is make a PCB with a place for a resistor to the LED pin, and if it is not needed then install a wire link.

As an aside, yesterday I got the code for the wireless mesh and combined it with the code for this terminal and also the code for your graphic ring display. Change one variable in the program from false to true to display as either text or graphics. That works really well. Had to comment out all but fonts except font 2 and font 4 to get it to fit in the arduino UNO but it fits with 2% to spare. Life is good :)

Thanks for making these displays so easy to use!

Bodmer (author)Dr_Acula2015-07-26

Yes R4 is the base resistir on my display. The LED series resisitor in not visibly but I guess there is one somewhere!

uhclem (author)2015-07-14

How hard would it be to adapt your library for use with 8-bit shields like this one:

By 8-bit I mean they use 8 pins for LCD data and SPI for the SD card.

Having used some of the other 320x240 TFT displays I assumed they all tended to use 8 or 16 bits, but it seems not.

I have a feeling it's easier to just order a different breakout that's all SPI.

Bodmer (author)uhclem2015-07-21

Hi, it would be possible to adapt the library. Is it the scrolling feature you are interested in or the fonts?

uhclem (author)Bodmer2015-07-21

Both really. It's sort of an uncommon shield configuration, as the Uno doesn't have that many pins and SPI is a better choice there. So, I understand if you feel it's not worth adding.

8/16-bit displays are better suited to the Mega, Due, or other 32-bit MCU.

I figured out ILI9325 scrolling (UTFT but the principle is the same for any library):

add to UTFT.h:
void scroll(int y);

add to UTFT.cpp:
void UTFT::scroll(int y)
if (y < 0 || y >= 320) return;
cbi (P_CS, B_CS);
LCD_Write_COM_DATA(0x0061, 0x0003);
LCD_Write_COM_DATA(0x006A, y);
sbi(P_CS, B_CS);

It's not really scrolling, it's moving where the display starts scanning in RAM. Because of this, if you really want to do something like scroll text, you have to blank the topmost line before scrolling and also keep track of where the bottommost line actually is based on your line offset.

Bodmer (author)uhclem2015-07-26

I have an 8 bit UNO shield and pulled together the attached libraries for it. These libraries may not work for you though. Might be worth a try.

The screen updates on these 8 bit shields is quite slow due to the way the 8 bits are split across the ports.

uhclem (author)Bodmer2015-07-26

Awesome! I was going to throw these 'mcufriend' shields in the trash out of frustration with their use of 8-bit interface in a UNO shield format, but now at least I have nice fonts and a modern library. They aren't really terribly slow, just not blazing fast. And of course they eat almost all the pins on an UNO. If you need more than a few 'buttons' you need to utilize the touchscreen.

Dr_Acula (author)2015-07-26

A few years ago I was playing around with 320x240 displays and came across what I think might be the tiniest font possible - 3x5 pixels (4x6 with spaces). Discussion here and there is a photo on that page of 80x40 = 3200 characters on a ILI9325 display (just a fraction larger than this Instructable's display - 2.4 vs 2.2"). It was a fun little project and was done on the Propeller chip - I don't know how hard it is to add fonts to the adafruit library?

Dr_Acula (author)2015-07-20

This project is so cool it needs a PCB, so I made some! See photos, has the resistors for the voltage divider, and also an APC220 module is on arduino pins 2 and 3. Thanks again for this great Instructable :)

Bodmer (author)Dr_Acula2015-07-21

That looks good, thanks for sharing.

matthiasl2 (author)2015-04-19

Thank you very much for this little code example, I successfully transfered it to STM32-Arduino. (We use a DMA "powered" library for the ILI9341, so ARM+DMA = really, really fast). The only thing I see is, when changing TOP/BOT_FIXED_AREA you get sometimes "ghost" lines as output (I think it's the old buffer, because the lines do not match 18/19 anymore) But not a big thing :)

Bodmer (author)matthiasl22015-04-19

Hi, I am pleased to hear you have found the sketch useful. There are a few 'magic' numbers in the code, the 19 is the number of 16 pixel high text lines in the scroll area, the 18 is simply one less than this number. I have used ARM processors and even written code in assembler to get optimum performance, but for the Arduino Instructables will be sticking with the UNO and Mega. In future I am going to be using the Teensy LC for a few projects but those are likely to have less general appeal to the Arduino community. TTFN

matthiasl2 (author)Bodmer2015-04-20

Yeah, so I guessed I was quiet right :) If you are interested in very, very cheap arduino compatible ARM's than grab one (or a bunch..) of those "maple mini" clones on ebay or better aliexpress for about USD 3.5 (free shipping) and join our project at:

Meanwhile the project is really advanced and 98% "arduino 1.6x compatible"

Bodmer (author)matthiasl22015-04-20

Will definitely follow up on those links. Thanks.

J3J3 made it! (author)2015-02-27

Have you plan to do an instructable for the touch ?

Bodmer (author)J3J32015-03-01


I do not have a touch version of the screen but if I do get one then I will see what can be done. Adafruit do, I think, have libraries that support the touch screen.

It is great to hear that your screen is working and thanks for the image.

J3J3 (author)Bodmer2015-03-03

The touch work with this library :

J3J3 (author)2015-02-26

Thank you so much !
My TFT works now

About This Instructable




More by Bodmer:Arduino - TFT display of icons and images from FLASH memoryArduino - TFT display of bitmap images from an SD CardArduino analogue 'ring' meter on colour TFT display
Add instructable to: