As explained in the Overview, this guide is not an in-depth tutorial to writing LittlevGL applications. For that, have a look through the explanations and examples on the LittlevGL documentation site.

Let’s look at the basics of using Adafruit_LvGL_Glue though. Once that’s understood, many LittlevGL examples can be copied-and-pasted and work with only minor modification.

Something common to ALL of the examples is the inclusion of the LittlevGL and Glue library header files, and then a few lines down a global Adafruit_LvGL_Glue object declaration (which has no arguments):

Download: file
#include <lvgl.h>
#include <Adafruit_LvGL_Glue.h>

Adafruit_LvGL_Glue glue;

For the PyPortal’s touchscreen, the TouchScreen.h header is included, and a global TouchScreen object is declared. We’ve abbreviated here…in the examples, you’ll see all the values for the XP, YP and other arguments are #defined.

Download: file
#include <TouchScreen.h>

TouchScreen ts(XP, YP, XM, YM, 300);

A TFT FeatherWing’s touchscreen works a little differently. Adafruit_STMPE610.h is included, and a global Adafruit_STMPE610 object declared, with different arguments (again, abbreviated here, we’re not showing all the #defines):

Download: file
#include <Adafruit_STMPE610.h>

Adafruit_STMPE610 ts(STMPE_CS);

Various displays will have their own device-specific header files. For the PyPortal Titano and 480x320 TFT FeatherWing, that would be Adafruit_HX8357.h. A global Adafruit_HX8357 object is then declared, but the arguments are totally different between PyPortal and FeatherWing. See the example sketches. For FeatherWing, it’s like so:

Download: file
#include <Adafruit_HX8357.h>

Adafruit_HX8357 tft(TFT_CS, TFT_DC, TFT_RST);

For other PyPortal models and 320x240 TFT FeatherWings, it’s Adafruit_ILI9341.h and an Adafruit_ILI9341 object. Again, the arguments vary between PyPortal and FeatherWing, see the examples. For PyPortal it’s:

Download: file
#include <Adafruit_ILI9341.h>

Adafruit_ILI9341 tft(tft8bitbus, TFT_D0, TFT_WR, TFT_DC, TFT_CS, TFT_RST, TFT_RD);

That’s it for the header includes and global objects.

Then, in the begin() function, the display is initialized…calling its begin() (and optionally setRotation()) functions, and (on some devices) switching on the backlight:

Download: file
// Initialize display BEFORE glue setup
  tft.begin();
  tft.setRotation(TFT_ROTATION);
  pinMode(TFT_BACKLIGHT, OUTPUT);    // On devices with controllable backlight
  digitalWrite(TFT_BACKLIGHT, HIGH); // otherwise omit these lines

With the TFT Gizmo for Circuit Playground, the backlight control is a little different:

Download: file
analogWrite(TFT_BACKLIGHT, 255); // USE analogWrite() FOR GIZMO BACKLIGHT!

If using the a TFT FeatherWing (or other screen with STMPE610 touchscreen controller), that library also needs a begin() call. On PyPortal, this isn’t needed.

Download: file
if(!ts.begin()) {
    Serial.println("Couldn't start touchscreen controller");
    for(;;);
  }

Finally, with the display and touch initialized, we call our Glue library’s begin() function, passing in the address of the TFT and (optionally) touchscreen objects (the & before each means “address of” in C). If using a non-touch device (as in the CLUE and Gizmo examples), the touchscreen object can be omitted.

Download: file
// Initialize glue, passing in address of display & touchscreen
  LvGLStatus status = glue.begin(&tft, &ts);
  if(status != LVGL_OK) {
    Serial.printf("Glue error %d\r\n", (int)status);
    for(;;);
  }

You can also optionally add a “true” last argument to begin(), which enables logging to the Serial Console (also requires some configuration of LittlevGL, as explained on the previous page).

All of our examples then have this call at the end of the setup() function:

Download: file
lvgl_setup(); // Call UI-building function above

In the examples, all of the LittlevGL interface-building happens inside a lvgl_setup() function, not in setup(). This makes it easier to just copy-and-paste the boilerplate Arduino code that sets up the display and touchscreen…you don’t need to snip pieces out of setup() every time.

The loop() function often then has very little to do. Just need to periodically call one of the LittlevGL internal functions every few milliseconds. Any special handling of events (such as button presses) is handled with other callback functions (see widgets_pyportal for example).

Download: file
void loop(void) {
  lv_task_handler(); // Call LittleVGL task handler periodically
  delay(5);
}

The CLUE and Gizmo examples have a bit more code inside their loop() functions. Since these devices lack touchscreens, our code needs to process input from the physical buttons on its own.

Using LittlevGL with Other Displays

Most of our color TFT and OLED displays could be used with LittlevGL, but the examples are all written for devices that offer lots of pixels… the utility of LittlevGL might be lost on smaller displays, but you’re welcome to experiment. PyGamer, for example, the screen is only 160x128 pixels and non-touch…but the joystick and buttons might still make it useful in unexpected ways.

To try out a different display, you can usually create a mashup starting with one of our LvGL_Glue examples, plus a simple graphics test from that particular display library. Extract those parts that define and initialize the display, then pass the corresponding display object to the Glue library’s begin() function.

For example, using a SSD1351-based OLED display, one might start with this:

Download: file
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1351.h>
#include <SPI.h>

#define SCREEN_WIDTH  128
#define SCREEN_HEIGHT 128 // Change this to 96 for 1.27" OLED.

#define DC_PIN   4
#define CS_PIN   5
#define RST_PIN  6

Adafruit_SSD1351 tft(SCREEN_WIDTH, SCREEN_HEIGHT, &SPI, CS_PIN, DC_PIN, RST_PIN);

And then, in the setup() function, initialize the screen and hand it off to the glue library:

Download: file
tft.begin();

  // Initialize glue, passing in address of display
  LvGLStatus status = glue.begin(&tft);
  if(status != LVGL_OK) {
    Serial.printf("Glue error %d\r\n", (int)status);
    for(;;);
  }

That’s just the setup…there would be additional code to create the LittlevGL interface and periodically call that library’s task handler, exactly like our examples for other devices.

The lesser resolution of these displays will limit what can be done there with LittlevGL, but it might still prove useful for a few small status indicators. If you really want to work at it, LittlevGL can be “themed” with different visual styles, and you might find something less busy that works well with less screen real estate.

See the LittlevGL documentation for more ideas!

This guide was first published on Apr 09, 2020. It was last updated on Apr 09, 2020.
This page (Using) was last updated on Jul 03, 2020.