Using the ST7735 TFT Display with the Arduino Nano

In earlier Arduino based projects I’ve used two different monochrome LCD screens, but these were quite limited in their display capabilities. In the drop controller I went with the two line by 16 character LCD Keypad shield. This was very easy to integrate into the build physically, but did lack a little finesse. In the slightly more recent photogate trigger I used the Nokia 5110 display. This has a resolution of 84 by 48 pixels and it can display more lines of text, or even rudimentary monochrome graphics.

For my macro rail project I decided to up the ante and go for a colour screen, namely the 1.8″ ST7735 TFT display. This can display 160 by 128 pixels with 16-bit “High color” and can draw shapes as well as text. It also has an SD card socket on the back, although I’m not going to use this at present.

The screen has a height of just over 3.5cm and a width of about 3cm. To me this raised the possibility of having four or five PCB mounted pushbutton switches up the side, aligned with text on the screen for menu item selection. I’m planning to control the macro rail with an Arduino Nano, so this time I’ve done my test setup with a Nano rather than the obligatory Uno.

Getting Started

In order to control the ST7735 with an Arduino there are a couple of libraries I had to add to the Arduino IDE: Adafruit ST7735 and Adafruit_GFX. These were added to the IDE via Tools>>Manage Libraries… With the libraries installed, a quick IDE restart had me ready to go:

ST7735 TFT Display

Hardware Connection

The next step was to connect the screen to the Arduino Nano: this only required five wires plus power. However, it’s worth noting that my test ST7735 is a 3.3 Volt unit. While the Nano can provide 3.3V power, the digital output pins are 5V. To protect the display I used 1KΩ resistors in series with the five Arduino data lines.

To make life very easy, the 7735 uses the SPI (serial peripheral interface) protocol to communicate with the Arduino. With Arduino, SPI can either be implemented in hardware or software. I decided to use the hardware option as it’s faster than software SPI, but has to use the board’s hardware SPI pins. For the Nano these are Pin 11 for MOSI and Pin 13 for SCLK. With software SPI it’s possible to assign your own pins, but unless there’s a particular need to use D11 and D13 for something else, I’m not sure I can see any benefit.

The full set of connections from Nano to ST7735 is:

Arduino NanoST7735 TFT
3.3VVcc
GNDGND
D11SDA
D13SCL
D5RES
D6CS
D7DC

Pretty straightforward stuff: I wired mine up on a small breadboard to get me started quickly.

First Test

With the display connected to the Nano I needed a sketch to check that the display was actually working. Luckily Adafruit have provided a couple of useful ones that can be accessed in the IDE via File>>Examples>>Adafruit ST7735 and ST7789 Library. The best of these was called “graphicstest”:

ST7735 TFT Display

This pretty much does what it says on the tin, once a very minor mod has been made to it. Because I was using D5, D6 and D7 for RES, CS and DC respectively, these had to be reflected in the define statements (highlighted) at the start of the sketch:

ST7735 TFT Display

With the define statements fixed, I compiled the sketch and uploaded it to the Nano. When the upload was complete the display ran through its range of text and graphical displays, proving that the screen was working and the interface to the Nano was good.

Programming

One of the great things about the graphicstest sketch is that it displays text and various graphics, so gives a quick insight into how to do these yourself. It also gives you bits you can pick out and tweak to help work out how to implement the displays you actually want.

Displaying Text

Displaying basic text is easy, but requires a few function calls to set things up prior to actually printing the text. First, set the text size using the following function:

void setTextSize(uint8_t size);

where size is an 8-bit integer. As far as the available text sizes are concerned, text size 1 is small, size 2 quite big and anything higher is, in my view, too big for the screen. Half sizes would have been useful, as between 1 and 2 would probably be ideal for what I was trying to do. However, we can only work with what we have!

I believe it may be possible to do intermediate text sizes with a different ST7735 library, but that’s another rabbit hole to chase down some other time.

Text colour is set with another call, this time passing an unsigned 16-bit integer:

void setTextColor(uint16_t color);

Colours can either be pre-defined values like “ST77XX_GREEN” or 16-bit hex values such as “0xEC02”.

It is possible to just print lines of text to the screen, but to fromat it neatly the position of each string can be set using the following function:

void setCursor(int16_t x0, int16_t y0);

where x0 and y0 are the coordinates of the top left of the first character, passed as unsigned 16-bit integers. [(0,0) is the top left corner of the screen.]

With the text parameters set up, the actual text is just written tft.print() instructions , which work the same as print() in serial printing.

Displaying Symbols

The GFX library implements a range of primitives: points, lines, rectangles, triangles, circles and rounded rectangles. For the display I was aiming to implement I only needed a few of these, detailed below.

As the screen area is quite small I decided to make my status symbols a traffic light system. This removes the need to make very small ticks and crosses – instead just having to use filled circles. Very usefully, filled circles have been implemented in the library and just require four parameters to be passed to the function:

void fillCircle(uint16_t x0, uint16_t y0, uint16_t r, uint16_t color);

where the parameters, all passed as unsigned 16-bit integers, are:

  • x0 and y0 are the position of the centre of the circle,
  • radius is the radius of the circle in pixels,
  • color is the circle’s colour: either pre-defined or a hex value (same as text colours, above).

There’s another library function to draw unfilled circles, which could be useful highlighting list items for selection. Calling it uses the same parameters as the filled circle:

void drawCircle(uint16_t x0, uint16_t y0, uint16_t r, uint16_t color);

Finally, for highlighting text items on the menu there’s a rounded rectangle:

void drawRoundRect(uint16_t x0, uint16_t y0, uint16_t w, uint16_t h, uint16_t radius, uint16_t color);

This one has a few more parameters:

  • x0 and y0 are the coordinates of the top-left corner,
  • w and h are the width and height of the rectangle,
  • radius is the radius of the corner rounding in pixels,
  • color is the colour.

Again all parameters are passed as unsigned 16-bit integers.

Test Sketch

Looking forward to the elements I think I need to produce the UI for the macro rail, I designed a simple display with a heading, a list of menu items on the left and values and status indicators on the right. I added highlighting outlines to one each of the values and status indicators. I coded this as a simple static display page, using a subset of the GFX library’s functionality.

The display I produced is shown here:

ST7735 TFT Display

… and the sketch to create it is listed below:

#include <Adafruit_GFX.h>    // Core graphics library
#include <Adafruit_ST7735.h> // Hardware-specific library for ST7735
#include <SPI.h>

#define TFT_CS         6  // define CS, RST and DC pins
#define TFT_RST        5 
#define TFT_DC         7

Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST);

void setup() {
  Serial.begin(9600);
  tft.initR(INITR_BLACKTAB);      // Init ST7735S chip, black tab
  tft.setTextWrap(false);
  delay(500);
  tft.fillScreen(ST77XX_BLACK);   //clear the screen
}

void loop() {
  // write the heading
  tft.setCursor(0, 0);
  tft.setTextColor(0xE383);
  tft.setTextSize(2);
  tft.print("Heading");
  // write the menu items
  tft.setCursor(0, 32);
  tft.setTextColor(ST77XX_WHITE);
  tft.setTextSize(1);
  tft.print("Item 1:");
  tft.setCursor(0, 57);
  tft.print("Item 2:");
  tft.setCursor(0, 82);
  tft.print("Item 3:");
  tft.setCursor(0, 107);
  tft.print("Item 4:");
  tft.setCursor(0, 132);
  tft.print("Item 5:");
  // write some filled circles
  tft.fillCircle(118, 35, 5, 0x1F09);   // green
  tft.fillCircle(118, 60, 5, 0xED42);   // orange
  tft.fillCircle(118, 85, 5, 0xF8E0);   // red
  // write two values as text
  tft.setCursor(70, 107);
  tft.print(123.45);
  tft.setCursor(70, 132);
  tft.print(567.8);
  // write indiviual characters
  tft.drawChar(110,107,0xE5 ,ST77XX_WHITE, 0, 1);
  tft.drawChar(116,107,0x6D ,ST77XX_WHITE, 0, 1);  
  tft.drawChar(116, 132,0x73,ST77XX_WHITE, 0, 1);
  // highlight second circle
  tft.drawCircle(118, 60, 8, ST77XX_YELLOW);
  // highlight first value
  tft.drawRoundRect(67, 104, 58, 13, 4, ST77XX_YELLOW);
}

Note: the above is purely a test sketch with a lot of static values and positioning, but it shows what the screen can do. Things will get more complicated code-wise when this gets turned into a properly structured sketch and integrated into the macro rail.

Leave a Reply

Your email address will not be published. Required fields are marked *