mirror of https://github.com/arendst/Tasmota.git
Merge pull request #14646 from s-hadinger/tx_fix_again
LVGL TS fix (again)
This commit is contained in:
commit
6dafe7cb60
File diff suppressed because it is too large
Load Diff
|
@ -1,88 +0,0 @@
|
|||
/*
|
||||
THIS EXAMPLE IS REALLY JUST DOCUMENTATION TO GUIDE YOU THROUGH CHANGES IN
|
||||
Adafruit_LvGL_Glue 2.0.
|
||||
|
||||
If you’re coming to Adafruit_LvGL_Glue for the first time as a current user
|
||||
of LittlevGL on other platforms, you're fine, there are no surprises here,
|
||||
have a look at the other examples to see how to set up an Adafruit display
|
||||
for your LittlevGL application.
|
||||
|
||||
BUT...if you've used a prior version of Adafruit_LvGL_Glue, and had written
|
||||
Arduino sketches around it, you're unfortunately in for some extra work.
|
||||
The "glue" hasn't changed at all, but LittlevGL has seen repeated overhauls,
|
||||
and projects using earlier versions of Adafruit_LvGL_Glue will no longer
|
||||
compile in the new system without substantial changes. Many function names,
|
||||
constants, and styles in particular, will require updating. Many LittlevGL
|
||||
projects are too much to fit on M0 (SAMD21) boards now -- it's best to work
|
||||
with a device with more RAM -- M4 (SAMD51), nRF52 and ESP32 are currently
|
||||
supported.
|
||||
|
||||
If desperate to get old code working, you can downgrade to lv_arduino 2.1.5
|
||||
and Adafruit_LvGL_Glue 1.0.2, but this is NOT recommended when developing
|
||||
for the long haul -- lv_arduino is altogether deprecated now and won't be
|
||||
staging a comeback.
|
||||
|
||||
For starters, LittlevGL has moved to an entirely different Arduino library.
|
||||
"lv_arduino" should be UNINSTALLED, and in its place, "lvgl" should be
|
||||
INSTALLED. The latter is at version 7.11.0 as this is being written...if
|
||||
there's a newer release, and if you find our glue examples failing to
|
||||
compile, it's recommended to install that version until this library can be
|
||||
updated. The LittlevGL developers' preference favors structural and
|
||||
consistency upgrades over backwards compatibility -- and that's fine, their
|
||||
project, their rules -- just explaining why this overhaul is necessary.
|
||||
|
||||
To repeat: in the Arduino Library Manager, uninstall lv_arduino, install
|
||||
lvgl.
|
||||
|
||||
Also in the Arduino Library Manager, you'll see a related library called
|
||||
"lv_examples" from the same developer. You can install that if you want, but
|
||||
understand that this is not actually a library, nor will any of the examples
|
||||
there compile in the Arduino IDE! But if you pick through these files
|
||||
manually, there's C code you can dissect for insights in creating interfaces
|
||||
with LittlevGL, and might create mash-ups with Adafruit_LvGL_Glue examples.
|
||||
|
||||
Adafruit_LvGL_Glue includes a lv_conf.file (LittlevGL configuration) that
|
||||
should "just work" and enables some settings for best compatibility with
|
||||
Adafruit displays. The only "gotcha" here is that user sketches MUST
|
||||
#include Adafruit_LvGL_Glue.h BEFORE including lvgl.h, in order for
|
||||
LittlevGL to pick up on the location of this header file.
|
||||
|
||||
BELOW IS A HYPOTHETICAL AND MINIMAL BUT ESSENTIALLY VALID ADAFRUIT_LVGL_GLUE
|
||||
ARDUINO SKETCH. Please see the other examples for more realistic use. Actual
|
||||
projects will have different display interfacing, backlight control, a set
|
||||
of UI widgets and so forth.
|
||||
*/
|
||||
|
||||
#include <Adafruit_LvGL_Glue.h> // Glue library header INCLUDE THIS FIRST!
|
||||
#include <lvgl.h> // LittlevGL header
|
||||
#include <Adafruit_ST7789.h> // Display-specific header
|
||||
|
||||
#define TFT_CS 1 // Display chip-select pin
|
||||
#define TFT_DC 2 // Display data/command pin
|
||||
#define TFT_RST 3 // Display reset pin
|
||||
|
||||
Adafruit_ST7789 tft(TFT_CS, TFT_DC, TFT_RST); // TFT on default SPI port
|
||||
Adafruit_LvGL_Glue glue;
|
||||
|
||||
void setup(void) {
|
||||
Serial.begin(115200);
|
||||
|
||||
tft.init(240, 240); // Initialize display
|
||||
|
||||
// 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(;;);
|
||||
}
|
||||
|
||||
// Create simple label centered on screen
|
||||
lv_obj_t *label = lv_label_create(lv_scr_act(), NULL);
|
||||
lv_label_set_text(label, "Hello Arduino!");
|
||||
lv_obj_align(label, NULL, LV_ALIGN_CENTER, 0, 0);
|
||||
}
|
||||
|
||||
void loop(void) {
|
||||
lv_task_handler(); // Call LittleVGL task handler periodically
|
||||
delay(5);
|
||||
}
|
|
@ -1,58 +0,0 @@
|
|||
// Minimal "Hello" example for LittlevGL on Adafruit CLUE. Requires
|
||||
// LittlevGL, Adafruit_LvGL_Glue, Adafruit_GFX and Adafruit_ST7735
|
||||
// libraries.
|
||||
|
||||
// Prior Adafruit_LvGL_Glue users: see hello_changes example for updates!
|
||||
|
||||
#include <Adafruit_LvGL_Glue.h> // Always include this BEFORE lvgl.h!
|
||||
#include <lvgl.h>
|
||||
#include <Adafruit_ST7789.h>
|
||||
|
||||
#define TFT_ROTATION 1 // Landscape orientation on CLUE
|
||||
#define TFT_SPI SPI1 // CLUE display peripheral & pins
|
||||
#define TFT_CS 31
|
||||
#define TFT_DC 32
|
||||
#define TFT_RST 33
|
||||
#define TFT_BACKLIGHT 34
|
||||
|
||||
Adafruit_ST7789 tft(&TFT_SPI, TFT_CS, TFT_DC, TFT_RST);
|
||||
Adafruit_LvGL_Glue glue;
|
||||
|
||||
// This example sketch's LittlevGL UI-building calls are all in this
|
||||
// function rather than in setup(), so simple programs can just
|
||||
// copy-and-paste this sketch as a starting point, then embellish here:
|
||||
void lvgl_setup(void) {
|
||||
// Create simple label centered on screen
|
||||
lv_obj_t *label = lv_label_create(lv_scr_act(), NULL);
|
||||
lv_label_set_text(label, "Hello Arduino!");
|
||||
lv_obj_align(label, NULL, LV_ALIGN_CENTER, 0, 0);
|
||||
}
|
||||
|
||||
void setup(void) {
|
||||
Serial.begin(115200);
|
||||
|
||||
// Initialize display BEFORE glue setup
|
||||
tft.init(240, 240);
|
||||
tft.setRotation(TFT_ROTATION);
|
||||
pinMode(TFT_BACKLIGHT, OUTPUT);
|
||||
digitalWrite(TFT_BACKLIGHT, HIGH);
|
||||
|
||||
// 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(;;);
|
||||
}
|
||||
|
||||
lvgl_setup(); // Call UI-building function above
|
||||
}
|
||||
|
||||
void loop(void) {
|
||||
lv_task_handler(); // Call LittleVGL task handler periodically
|
||||
delay(5);
|
||||
}
|
||||
|
||||
// NOTE TO FUTURE SELF: this sketch is essentially the same as the Gizmo
|
||||
// widgets example. If updating one sketch, make sure the other is kept
|
||||
// in sync. Aside from screen setup, differences include backlight control
|
||||
// and A/B button setup & read (CPX is active high, CLUE is active low).
|
|
@ -1,74 +0,0 @@
|
|||
// Minimal "Hello" example for LittlevGL on Adafruit TFT FeatherWings.
|
||||
// Requires LittlevGL, Adafruit_LvGL_Glue, Adafruit_STMPE610, Adafruit_GFX
|
||||
// and Adafruit_ILI9341 (2.4" TFT) or Adafruit_HX8357 (3.5") libraries.
|
||||
// This example doesn't use any touchscreen input, but it's declared anyway
|
||||
// so this sketch can be copied-and-pasted to serve as a starting point for
|
||||
// other projects. If display is scrambled, check that correct FeatherWing
|
||||
// type is selected below (set BIG_FEATHERWING to 0 or 1 as needed).
|
||||
|
||||
// Prior Adafruit_LvGL_Glue users: see hello_changes example for updates!
|
||||
|
||||
#define BIG_FEATHERWING 0 // Set this to 1 for 3.5" (480x320) FeatherWing!
|
||||
|
||||
#include <Adafruit_LvGL_Glue.h> // Always include this BEFORE lvgl.h!
|
||||
#include <lvgl.h>
|
||||
#include <Adafruit_STMPE610.h>
|
||||
|
||||
#ifdef ESP32
|
||||
#define TFT_CS 15
|
||||
#define TFT_DC 33
|
||||
#define STMPE_CS 32
|
||||
#else
|
||||
#define TFT_CS 9
|
||||
#define TFT_DC 10
|
||||
#define STMPE_CS 6
|
||||
#endif
|
||||
#define TFT_ROTATION 1 // Landscape orientation on FeatherWing
|
||||
#define TFT_RST -1
|
||||
|
||||
#if BIG_FEATHERWING
|
||||
#include <Adafruit_HX8357.h>
|
||||
Adafruit_HX8357 tft(TFT_CS, TFT_DC, TFT_RST);
|
||||
#else
|
||||
#include <Adafruit_ILI9341.h>
|
||||
Adafruit_ILI9341 tft(TFT_CS, TFT_DC);
|
||||
#endif
|
||||
|
||||
Adafruit_STMPE610 ts(STMPE_CS);
|
||||
Adafruit_LvGL_Glue glue;
|
||||
|
||||
// This example sketch's LittlevGL UI-building calls are all in this
|
||||
// function rather than in setup(), so simple programs can just
|
||||
// copy-and-paste this sketch as a starting point, then embellish here:
|
||||
void lvgl_setup(void) {
|
||||
// Create simple label centered on screen
|
||||
lv_obj_t *label = lv_label_create(lv_scr_act(), NULL);
|
||||
lv_label_set_text(label, "Hello Arduino!");
|
||||
lv_obj_align(label, NULL, LV_ALIGN_CENTER, 0, 0);
|
||||
}
|
||||
|
||||
void setup(void) {
|
||||
Serial.begin(115200);
|
||||
|
||||
// Initialize display and touchscreen BEFORE glue setup
|
||||
tft.begin();
|
||||
tft.setRotation(TFT_ROTATION);
|
||||
if(!ts.begin()) {
|
||||
Serial.println("Couldn't start touchscreen controller");
|
||||
for(;;);
|
||||
}
|
||||
|
||||
// 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(;;);
|
||||
}
|
||||
|
||||
lvgl_setup(); // Call UI-building function above
|
||||
}
|
||||
|
||||
void loop(void) {
|
||||
lv_task_handler(); // Call LittleVGL task handler periodically
|
||||
delay(5);
|
||||
}
|
|
@ -1,57 +0,0 @@
|
|||
// Minimal "Hello" example for LittlevGL on TFT Gizmo display for Adafruit
|
||||
// Circuit Playground Express or Circuit Playground Bluefruit. Requires
|
||||
// LittlevGL, Adafruit_LvGL_Glue, Adafruit_GFX and Adafruit_ST7735 libraries.
|
||||
|
||||
// Prior Adafruit_LvGL_Glue users: see hello_changes example for updates!
|
||||
|
||||
#include <Adafruit_LvGL_Glue.h> // Always include this BEFORE lvgl.h!
|
||||
#include <lvgl.h>
|
||||
#include <Adafruit_ST7789.h>
|
||||
|
||||
#define TFT_ROTATION 2
|
||||
#define TFT_CS A6
|
||||
#define TFT_DC A7
|
||||
#define TFT_RST -1
|
||||
#define TFT_BACKLIGHT A3
|
||||
#if defined(NRF52_SERIES) // Circuit Playground Bluefruit
|
||||
#define TFT_SPI SPI
|
||||
#else // Circuit Playground Express
|
||||
#define TFT_SPI SPI1
|
||||
#endif
|
||||
|
||||
Adafruit_ST7789 tft(&TFT_SPI, TFT_CS, TFT_DC, TFT_RST);
|
||||
Adafruit_LvGL_Glue glue;
|
||||
|
||||
// This example sketch's LittlevGL UI-building calls are all in this
|
||||
// function rather than in setup(), so simple programs can just
|
||||
// copy-and-paste this sketch as a starting point, then embellish here:
|
||||
void lvgl_setup(void) {
|
||||
// Create simple label centered on screen
|
||||
lv_obj_t *label = lv_label_create(lv_scr_act(), NULL);
|
||||
lv_label_set_text(label, "Hello Arduino!");
|
||||
lv_obj_align(label, NULL, LV_ALIGN_CENTER, 0, 0);
|
||||
}
|
||||
|
||||
void setup(void) {
|
||||
Serial.begin(115200);
|
||||
|
||||
// Initialize display BEFORE glue setup
|
||||
tft.init(240, 240);
|
||||
tft.setRotation(TFT_ROTATION);
|
||||
pinMode(TFT_BACKLIGHT, OUTPUT);
|
||||
analogWrite(TFT_BACKLIGHT, 255); // USE analogWrite() FOR GIZMO BACKLIGHT!
|
||||
|
||||
// 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(;;);
|
||||
}
|
||||
|
||||
lvgl_setup(); // Call UI-building function above
|
||||
}
|
||||
|
||||
void loop(void) {
|
||||
lv_task_handler(); // Call LittleVGL task handler periodically
|
||||
delay(5);
|
||||
}
|
|
@ -1,74 +0,0 @@
|
|||
// Minimal "Hello" example for LittlevGL on Adafruit PyPortal. Requires
|
||||
// LittlevGL, Adafruit_LvGL_Glue, Adafruit Touchscreen, Adafruit_GFX and
|
||||
// Adafruit_ILI9341 (PyPortal, PyPortal Pynt) or Adafruit_HX8357 (PyPortal
|
||||
// Titano) libraries. This example doesn't use any touchscreen input, but
|
||||
// it's declared anyway so this sketch can be copied-and-pasted to serve
|
||||
// as a starting point for other projects. If display is scrambled, check
|
||||
// that the correct board is selected -- PyPortal vs PyPortal Titano.
|
||||
|
||||
// Prior Adafruit_LvGL_Glue users: see hello_changes example for updates!
|
||||
|
||||
#include <Adafruit_LvGL_Glue.h> // Always include this BEFORE lvgl.h!
|
||||
#include <lvgl.h>
|
||||
#include <TouchScreen.h>
|
||||
|
||||
#define TFT_ROTATION 3 // Landscape orientation on PyPortal
|
||||
#define TFT_D0 34 // PyPortal TFT pins
|
||||
#define TFT_WR 26
|
||||
#define TFT_DC 10
|
||||
#define TFT_CS 11
|
||||
#define TFT_RST 24
|
||||
#define TFT_RD 9
|
||||
#define TFT_BACKLIGHT 25
|
||||
#define YP A4 // PyPortal touchscreen pins
|
||||
#define XP A5
|
||||
#define YM A6
|
||||
#define XM A7
|
||||
|
||||
#if defined(ADAFRUIT_PYPORTAL_M4_TITANO)
|
||||
#include <Adafruit_HX8357.h>
|
||||
Adafruit_HX8357 tft(tft8bitbus, TFT_D0, TFT_WR, TFT_DC, TFT_CS, TFT_RST,
|
||||
TFT_RD);
|
||||
#else
|
||||
#include <Adafruit_ILI9341.h>
|
||||
Adafruit_ILI9341 tft(tft8bitbus, TFT_D0, TFT_WR, TFT_DC, TFT_CS, TFT_RST,
|
||||
TFT_RD);
|
||||
#endif
|
||||
TouchScreen ts(XP, YP, XM, YM, 300);
|
||||
Adafruit_LvGL_Glue glue;
|
||||
|
||||
// This example sketch's LittlevGL UI-building calls are all in this
|
||||
// function rather than in setup(), so simple programs can just
|
||||
// copy-and-paste this sketch as a starting point, then embellish here:
|
||||
void lvgl_setup(void) {
|
||||
// Create simple label centered on screen
|
||||
lv_obj_t *label = lv_label_create(lv_scr_act(), NULL);
|
||||
lv_label_set_text(label, "Hello Arduino!");
|
||||
lv_obj_align(label, NULL, LV_ALIGN_CENTER, 0, 0);
|
||||
}
|
||||
|
||||
void setup(void) {
|
||||
Serial.begin(115200);
|
||||
|
||||
// Initialize display BEFORE glue setup
|
||||
tft.begin();
|
||||
tft.setRotation(TFT_ROTATION);
|
||||
pinMode(TFT_BACKLIGHT, OUTPUT);
|
||||
digitalWrite(TFT_BACKLIGHT, HIGH);
|
||||
|
||||
// PyPortal touchscreen needs no init
|
||||
|
||||
// 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(;;);
|
||||
}
|
||||
|
||||
lvgl_setup(); // Call UI-building function above
|
||||
}
|
||||
|
||||
void loop(void) {
|
||||
lv_task_handler(); // Call LittleVGL task handler periodically
|
||||
delay(5);
|
||||
}
|
|
@ -1,203 +0,0 @@
|
|||
// A more functional example for LittlevGL on Adafruit CLUE. Lacking
|
||||
// touchscreen input, interaction options are limited (but not impossible).
|
||||
// In this case we'll pretend like it's a status display for something --
|
||||
// the left and right buttons switch between three different tabs, each
|
||||
// displaying different information. The code's a bit more complex than
|
||||
// the hello_clue example, so best get that working before trying this.
|
||||
|
||||
// Prior Adafruit_LvGL_Glue users: see hello_changes example for updates!
|
||||
|
||||
#include <Adafruit_LvGL_Glue.h> // Always include this BEFORE lvgl.h!
|
||||
#include <lvgl.h>
|
||||
#include <Adafruit_ST7789.h>
|
||||
|
||||
#define TFT_ROTATION 1 // Landscape orientation on CLUE
|
||||
#define TFT_SPI SPI1 // CLUE display peripheral & pins
|
||||
#define TFT_CS 31
|
||||
#define TFT_DC 32
|
||||
#define TFT_RST 33
|
||||
#define TFT_BACKLIGHT 34
|
||||
|
||||
Adafruit_ST7789 tft(&TFT_SPI, TFT_CS, TFT_DC, TFT_RST);
|
||||
Adafruit_LvGL_Glue glue;
|
||||
|
||||
lv_obj_t *tabview, // LittlevGL tabview object
|
||||
*gauge, // Gauge object (on first of three tabs)
|
||||
*chart, // Chart object (second tab)
|
||||
*canvas; // Canvas object (third tab)
|
||||
uint8_t active_tab = 0, // Index of currently-active tab (0-2)
|
||||
prev_tab = 0; // Index of previously-active tab
|
||||
lv_chart_series_t *series; // 'Series' data for the bar chart
|
||||
|
||||
// Canvas object is an image in memory. Although LittlevGL supports several
|
||||
// internal image formats, only the TRUE_COLOR variety allows drawing
|
||||
// operations. Fortunately nRF52840 has gobs of RAM for this. The canvas
|
||||
// buffer is global because it's referenced both in the setup and main loop.
|
||||
#define CANVAS_WIDTH 200 // Dimensions in pixels
|
||||
#define CANVAS_HEIGHT 150
|
||||
lv_color_t canvas_buffer[
|
||||
LV_CANVAS_BUF_SIZE_TRUE_COLOR(CANVAS_WIDTH, CANVAS_HEIGHT)];
|
||||
lv_draw_line_dsc_t draw_dsc; // Drawing style (for canvas) is similarly global
|
||||
|
||||
void lvgl_setup(void) {
|
||||
// Create a tabview object, by default this covers the full display.
|
||||
tabview = lv_tabview_create(lv_disp_get_scr_act(NULL), NULL);
|
||||
// The CLUE display has a lot of pixels and can't refresh very fast.
|
||||
// To show off the tabview animation, let's slow it down to 1 second.
|
||||
lv_tabview_set_anim_time(tabview, 1000);
|
||||
|
||||
// Because they're referenced any time an object is drawn, styles need
|
||||
// to be permanent in scope; either declared globally (outside all
|
||||
// functions), or static. The styles used on tabs are never modified after
|
||||
// they're used here, so let's use static on those...
|
||||
static lv_style_t tab_style, tab_background_style, indicator_style;
|
||||
|
||||
// This is the background style "behind" the tabs. This is what shows
|
||||
// through for "off" (inactive) tabs -- a vertical green gradient,
|
||||
// minimal padding around edges (zero at bottom).
|
||||
lv_style_init(&tab_background_style);
|
||||
lv_style_set_bg_color(&tab_background_style, LV_STATE_DEFAULT, lv_color_hex(0x408040));
|
||||
lv_style_set_bg_grad_color(&tab_background_style, LV_STATE_DEFAULT, lv_color_hex(0x304030));
|
||||
lv_style_set_bg_grad_dir(&tab_background_style, LV_STATE_DEFAULT, LV_GRAD_DIR_VER);
|
||||
lv_style_set_pad_top(&tab_background_style, LV_STATE_DEFAULT, 2);
|
||||
lv_style_set_pad_left(&tab_background_style, LV_STATE_DEFAULT, 2);
|
||||
lv_style_set_pad_right(&tab_background_style, LV_STATE_DEFAULT, 2);
|
||||
lv_style_set_pad_bottom(&tab_background_style, LV_STATE_DEFAULT, 0);
|
||||
lv_obj_add_style(tabview, LV_TABVIEW_PART_TAB_BG, &tab_background_style);
|
||||
|
||||
// Style for tabs. Active tab is white with opaque background, inactive
|
||||
// tabs are transparent so the background shows through (only the white
|
||||
// text is seen). A little top & bottom padding reduces scrunchyness.
|
||||
lv_style_init(&tab_style);
|
||||
lv_style_set_pad_top(&tab_style, LV_STATE_DEFAULT, 3);
|
||||
lv_style_set_pad_bottom(&tab_style, LV_STATE_DEFAULT, 10);
|
||||
lv_style_set_bg_color(&tab_style, LV_STATE_CHECKED, LV_COLOR_WHITE);
|
||||
lv_style_set_bg_opa(&tab_style, LV_STATE_CHECKED, LV_OPA_100);
|
||||
lv_style_set_text_color(&tab_style, LV_STATE_CHECKED, LV_COLOR_GRAY);
|
||||
lv_style_set_bg_opa(&tab_style, LV_STATE_DEFAULT, LV_OPA_TRANSP);
|
||||
lv_style_set_text_color(&tab_style, LV_STATE_DEFAULT, LV_COLOR_WHITE);
|
||||
lv_obj_add_style(tabview, LV_TABVIEW_PART_TAB_BTN, &tab_style);
|
||||
|
||||
// Style for the small indicator bar that appears below the active tab.
|
||||
lv_style_init(&indicator_style);
|
||||
lv_style_set_bg_color(&indicator_style, LV_STATE_DEFAULT, LV_COLOR_RED);
|
||||
lv_style_set_size(&indicator_style, LV_STATE_DEFAULT, 5);
|
||||
lv_obj_add_style(tabview, LV_TABVIEW_PART_INDIC, &indicator_style);
|
||||
|
||||
// Back to creating widgets...
|
||||
|
||||
// Add three tabs to the tabview
|
||||
lv_obj_t *tab1 = lv_tabview_add_tab(tabview, "Gauge");
|
||||
lv_obj_t *tab2 = lv_tabview_add_tab(tabview, "Chart");
|
||||
lv_obj_t *tab3 = lv_tabview_add_tab(tabview, "Canvas");
|
||||
|
||||
// And then add stuff in each tab...
|
||||
|
||||
// The first tab holds a gauge. To keep the demo simple, let's just use
|
||||
// the default style and range (0-100). See LittlevGL docs for options.
|
||||
gauge = lv_gauge_create(tab1, NULL);
|
||||
lv_obj_set_size(gauge, 186, 186);
|
||||
lv_obj_align(gauge, NULL, LV_ALIGN_CENTER, 0, 0);
|
||||
|
||||
// Second tab, make a chart...
|
||||
chart = lv_chart_create(tab2, NULL);
|
||||
lv_obj_set_size(chart, 200, 180);
|
||||
lv_obj_align(chart, NULL, LV_ALIGN_CENTER, 0, 0);
|
||||
lv_chart_set_type(chart, LV_CHART_TYPE_COLUMN);
|
||||
// For simplicity, we'll stick with the chart's default 10 data points:
|
||||
series = lv_chart_add_series(chart, LV_COLOR_RED);
|
||||
lv_chart_init_points(chart, series, 0);
|
||||
// Make each column shift left as new values enter on right:
|
||||
lv_chart_set_update_mode(chart, LV_CHART_UPDATE_MODE_SHIFT);
|
||||
|
||||
// Third tab is a canvas, which we'll fill with random colored lines.
|
||||
// LittlevGL draw functions only work on TRUE_COLOR canvas.
|
||||
canvas = lv_canvas_create(tab3, NULL);
|
||||
lv_canvas_set_buffer(canvas, canvas_buffer,
|
||||
CANVAS_WIDTH, CANVAS_HEIGHT, LV_IMG_CF_TRUE_COLOR);
|
||||
lv_obj_align(canvas, NULL, LV_ALIGN_CENTER, 0, 0);
|
||||
lv_canvas_fill_bg(canvas, LV_COLOR_WHITE, LV_OPA_100);
|
||||
|
||||
// Set up canvas line-drawing style based on defaults.
|
||||
// Later we'll change color settings when drawing each line.
|
||||
lv_draw_line_dsc_init(&draw_dsc);
|
||||
}
|
||||
|
||||
void setup(void) {
|
||||
Serial.begin(115200);
|
||||
|
||||
// Initialize display BEFORE glue setup
|
||||
tft.init(240, 240);
|
||||
tft.setRotation(TFT_ROTATION);
|
||||
pinMode(TFT_BACKLIGHT, OUTPUT);
|
||||
digitalWrite(TFT_BACKLIGHT, HIGH);
|
||||
|
||||
// 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(;;);
|
||||
}
|
||||
|
||||
lvgl_setup(); // Call UI-building function above
|
||||
|
||||
// Enable "A" and "B" buttons as inputs
|
||||
pinMode(PIN_BUTTON1, INPUT_PULLUP);
|
||||
pinMode(PIN_BUTTON2, INPUT_PULLUP);
|
||||
}
|
||||
|
||||
uint32_t prev_time = -1;
|
||||
|
||||
void loop(void) {
|
||||
|
||||
// Read left/right button inputs. Debouncing could be done better,
|
||||
// but this'll do for a quick simple demo...
|
||||
if(digitalRead(PIN_BUTTON1) == LOW) {
|
||||
if(active_tab > 0) {
|
||||
active_tab--;
|
||||
}
|
||||
while(digitalRead(PIN_BUTTON1) == LOW); // Wait for button release
|
||||
} else if(digitalRead(PIN_BUTTON2) == LOW) {
|
||||
if(active_tab < 2) {
|
||||
active_tab++;
|
||||
}
|
||||
while(digitalRead(PIN_BUTTON2) == LOW); // Wait for button release
|
||||
}
|
||||
|
||||
// Change active tab if button pressings happened
|
||||
if(active_tab != prev_tab) {
|
||||
lv_tabview_set_tab_act(tabview, active_tab, true);
|
||||
prev_tab = active_tab;
|
||||
}
|
||||
|
||||
// Make the gauge sweep a full sine wave over time
|
||||
lv_gauge_set_value(gauge, 0, (int)(50.5 + sin(millis() / 1000.0) * 50.0));
|
||||
|
||||
// About 2X a second, things happen on the other two tabs...
|
||||
uint32_t new_time = millis() / 500; // Current half-second
|
||||
if(new_time != prev_time) { // freshly elapsed
|
||||
prev_time = new_time;
|
||||
|
||||
// Add a new random item to the bar chart (old value shift left)
|
||||
lv_chart_set_next(chart, series, random(100));
|
||||
lv_chart_refresh(chart);
|
||||
|
||||
// Add a random line to the canvas
|
||||
lv_point_t points[2];
|
||||
points[0].x = random(CANVAS_WIDTH);
|
||||
points[0].y = random(CANVAS_HEIGHT);
|
||||
points[1].x = random(CANVAS_WIDTH);
|
||||
points[1].y = random(CANVAS_HEIGHT);
|
||||
draw_dsc.color.ch.red = random();
|
||||
draw_dsc.color.ch.green = random();
|
||||
draw_dsc.color.ch.blue = random();
|
||||
lv_canvas_draw_line(canvas, points, 2, &draw_dsc);
|
||||
// This forces the canvas to update (otherwise changes aren't
|
||||
// seen unless leaving and returning to the canvas tab):
|
||||
lv_canvas_set_buffer(canvas, canvas_buffer,
|
||||
CANVAS_WIDTH, CANVAS_HEIGHT, LV_IMG_CF_TRUE_COLOR);
|
||||
}
|
||||
|
||||
lv_task_handler(); // Call LittleVGL task handler periodically
|
||||
delay(5);
|
||||
}
|
|
@ -1,261 +0,0 @@
|
|||
// A more interesting example for LittlevGL on Adafruit TFT FeatherWings,
|
||||
// showing use of the touchscreen. Code's a little more complex than the
|
||||
// hello_featherwing example, so best get that working before trying this.
|
||||
// By default, as written, on a 320x240 TFT FeatherWing the example is a
|
||||
// pretend calculator keypad, while 480x320 TFT has a whole keyboard
|
||||
// (though you'll probably need a stylus). These just seemed the right
|
||||
// level of detail for their respective screens, but feel free to override
|
||||
// and try either for yourself. If display is scrambled, check that
|
||||
// correct FeatherWing type is selected below (set BIG_FEATHERWING to 0
|
||||
// or 1 as needed).
|
||||
|
||||
// Prior Adafruit_LvGL_Glue users: see hello_changes example for updates!
|
||||
|
||||
// Feather M0 is no longer recommended for LittlevGL use. The calculator
|
||||
// almost works (hangs after a few button presses) and keyboard won't run
|
||||
// at all. A Feather M4 or other >32K RAM device is recommended.
|
||||
|
||||
#define BIG_FEATHERWING 0 // Set this to 1 for 3.5" (480x320) FeatherWing!
|
||||
|
||||
#include <Adafruit_LvGL_Glue.h> // Always include this BEFORE lvgl.h!
|
||||
#include <lvgl.h>
|
||||
#include <Adafruit_STMPE610.h>
|
||||
|
||||
#define DEMO_CALC 0
|
||||
#define DEMO_TEXT 1
|
||||
|
||||
#ifdef ESP32
|
||||
#define TFT_CS 15
|
||||
#define TFT_DC 33
|
||||
#define STMPE_CS 32
|
||||
#else
|
||||
#define TFT_CS 9
|
||||
#define TFT_DC 10
|
||||
#define STMPE_CS 6
|
||||
#endif
|
||||
#define TFT_RST -1
|
||||
|
||||
#if BIG_FEATHERWING
|
||||
#include <Adafruit_HX8357.h>
|
||||
Adafruit_HX8357 tft(TFT_CS, TFT_DC, TFT_RST);
|
||||
#define DEMO DEMO_TEXT // On big TFT, do text/keyboard example
|
||||
#else
|
||||
#include <Adafruit_ILI9341.h>
|
||||
Adafruit_ILI9341 tft(TFT_CS, TFT_DC);
|
||||
#define DEMO DEMO_CALC // Smaller TFT, do keypad example
|
||||
#endif
|
||||
|
||||
Adafruit_STMPE610 ts(STMPE_CS);
|
||||
Adafruit_LvGL_Glue glue;
|
||||
|
||||
#if DEMO == DEMO_CALC
|
||||
|
||||
// "Pretend" calculator example. Please, PLEASE...do NOT implement the whole
|
||||
// calculator and submit as a pull request, because it will NOT be merged!
|
||||
// This sketch is meant only to be illustrative and not functional, just
|
||||
// showing LittlevGL + Adafruit display/touch tied together with a modest
|
||||
// amount of code. Even a simple but working calc would require WAY more
|
||||
// code, distracting from that core idea (and is a waste of hardware).
|
||||
// Daiso has better calculators for $1.50.
|
||||
|
||||
#define TFT_ROTATION 0 // Portrait orientation
|
||||
|
||||
lv_obj_t *digits_label = NULL; // LittlevGL label object showing digits
|
||||
String digits = "0"; // Current digits string value
|
||||
bool hasDecimal = false; // Only allow one decimal point
|
||||
const char *buttons[] = { // Button matrix labels
|
||||
"7", "8", "9", "/", "\n",
|
||||
"4", "5", "6", "x", "\n",
|
||||
"1", "2", "3", "-", "\n",
|
||||
"0", ".", "=", "+", "" };
|
||||
|
||||
// This function processes events from the button matrix
|
||||
void button_event_handler(lv_obj_t *obj, lv_event_t event) {
|
||||
if(event == LV_EVENT_VALUE_CHANGED) {
|
||||
const char *txt = lv_btnmatrix_get_active_btn_text(obj);
|
||||
if(txt) { // NULL if pressed in frame area outside buttons
|
||||
if(txt[0] == '.') {
|
||||
// Decimal button pressed. Add decimal point to "digits" string
|
||||
// if it's not too long and there's no decimal present yet...
|
||||
if((digits.length() < 15) && !hasDecimal) {
|
||||
digits += '.';
|
||||
hasDecimal = true;
|
||||
}
|
||||
} else if((txt[0] >= '0') && (txt[0] <= '9')) {
|
||||
// Number button (0-9) pressed. If there's nothing currently
|
||||
// being displayed, take the digit literally. Otherwise, append
|
||||
// the new digit if the "digits" string is not too long.
|
||||
if(digits.equals("0")) {
|
||||
digits = txt[0];
|
||||
} else if(digits.length() < 15) {
|
||||
digits += txt[0];
|
||||
}
|
||||
} else {
|
||||
// Any other button, just reset the calculator display.
|
||||
// It's all just pretend.
|
||||
digits = "0";
|
||||
hasDecimal = false;
|
||||
}
|
||||
if(digits_label != NULL) {
|
||||
lv_label_set_text(digits_label, digits.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void lvgl_setup(void) {
|
||||
|
||||
// Just using the default styles for everything here, keeping
|
||||
// it basic. See the official LittlevGL docs and examples for
|
||||
// insights on applying styles.
|
||||
|
||||
// The calculator digits are held inside a LvGL container object
|
||||
// as this gives us a little more control over positioning.
|
||||
lv_obj_t *container = lv_cont_create(lv_scr_act(), NULL);
|
||||
lv_cont_set_fit(container, LV_FIT_NONE); // Don't auto fit
|
||||
lv_obj_set_size(container, tft.width(), 50); // Full width x 50 px
|
||||
|
||||
// Calculator digits are just a text label inside the container,
|
||||
// refreshed whenever the global "digits" string changes.
|
||||
digits_label = lv_label_create(container, NULL);
|
||||
lv_label_set_text(digits_label, digits.c_str());
|
||||
lv_obj_align(digits_label, NULL, LV_ALIGN_IN_TOP_LEFT, 20, 10);
|
||||
lv_label_set_long_mode(digits_label, LV_LABEL_LONG_CROP);
|
||||
lv_obj_set_size(digits_label, tft.width() - 40, 30);
|
||||
lv_label_set_align(digits_label, LV_LABEL_ALIGN_RIGHT);
|
||||
|
||||
// Fill the remaining space with the button matrix.
|
||||
lv_obj_t *button_matrix = lv_btnmatrix_create(lv_scr_act(), NULL);
|
||||
lv_btnmatrix_set_map(button_matrix, buttons);
|
||||
lv_obj_align(button_matrix, NULL, LV_ALIGN_IN_TOP_LEFT, 0, 50);
|
||||
lv_obj_set_size(button_matrix, tft.width(), tft.height() - 50);
|
||||
lv_obj_set_event_cb(button_matrix, button_event_handler);
|
||||
}
|
||||
|
||||
#else // Keyboard demo
|
||||
|
||||
// Keyboard example, lets you enter and edit text in a field. Even on a
|
||||
// 480x320 TFT it requires a stylus to be even half useful (fingertip
|
||||
// is possible if very patient), but having the option of a keyboard at
|
||||
// all on this device is pretty nifty!
|
||||
|
||||
#define TFT_ROTATION 1 // Landscape orientation
|
||||
|
||||
lv_obj_t *textarea,
|
||||
*keyboard = NULL; // Created/deleted as needed
|
||||
|
||||
#if LV_USE_ANIMATION
|
||||
// Called after keyboard slides closed - deletes keyboard object
|
||||
void delete_keyboard(lv_anim_t * a) {
|
||||
lv_obj_del((lv_obj_t *)a->var);
|
||||
keyboard = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Called when the close or ok button is pressed on the keyboard
|
||||
void keyboard_event_handler(lv_obj_t *obj, lv_event_t event) {
|
||||
lv_keyboard_def_event_cb(keyboard, event);
|
||||
|
||||
if(event == LV_EVENT_APPLY || event == LV_EVENT_CANCEL) {
|
||||
#if LV_USE_ANIMATION
|
||||
// If animation is enabled, make keyboard slide away
|
||||
lv_anim_path_t path;
|
||||
lv_anim_path_init(&path);
|
||||
lv_anim_path_set_cb(&path, lv_anim_path_ease_in_out);
|
||||
lv_anim_t a;
|
||||
lv_anim_init(&a);
|
||||
lv_anim_set_exec_cb(&a, (lv_anim_exec_xcb_t)lv_obj_set_y);
|
||||
lv_anim_set_var(&a, keyboard);
|
||||
lv_anim_set_time(&a, 300);
|
||||
lv_anim_set_values(&a, lv_obj_get_y(keyboard), LV_VER_RES); // start, end
|
||||
lv_anim_set_path(&a, &path);
|
||||
lv_anim_set_ready_cb(&a, delete_keyboard);
|
||||
lv_anim_start(&a);
|
||||
#else
|
||||
lv_obj_del(keyboard);
|
||||
keyboard = NULL;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// Other clicks in the text area
|
||||
void text_area_event_handler(lv_obj_t *obj, lv_event_t event) {
|
||||
if(event == LV_EVENT_CLICKED) {
|
||||
|
||||
// Unsure why, but text area has an initial clicked event on
|
||||
// creation, causing the keyboard to appear. This is a hacky
|
||||
// workaround that just ignores the first click event, so
|
||||
// subsequent actual clicks bring up the keyboard.
|
||||
static bool first = true;
|
||||
if(first) {
|
||||
first = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if(keyboard == NULL) {
|
||||
// If not present, create keyboard object at bottom of screen
|
||||
keyboard = lv_keyboard_create(lv_scr_act(), NULL);
|
||||
lv_obj_set_size(keyboard, tft.width(), tft.height() * 7 / 16);
|
||||
lv_obj_align(keyboard, textarea, LV_ALIGN_IN_BOTTOM_MID, 0, 0);
|
||||
lv_keyboard_set_textarea(keyboard, textarea);
|
||||
lv_keyboard_set_cursor_manage(keyboard, true);
|
||||
lv_obj_set_event_cb(keyboard, keyboard_event_handler);
|
||||
|
||||
#if LV_USE_ANIMATION
|
||||
// If animation is enabled, make keyboard slide into place
|
||||
lv_anim_path_t path;
|
||||
lv_anim_path_init(&path);
|
||||
lv_anim_path_set_cb(&path, lv_anim_path_ease_in_out);
|
||||
lv_anim_t a;
|
||||
lv_anim_init(&a);
|
||||
lv_anim_set_exec_cb(&a, (lv_anim_exec_xcb_t)lv_obj_set_y);
|
||||
lv_anim_set_var(&a, keyboard);
|
||||
lv_anim_set_time(&a, 300);
|
||||
lv_anim_set_values(&a, LV_VER_RES, lv_obj_get_y(keyboard)); // start, end
|
||||
lv_anim_set_path(&a, &path);
|
||||
lv_anim_start(&a);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void lvgl_setup(void) {
|
||||
textarea = lv_textarea_create(lv_scr_act(), NULL);
|
||||
lv_obj_set_size(textarea, LV_HOR_RES, LV_VER_RES); // Whole screen
|
||||
lv_obj_align(textarea, NULL, LV_ALIGN_CENTER, 0, 0);
|
||||
lv_textarea_set_text(textarea, "This text is editable.");
|
||||
lv_obj_set_event_cb(textarea, text_area_event_handler);
|
||||
lv_textarea_set_cursor_pos(textarea, LV_TEXTAREA_CURSOR_LAST);
|
||||
}
|
||||
|
||||
#endif // end calculator / keyboard examples
|
||||
|
||||
void setup(void) {
|
||||
Serial.begin(115200);
|
||||
|
||||
// Initialize display and touchscreen BEFORE glue setup
|
||||
tft.begin();
|
||||
tft.setRotation(TFT_ROTATION);
|
||||
if(!ts.begin()) {
|
||||
Serial.println("Couldn't start touchscreen controller");
|
||||
for(;;);
|
||||
}
|
||||
|
||||
// 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(;;);
|
||||
}
|
||||
|
||||
lvgl_setup(); // Call UI-building function above
|
||||
}
|
||||
|
||||
void loop(void) {
|
||||
lv_task_handler(); // Call LittleVGL task handler periodically
|
||||
delay(5);
|
||||
}
|
||||
|
||||
// NOTE TO FUTURE SELF: this sketch is largely similar to the PyPortal
|
||||
// widgets example. If updating/bugfixing one sketch, make sure the other
|
||||
// is kept in sync.
|
|
@ -1,225 +0,0 @@
|
|||
// A more functional example for LittlevGL on Adafruit Circuit Playground
|
||||
// Bluefruit (NOT Express!) with TFT Gizmo display. Lacking touchscreen
|
||||
// input, interaction options are limited (but not impossible). In this
|
||||
// case we'll pretend like it's a status display for something -- the left
|
||||
// and right buttons (on the back if looking at the screen) switch between
|
||||
// three different tabs, each displaying different information. The code's
|
||||
// a bit more complex than the hello_gizmo example, so best get that
|
||||
// working before trying this. This example is a bit too much for the
|
||||
// Circuit Playground Express to handle now, but simpler LittlevGL
|
||||
// programs should be possible.
|
||||
|
||||
// Prior Adafruit_LvGL_Glue users: see hello_changes example for updates!
|
||||
|
||||
#if !defined(NRF52_SERIES) // Circuit Playground Bluefruit
|
||||
#error "This example is for Circuit Playground Bluefruit only."
|
||||
#endif
|
||||
|
||||
#include <Adafruit_LvGL_Glue.h> // Always include this BEFORE lvgl.h!
|
||||
#include <lvgl.h>
|
||||
#include <Adafruit_ST7789.h>
|
||||
|
||||
#define TFT_ROTATION 2
|
||||
#define TFT_CS A6
|
||||
#define TFT_DC A7
|
||||
#define TFT_RST -1
|
||||
#define TFT_BACKLIGHT A3
|
||||
#if defined(NRF52_SERIES) // Circuit Playground Bluefruit
|
||||
#define TFT_SPI SPI
|
||||
#else // Circuit Playground Express
|
||||
#define TFT_SPI SPI1
|
||||
// Though this example won't work on CP Express, including this
|
||||
// here anyway for reference, if copying over to a simpler sketch.
|
||||
#endif
|
||||
|
||||
Adafruit_ST7789 tft(&TFT_SPI, TFT_CS, TFT_DC, TFT_RST);
|
||||
Adafruit_LvGL_Glue glue;
|
||||
|
||||
lv_obj_t *tabview, // LittlevGL tabview object
|
||||
*gauge, // Gauge object (on first of three tabs)
|
||||
*chart, // Chart object (second tab)
|
||||
*canvas; // Canvas object (third tab)
|
||||
uint8_t active_tab = 0, // Index of currently-active tab (0-2)
|
||||
prev_tab = 0; // Index of previously-active tab
|
||||
lv_chart_series_t *series; // 'Series' data for the bar chart
|
||||
|
||||
// Canvas object is an image in memory. Although LittlevGL supports several
|
||||
// internal image formats, only the TRUE_COLOR variety allows drawing
|
||||
// operations. Fortunately nRF52840 has gobs of RAM for this. The canvas
|
||||
// buffer is global because it's referenced both in the setup and main loop.
|
||||
// Circuit Playground Express (M0) doesn't have enough RAM for this.
|
||||
#define CANVAS_WIDTH 200 // Dimensions in pixels
|
||||
#define CANVAS_HEIGHT 150
|
||||
lv_color_t canvas_buffer[
|
||||
LV_CANVAS_BUF_SIZE_TRUE_COLOR(CANVAS_WIDTH, CANVAS_HEIGHT)];
|
||||
lv_draw_line_dsc_t draw_dsc; // Drawing style (for canvas) is similarly global
|
||||
|
||||
void lvgl_setup(void) {
|
||||
// Create a tabview object, by default this covers the full display.
|
||||
tabview = lv_tabview_create(lv_disp_get_scr_act(NULL), NULL);
|
||||
// The Gizmo display has a lot of pixels and can't refresh very fast.
|
||||
// To show off the tabview animation, let's slow it down to 1 second.
|
||||
lv_tabview_set_anim_time(tabview, 1000);
|
||||
|
||||
// Because they're referenced any time an object is drawn, styles need
|
||||
// to be permanent in scope; either declared globally (outside all
|
||||
// functions), or static. The styles used on tabs are never modified after
|
||||
// they're used here, so let's use static on those...
|
||||
static lv_style_t tab_style, tab_background_style, indicator_style;
|
||||
|
||||
// This is the background style "behind" the tabs. This is what shows
|
||||
// through for "off" (inactive) tabs -- a vertical green gradient,
|
||||
// minimal padding around edges (zero at bottom).
|
||||
lv_style_init(&tab_background_style);
|
||||
lv_style_set_bg_color(&tab_background_style, LV_STATE_DEFAULT, lv_color_hex(0x408040));
|
||||
lv_style_set_bg_grad_color(&tab_background_style, LV_STATE_DEFAULT, lv_color_hex(0x304030));
|
||||
lv_style_set_bg_grad_dir(&tab_background_style, LV_STATE_DEFAULT, LV_GRAD_DIR_VER);
|
||||
lv_style_set_pad_top(&tab_background_style, LV_STATE_DEFAULT, 2);
|
||||
lv_style_set_pad_left(&tab_background_style, LV_STATE_DEFAULT, 2);
|
||||
lv_style_set_pad_right(&tab_background_style, LV_STATE_DEFAULT, 2);
|
||||
lv_style_set_pad_bottom(&tab_background_style, LV_STATE_DEFAULT, 0);
|
||||
lv_obj_add_style(tabview, LV_TABVIEW_PART_TAB_BG, &tab_background_style);
|
||||
|
||||
// Style for tabs. Active tab is white with opaque background, inactive
|
||||
// tabs are transparent so the background shows through (only the white
|
||||
// text is seen). A little top & bottom padding reduces scrunchyness.
|
||||
lv_style_init(&tab_style);
|
||||
lv_style_set_pad_top(&tab_style, LV_STATE_DEFAULT, 3);
|
||||
lv_style_set_pad_bottom(&tab_style, LV_STATE_DEFAULT, 10);
|
||||
lv_style_set_bg_color(&tab_style, LV_STATE_CHECKED, LV_COLOR_WHITE);
|
||||
lv_style_set_bg_opa(&tab_style, LV_STATE_CHECKED, LV_OPA_100);
|
||||
lv_style_set_text_color(&tab_style, LV_STATE_CHECKED, LV_COLOR_GRAY);
|
||||
lv_style_set_bg_opa(&tab_style, LV_STATE_DEFAULT, LV_OPA_TRANSP);
|
||||
lv_style_set_text_color(&tab_style, LV_STATE_DEFAULT, LV_COLOR_WHITE);
|
||||
lv_obj_add_style(tabview, LV_TABVIEW_PART_TAB_BTN, &tab_style);
|
||||
|
||||
// Style for the small indicator bar that appears below the active tab.
|
||||
lv_style_init(&indicator_style);
|
||||
lv_style_set_bg_color(&indicator_style, LV_STATE_DEFAULT, LV_COLOR_RED);
|
||||
lv_style_set_size(&indicator_style, LV_STATE_DEFAULT, 5);
|
||||
lv_obj_add_style(tabview, LV_TABVIEW_PART_INDIC, &indicator_style);
|
||||
|
||||
// Back to creating widgets...
|
||||
|
||||
// Add three tabs to the tabview
|
||||
lv_obj_t *tab1 = lv_tabview_add_tab(tabview, "Gauge");
|
||||
lv_obj_t *tab2 = lv_tabview_add_tab(tabview, "Chart");
|
||||
lv_obj_t *tab3 = lv_tabview_add_tab(tabview, "Canvas");
|
||||
|
||||
// And then add stuff in each tab...
|
||||
|
||||
// The first tab holds a gauge. To keep the demo simple, let's just use
|
||||
// the default style and range (0-100). See LittlevGL docs for options.
|
||||
gauge = lv_gauge_create(tab1, NULL);
|
||||
lv_obj_set_size(gauge, 186, 186);
|
||||
lv_obj_align(gauge, NULL, LV_ALIGN_CENTER, 0, 0);
|
||||
|
||||
// Second tab, make a chart...
|
||||
chart = lv_chart_create(tab2, NULL);
|
||||
lv_obj_set_size(chart, 200, 180);
|
||||
lv_obj_align(chart, NULL, LV_ALIGN_CENTER, 0, 0);
|
||||
lv_chart_set_type(chart, LV_CHART_TYPE_COLUMN);
|
||||
// For simplicity, we'll stick with the chart's default 10 data points:
|
||||
series = lv_chart_add_series(chart, LV_COLOR_RED);
|
||||
lv_chart_init_points(chart, series, 0);
|
||||
// Make each column shift left as new values enter on right:
|
||||
lv_chart_set_update_mode(chart, LV_CHART_UPDATE_MODE_SHIFT);
|
||||
|
||||
// Third tab is a canvas, which we'll fill with random colored lines.
|
||||
// LittlevGL draw functions only work on TRUE_COLOR canvas.
|
||||
canvas = lv_canvas_create(tab3, NULL);
|
||||
lv_canvas_set_buffer(canvas, canvas_buffer,
|
||||
CANVAS_WIDTH, CANVAS_HEIGHT, LV_IMG_CF_TRUE_COLOR);
|
||||
lv_obj_align(canvas, NULL, LV_ALIGN_CENTER, 0, 0);
|
||||
lv_canvas_fill_bg(canvas, LV_COLOR_WHITE, LV_OPA_100);
|
||||
|
||||
// Set up canvas line-drawing style based on defaults.
|
||||
// Later we'll change color settings when drawing each line.
|
||||
lv_draw_line_dsc_init(&draw_dsc);
|
||||
}
|
||||
|
||||
void setup(void) {
|
||||
Serial.begin(115200);
|
||||
|
||||
// Initialize display BEFORE glue setup
|
||||
tft.init(240, 240);
|
||||
tft.setRotation(TFT_ROTATION);
|
||||
analogWrite(TFT_BACKLIGHT, 255); // USE analogWrite() FOR GIZMO BACKLIGHT!
|
||||
|
||||
// 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(;;);
|
||||
}
|
||||
|
||||
lvgl_setup(); // Call UI-building function above
|
||||
|
||||
// Enable "A" and "B" buttons as inputs
|
||||
pinMode(4, INPUT_PULLDOWN); // Left (A) button
|
||||
pinMode(5, INPUT_PULLDOWN); // Right (B) button
|
||||
}
|
||||
|
||||
uint32_t prev_time = -1;
|
||||
|
||||
void loop(void) {
|
||||
|
||||
// Read left/right button inputs. Debouncing could be done better,
|
||||
// but this'll do for a quick simple demo. The arrangements of the
|
||||
// buttons is intentionally flipped here (pin 5, the "right" button,
|
||||
// moves one tab left, pin 4 the opposite) so the button placements
|
||||
// make sense while looking at the screen on the reverse side.
|
||||
if(digitalRead(5) == HIGH) {
|
||||
if(active_tab > 0) {
|
||||
active_tab--;
|
||||
}
|
||||
while(digitalRead(5) == HIGH); // Wait for button release
|
||||
} else if(digitalRead(4) == HIGH) {
|
||||
if(active_tab < 2) {
|
||||
active_tab++;
|
||||
}
|
||||
while(digitalRead(4) == HIGH); // Wait for button release
|
||||
}
|
||||
|
||||
// Change active tab if button pressings happened
|
||||
if(active_tab != prev_tab) {
|
||||
lv_tabview_set_tab_act(tabview, active_tab, true);
|
||||
prev_tab = active_tab;
|
||||
}
|
||||
|
||||
// Make the gauge sweep a full sine wave over timeCLUE
|
||||
lv_gauge_set_value(gauge, 0, (int)(50.5 + sin(millis() / 1000.0) * 50.0));
|
||||
|
||||
// About 2X a second, things happen on the other two tabs...
|
||||
uint32_t new_time = millis() / 500; // Current half-second
|
||||
if(new_time != prev_time) { // freshly elapsed
|
||||
prev_time = new_time;
|
||||
|
||||
// Add a new random item to the bar chart (old value shift left)
|
||||
lv_chart_set_next(chart, series, random(100));
|
||||
lv_chart_refresh(chart);
|
||||
|
||||
// Add a random line to the canvas
|
||||
lv_point_t points[2];
|
||||
points[0].x = random(CANVAS_WIDTH);
|
||||
points[0].y = random(CANVAS_HEIGHT);
|
||||
points[1].x = random(CANVAS_WIDTH);
|
||||
points[1].y = random(CANVAS_HEIGHT);
|
||||
draw_dsc.color.ch.red = random();
|
||||
draw_dsc.color.ch.green = random();
|
||||
draw_dsc.color.ch.blue = random();
|
||||
lv_canvas_draw_line(canvas, points, 2, &draw_dsc);
|
||||
// This forces the canvas to update (otherwise changes aren't
|
||||
// seen unless leaving and returning to the canvas tab):
|
||||
lv_canvas_set_buffer(canvas, canvas_buffer,
|
||||
CANVAS_WIDTH, CANVAS_HEIGHT, LV_IMG_CF_TRUE_COLOR);
|
||||
}
|
||||
|
||||
lv_task_handler(); // Call LittleVGL task handler periodically
|
||||
delay(5);
|
||||
}
|
||||
|
||||
// NOTE TO FUTURE SELF: this sketch is essentially the same as the CLUE
|
||||
// widgets example. If updating one sketch, make sure the other is kept
|
||||
// in sync. Aside from screen setup, differences include backlight control
|
||||
// and A/B button setup & read (CPX is active high, CLUE is active low).
|
|
@ -1,255 +0,0 @@
|
|||
// A more interesting example for LittlevGL on Adafruit PyPortal, showing
|
||||
// use of the touchscreen. Code's a little more complex than the
|
||||
// hello_pyportal example, so best get that working before trying this.
|
||||
// By default, as written, on regular (320x240) PyPortal the example is a
|
||||
// pretend calculator keypad, while PyPortal Titano (480x320) has a whole
|
||||
// keyboard (though you'll probably need a stylus). These just seemed the
|
||||
// right level of detail for their respective screens, but feel free to
|
||||
// override and try either for yourself.
|
||||
|
||||
// Prior Adafruit_LvGL_Glue users: see hello_changes example for updates!
|
||||
|
||||
#include <Adafruit_LvGL_Glue.h> // Always include this BEFORE lvgl.h!
|
||||
#include <lvgl.h>
|
||||
#include <TouchScreen.h>
|
||||
|
||||
#define DEMO_CALC 0
|
||||
#define DEMO_TEXT 1
|
||||
|
||||
#define TFT_D0 34 // PyPortal TFT pins
|
||||
#define TFT_WR 26
|
||||
#define TFT_DC 10
|
||||
#define TFT_CS 11
|
||||
#define TFT_RST 24
|
||||
#define TFT_RD 9
|
||||
#define TFT_BACKLIGHT 25
|
||||
#define YP A4 // PyPortal touchscreen pins
|
||||
#define XP A5
|
||||
#define YM A6
|
||||
#define XM A7
|
||||
|
||||
#if defined(ADAFRUIT_PYPORTAL_M4_TITANO)
|
||||
#include <Adafruit_HX8357.h>
|
||||
Adafruit_HX8357 tft(tft8bitbus, TFT_D0, TFT_WR, TFT_DC, TFT_CS, TFT_RST,
|
||||
TFT_RD);
|
||||
#define DEMO DEMO_TEXT // On Titano, do text/keyboard example
|
||||
#else
|
||||
#include <Adafruit_ILI9341.h>
|
||||
Adafruit_ILI9341 tft(tft8bitbus, TFT_D0, TFT_WR, TFT_DC, TFT_CS, TFT_RST,
|
||||
TFT_RD);
|
||||
#define DEMO DEMO_CALC // Smaller PyPortal, do keypad example
|
||||
#endif
|
||||
TouchScreen ts(XP, YP, XM, YM, 300);
|
||||
Adafruit_LvGL_Glue glue;
|
||||
|
||||
#if DEMO == DEMO_CALC
|
||||
|
||||
// "Pretend" calculator example. Please, PLEASE...do NOT implement the whole
|
||||
// calculator and submit as a pull request, because it will NOT be merged!
|
||||
// This sketch is meant only to be illustrative and not functional, just
|
||||
// showing LittlevGL + Adafruit display/touch tied together with a modest
|
||||
// amount of code. Even a simple but working calc would require WAY more
|
||||
// code, distracting from that core idea (and is a waste of PyPortal).
|
||||
// Daiso has better calculators for $1.50.
|
||||
|
||||
#define TFT_ROTATION 0 // Portrait orientation on PyPortal (USB top)
|
||||
|
||||
lv_obj_t *digits_label = NULL; // LittlevGL label object showing digits
|
||||
String digits = "0"; // Current digits string value
|
||||
bool hasDecimal = false; // Only allow one decimal point
|
||||
const char *buttons[] = { // Button matrix labels
|
||||
"7", "8", "9", "/", "\n",
|
||||
"4", "5", "6", "x", "\n",
|
||||
"1", "2", "3", "-", "\n",
|
||||
"0", ".", "=", "+", "" };
|
||||
|
||||
// This function processes events from the button matrix
|
||||
void button_event_handler(lv_obj_t *obj, lv_event_t event) {
|
||||
if(event == LV_EVENT_VALUE_CHANGED) {
|
||||
const char *txt = lv_btnmatrix_get_active_btn_text(obj);
|
||||
if(txt) { // NULL if pressed in frame area outside buttons
|
||||
if(txt[0] == '.') {
|
||||
// Decimal button pressed. Add decimal point to "digits" string
|
||||
// if it's not too long and there's no decimal present yet...
|
||||
if((digits.length() < 15) && !hasDecimal) {
|
||||
digits += '.';
|
||||
hasDecimal = true;
|
||||
}
|
||||
} else if((txt[0] >= '0') && (txt[0] <= '9')) {
|
||||
// Number button (0-9) pressed. If there's nothing currently
|
||||
// being displayed, take the digit literally. Otherwise, append
|
||||
// the new digit if the "digits" string is not too long.
|
||||
if(digits.equals("0")) {
|
||||
digits = txt[0];
|
||||
} else if(digits.length() < 15) {
|
||||
digits += txt[0];
|
||||
}
|
||||
} else {
|
||||
// Any other button, just reset the calculator display.
|
||||
// It's all just pretend.
|
||||
digits = "0";
|
||||
hasDecimal = false;
|
||||
}
|
||||
if(digits_label != NULL) {
|
||||
lv_label_set_text(digits_label, digits.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void lvgl_setup(void) {
|
||||
|
||||
// Just using the default styles for everything here, keeping
|
||||
// it basic. See the official LittlevGL docs and examples for
|
||||
// insights on applying styles.
|
||||
|
||||
// The calculator digits are held inside a LvGL container object
|
||||
// as this gives us a little more control over positioning.
|
||||
lv_obj_t *container = lv_cont_create(lv_scr_act(), NULL);
|
||||
lv_cont_set_fit(container, LV_FIT_NONE); // Don't auto fit
|
||||
lv_obj_set_size(container, tft.width(), 50); // Full width x 50 px
|
||||
|
||||
// Calculator digits are just a text label inside the container,
|
||||
// refreshed whenever the global "digits" string changes.
|
||||
digits_label = lv_label_create(container, NULL);
|
||||
lv_label_set_text(digits_label, digits.c_str());
|
||||
lv_obj_align(digits_label, NULL, LV_ALIGN_IN_TOP_LEFT, 20, 10);
|
||||
lv_label_set_long_mode(digits_label, LV_LABEL_LONG_CROP);
|
||||
lv_obj_set_size(digits_label, tft.width() - 40, 30);
|
||||
lv_label_set_align(digits_label, LV_LABEL_ALIGN_RIGHT);
|
||||
|
||||
// Fill the remaining space with the button matrix.
|
||||
lv_obj_t *button_matrix = lv_btnmatrix_create(lv_scr_act(), NULL);
|
||||
lv_btnmatrix_set_map(button_matrix, buttons);
|
||||
lv_obj_align(button_matrix, NULL, LV_ALIGN_IN_TOP_LEFT, 0, 50);
|
||||
lv_obj_set_size(button_matrix, tft.width(), tft.height() - 50);
|
||||
lv_obj_set_event_cb(button_matrix, button_event_handler);
|
||||
}
|
||||
|
||||
#else // Keyboard demo
|
||||
|
||||
// Keyboard example, lets you enter and edit text in a field. Even on a
|
||||
// PyPortal Titano it requires a stylus to be even half useful (fingertip
|
||||
// is possible if very patient), but having the option of a keyboard at
|
||||
// all on this device is pretty nifty!
|
||||
|
||||
#define TFT_ROTATION 3 // Landscape orientation on PyPortal (USB right)
|
||||
|
||||
lv_obj_t *textarea,
|
||||
*keyboard = NULL; // Created/deleted as needed
|
||||
|
||||
#if LV_USE_ANIMATION
|
||||
// Called after keyboard slides closed - deletes keyboard object
|
||||
void delete_keyboard(lv_anim_t * a) {
|
||||
lv_obj_del((lv_obj_t *)a->var);
|
||||
keyboard = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Called when the close or ok button is pressed on the keyboard
|
||||
void keyboard_event_handler(lv_obj_t *obj, lv_event_t event) {
|
||||
lv_keyboard_def_event_cb(keyboard, event);
|
||||
|
||||
if(event == LV_EVENT_APPLY || event == LV_EVENT_CANCEL) {
|
||||
#if LV_USE_ANIMATION
|
||||
// If animation is enabled, make keyboard slide away
|
||||
lv_anim_path_t path;
|
||||
lv_anim_path_init(&path);
|
||||
lv_anim_path_set_cb(&path, lv_anim_path_ease_in_out);
|
||||
lv_anim_t a;
|
||||
lv_anim_init(&a);
|
||||
lv_anim_set_exec_cb(&a, (lv_anim_exec_xcb_t)lv_obj_set_y);
|
||||
lv_anim_set_var(&a, keyboard);
|
||||
lv_anim_set_time(&a, 300);
|
||||
lv_anim_set_values(&a, lv_obj_get_y(keyboard), LV_VER_RES); // start, end
|
||||
lv_anim_set_path(&a, &path);
|
||||
lv_anim_set_ready_cb(&a, delete_keyboard);
|
||||
lv_anim_start(&a);
|
||||
#else
|
||||
lv_obj_del(keyboard);
|
||||
keyboard = NULL;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// Other clicks in the text area
|
||||
void text_area_event_handler(lv_obj_t *obj, lv_event_t event) {
|
||||
if(event == LV_EVENT_CLICKED) {
|
||||
|
||||
// Unsure why, but text area has an initial clicked event on
|
||||
// creation, causing the keyboard to appear. This is a hacky
|
||||
// workaround that just ignores the first click event, so
|
||||
// subsequent actual clicks bring up the keyboard.
|
||||
static bool first = true;
|
||||
if(first) {
|
||||
first = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if(keyboard == NULL) {
|
||||
// If not present, create keyboard object at bottom of screen
|
||||
keyboard = lv_keyboard_create(lv_scr_act(), NULL);
|
||||
lv_obj_set_size(keyboard, tft.width(), tft.height() * 7 / 16);
|
||||
lv_obj_align(keyboard, textarea, LV_ALIGN_IN_BOTTOM_MID, 0, 0);
|
||||
lv_keyboard_set_textarea(keyboard, textarea);
|
||||
lv_keyboard_set_cursor_manage(keyboard, true);
|
||||
lv_obj_set_event_cb(keyboard, keyboard_event_handler);
|
||||
|
||||
#if LV_USE_ANIMATION
|
||||
// If animation is enabled, make keyboard slide into place
|
||||
lv_anim_path_t path;
|
||||
lv_anim_path_init(&path);
|
||||
lv_anim_path_set_cb(&path, lv_anim_path_ease_in_out);
|
||||
lv_anim_t a;
|
||||
lv_anim_init(&a);
|
||||
lv_anim_set_exec_cb(&a, (lv_anim_exec_xcb_t)lv_obj_set_y);
|
||||
lv_anim_set_var(&a, keyboard);
|
||||
lv_anim_set_time(&a, 300);
|
||||
lv_anim_set_values(&a, LV_VER_RES, lv_obj_get_y(keyboard)); // start, end
|
||||
lv_anim_set_path(&a, &path);
|
||||
lv_anim_start(&a);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void lvgl_setup(void) {
|
||||
textarea = lv_textarea_create(lv_scr_act(), NULL);
|
||||
lv_obj_set_size(textarea, LV_HOR_RES, LV_VER_RES); // Whole screen
|
||||
lv_obj_align(textarea, NULL, LV_ALIGN_CENTER, 0, 0);
|
||||
lv_textarea_set_text(textarea, "This text is editable.");
|
||||
lv_obj_set_event_cb(textarea, text_area_event_handler);
|
||||
lv_textarea_set_cursor_pos(textarea, LV_TEXTAREA_CURSOR_LAST);
|
||||
}
|
||||
|
||||
#endif // end calculator / keyboard examples
|
||||
|
||||
void setup(void) {
|
||||
Serial.begin(115200);
|
||||
|
||||
// Initialize display BEFORE glue setup
|
||||
tft.begin();
|
||||
tft.setRotation(TFT_ROTATION);
|
||||
pinMode(TFT_BACKLIGHT, OUTPUT);
|
||||
digitalWrite(TFT_BACKLIGHT, HIGH);
|
||||
|
||||
// PyPortal touchscreen needs no init
|
||||
|
||||
// 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(;;);
|
||||
}
|
||||
|
||||
lvgl_setup(); // Call UI-building function above
|
||||
}
|
||||
|
||||
void loop(void) {
|
||||
lv_task_handler(); // Call LittleVGL task handler periodically
|
||||
delay(5);
|
||||
}
|
||||
|
||||
// NOTE TO FUTURE SELF: this sketch is largely similar to the FeatherWing
|
||||
// widgets example. If updating/bugfixing one sketch, make sure the other
|
||||
// is kept in sync.
|
|
@ -136,8 +136,8 @@ void Touch_Check(void(*rotconvert)(int16_t *x, int16_t *y)) {
|
|||
if (FT5206_found) {
|
||||
touched = FT5206_touched();
|
||||
if (touched) {
|
||||
touch_xp = FT5206_x();
|
||||
touch_yp = FT5206_y();
|
||||
raw_touch_xp = FT5206_x();
|
||||
raw_touch_yp = FT5206_y();
|
||||
}
|
||||
}
|
||||
#endif // USE_FT5206
|
||||
|
@ -146,14 +146,14 @@ void Touch_Check(void(*rotconvert)(int16_t *x, int16_t *y)) {
|
|||
if (XPT2046_found) {
|
||||
touched = XPT2046_touched();
|
||||
if (touched) {
|
||||
touch_xp = XPT2046_x();
|
||||
touch_yp = XPT2046_y();
|
||||
raw_touch_xp = XPT2046_x();
|
||||
raw_touch_yp = XPT2046_y();
|
||||
}
|
||||
}
|
||||
#endif // USE_XPT2046
|
||||
touch_xp = raw_touch_xp;
|
||||
touch_yp = raw_touch_yp;
|
||||
|
||||
raw_touch_xp = touch_xp;
|
||||
raw_touch_yp = touch_yp;
|
||||
if (touched) {
|
||||
was_touched = true;
|
||||
#ifdef USE_TOUCH_BUTTONS
|
||||
|
@ -185,10 +185,6 @@ void Touch_Check(void(*rotconvert)(int16_t *x, int16_t *y)) {
|
|||
#endif // USE_TOUCH_BUTTONS
|
||||
|
||||
} else {
|
||||
if (was_touched) {
|
||||
AddLog(LOG_LEVEL_DEBUG_MORE, "TS : released x=%i y=%i (raw x=%i y=%i)", touch_xp, touch_yp, raw_touch_xp, raw_touch_yp);
|
||||
was_touched = false;
|
||||
}
|
||||
#ifdef USE_M5STACK_CORE2
|
||||
for (uint32_t tbut = 0; tbut < 3; tbut++) {
|
||||
if (tbstate[tbut] & 1) {
|
||||
|
@ -201,6 +197,10 @@ void Touch_Check(void(*rotconvert)(int16_t *x, int16_t *y)) {
|
|||
#endif // USE_M5STACK_CORE2
|
||||
|
||||
rotconvert(&touch_xp, &touch_yp); // still do rot convert if not touched
|
||||
if (was_touched) {
|
||||
AddLog(LOG_LEVEL_DEBUG_MORE, "TS : released x=%i y=%i (raw x=%i y=%i)", touch_xp, touch_yp, raw_touch_xp, raw_touch_yp);
|
||||
was_touched = false;
|
||||
}
|
||||
#ifdef USE_TOUCH_BUTTONS
|
||||
CheckTouchButtons(touched, touch_xp, touch_yp);
|
||||
#endif // USE_TOUCH_BUTTONS
|
||||
|
|
Loading…
Reference in New Issue