Adding examples for the EYESPI BFF

Adding CP and Arduino example code for the EYESPI BFF. Both use the 1.54" 240x240 display.
This commit is contained in:
Liz 2023-06-26 15:51:34 -04:00
parent 035a6775de
commit 7f1493f854
4 changed files with 1004 additions and 4 deletions

View file

@ -20,7 +20,7 @@
// CONSTANTS, HEADERS and GLOBAL VARIABLES ---------------------------------
// *** EDIT THIS VALUE TO MATCH THE ADAFRUIT PRODUCT ID FOR YOUR DISPLAY: ***
#define SCREEN_PRODUCT_ID 5393
#define SCREEN_PRODUCT_ID 3787
// You can find the product ID several ways:
// - "PID" accompanies each line-item on your receipt or order details page.
// - Visit adafruit.com and search for EYESPI displays. On product pages,
@ -28,9 +28,9 @@
// - Check the comments in setup() later that reference various screens.
// **** EDIT PINS TO MATCH YOUR WIRING ****
#define TFT_CS 10 // To display chip-select pin
#define TFT_RST 9 // To display reset pin
#define TFT_DC 8 // To display data/command pin
#define TFT_CS PIN_SERIAL2_TX // To display chip-select pin
#define TFT_RST -1 // To display reset pin
#define TFT_DC PIN_SERIAL2_RX // To display data/command pin
// For the remaining pins, this code assumes display is wired to hardware SPI
// on the dev board's primary SPI interface. The display libraries can support
// secondary SPI (if present) or bitbang (software) SPI, but that's not

View file

@ -0,0 +1,674 @@
// SPDX-FileCopyrightText: 2022 Phillip Burgess for Adafruit Industries
//
// SPDX-License-Identifier: MIT
// Graphics example for EYESPI-capable color displays. This code:
// - Functions as a "Hello World" to verify that microcontroller and screen
// are communicating.
// - Demonstrates most of the drawing commands of the Adafruit_GFX library.
// - Showcases some techniques that might not be obvious or that aren't
// built-in but can be handled with a little extra code.
// It DOES NOT:
// - Support all Adafruit screens, ONLY EYESPI products at the time this was
// written! But it's easily adapted by looking at other examples.
// - Demonstrate the Adafruit_GFX_Button class, as that's unique to
// touch-capable displays. Again, other examples may cover this topic.
// This sketch is long, but a lot of it is comments to explain each step. You
// can copy just the parts you need as a starting point for your own projects,
// and strip comments once understood.
// CONSTANTS, HEADERS and GLOBAL VARIABLES ---------------------------------
// *** EDIT THIS VALUE TO MATCH THE ADAFRUIT PRODUCT ID FOR YOUR DISPLAY: ***
#define SCREEN_PRODUCT_ID 3787
// You can find the product ID several ways:
// - "PID" accompanies each line-item on your receipt or order details page.
// - Visit adafruit.com and search for EYESPI displays. On product pages,
// PID is shown just below product title, and is at the end of URLs.
// - Check the comments in setup() later that reference various screens.
// **** EDIT PINS TO MATCH YOUR WIRING ****
#define TFT_CS PIN_SERIAL2_TX // To display chip-select pin
#define TFT_RST -1 // To display reset pin
#define TFT_DC PIN_SERIAL2_RX // To display data/command pin
// For the remaining pins, this code assumes display is wired to hardware SPI
// on the dev board's primary SPI interface. The display libraries can support
// secondary SPI (if present) or bitbang (software) SPI, but that's not
// demonstrated here. See other examples for more varied interfacing options.
#include <Adafruit_GFX.h> // Core graphics library
#include <Fonts/FreeSansBold18pt7b.h> // A custom font
#if (SCREEN_PRODUCT_ID == 1480) || (SCREEN_PRODUCT_ID == 2090)
#include <Adafruit_ILI9341.h> // Library for ILI9341-based screens
Adafruit_ILI9341 display(TFT_CS, TFT_DC, TFT_RST);
#else
#include <Adafruit_ST7789.h> // Library for ST7789-based screens
Adafruit_ST7789 display(TFT_CS, TFT_DC, TFT_RST);
#endif
#define PAUSE 3000 // Delay (millisecondss) between examples
uint8_t rotate = 0; // Current screen orientation (0-3)
// setup() RUNS ONCE AT PROGRAM STARTUP ------------------------------------
void setup() {
// Initialize display hardware
#if (SCREEN_PRODUCT_ID == 5393) // 1.47" 320x172 round-rect TFT
#define CORNER_RADIUS 22
display.init(172, 320);
#elif (SCREEN_PRODUCT_ID == 3787) // 1.54" 240x240 TFT
display.init(240, 240);
#elif (SCREEN_PRODUCT_ID == 5206) // 1.69" 280x240 round-rect TFT
#define CORNER_RADIUS 43
display.init(240, 280);
#elif (SCREEN_PRODUCT_ID == 5394) // 1.9" 320x170 TFT
display.init(170, 320);
#else // All ILI9341 TFTs (320x240)
display.begin();
#endif
#if !defined(CORNER_RADIUS)
#define CORNER_RADIUS 0
#endif
// OPTIONAL: default TFT SPI speed is fairly conservative, you can try
// overriding here for faster screen updates. Actual SPI speed may be less
// depending on microcontroller's capabilities. Max reliable speed also
// depends on wiring length and tidyness.
//display.setSPISpeed(40000000);
}
// MAIN LOOP, REPEATS FOREVER ----------------------------------------------
void loop() {
// Each of these functions demonstrates a different Adafruit_GFX concept:
show_shapes();
show_charts();
show_basic_text();
show_char_map();
show_custom_text();
show_bitmap();
#if !defined(AVR)
// The full set of examples (plus the custom font) won't fit on an 8-bit
// Arduino, something's got to go. You can try out this one IF the other
// examples are disabled instead.
show_canvas();
#endif
if (++rotate > 3) rotate = 0; // Cycle through screen rotations 0-3
display.setRotation(rotate); // Takes effect on next drawing command
}
// BASIC SHAPES EXAMPLE ----------------------------------------------------
void show_shapes() {
// Draw outlined and filled shapes. This demonstrates:
// - Enclosed shapes supported by GFX (points & lines are shown later).
// - Adapting to different-sized displays, and to rounded corners.
const int16_t cx = display.width() / 2; // Center of screen =
const int16_t cy = display.height() / 2; // half of width, height
int16_t minor = min(cx, cy); // Lesser of half width or height
// Shapes will be drawn in a square region centered on the screen. But one
// particular screen -- rounded 240x280 ST7789 -- has VERY rounded corners
// that would clip a couple of shapes if drawn full size. If using that
// screen type, reduce area by a few pixels to avoid drawing in corners.
if (CORNER_RADIUS > 40) minor -= 4;
const uint8_t pad = 5; // Space between shapes is 2X this
const int16_t size = minor - pad; // Shapes are this width & height
const int16_t half = size / 2; // 1/2 of shape size
display.fillScreen(0); // Start by clearing the screen; color 0 = black
// Draw outline version of basic shapes: rectangle, triangle, circle and
// rounded rectangle in different colors. Rather than hardcoded numbers
// for position and size, some arithmetic helps adapt to screen dimensions.
display.drawRect(cx - minor, cy - minor, size, size, 0xF800);
display.drawTriangle(cx + pad, cy - pad, cx + pad + half, cy - minor,
cx + minor - 1, cy - pad, 0x07E0);
display.drawCircle(cx - pad - half, cy + pad + half, half, 0x001F);
display.drawRoundRect(cx + pad, cy + pad, size, size, size / 5, 0xFFE0);
delay(PAUSE);
// Draw same shapes, same positions, but filled this time.
display.fillRect(cx - minor, cy - minor, size, size, 0xF800);
display.fillTriangle(cx + pad, cy - pad, cx + pad + half, cy - minor,
cx + minor - 1, cy - pad, 0x07E0);
display.fillCircle(cx - pad - half, cy + pad + half, half, 0x001F);
display.fillRoundRect(cx + pad, cy + pad, size, size, size / 5, 0xFFE0);
delay(PAUSE);
} // END SHAPE EXAMPLE
// CHART EXAMPLES ----------------------------------------------------------
void show_charts() {
// Draw some graphs and charts. GFX library doesn't handle these as native
// object types, but it only takes a little code to build them from simple
// shapes. This demonstrates:
// - Drawing points and horizontal, vertical and arbitrary lines.
// - Adapting to different-sized displays.
// - Graphics being clipped off edge.
// - Use of negative values to draw shapes "backward" from an anchor point.
// - C technique for finding array size at runtime (vs hardcoding).
display.fillScreen(0); // Clear screen
const int16_t cx = display.width() / 2; // Center of screen =
const int16_t cy = display.height() / 2; // half of width, height
const int16_t minor = min(cx, cy); // Lesser of half width or height
const int16_t major = max(cx, cy); // Greater of half width or height
// Let's start with a relatively simple sine wave graph with axes.
// Draw graph axes centered on screen. drawFastHLine() and drawFastVLine()
// need fewer arguments than normal 2-point line drawing shown later.
display.drawFastHLine(0, cy, display.width(), 0x0210); // Dark blue
display.drawFastVLine(cx, 0, display.height(), 0x0210);
// Then draw some tick marks along the axes. To keep this code simple,
// these aren't to any particular scale, but a real program may want that.
// The loop here draws them from the center outward and pays no mind
// whether the screen is rectangular; any ticks that go off-screen will
// be clipped by the library.
for (uint8_t i=1; i<=10; i++) {
// The Arduino map() function scales an input value (e.g. "i") from an
// input range (0-10 here) to an output range (0 to major-1 here).
// Very handy for making graphics adjust to different screens!
int16_t n = map(i, 0, 10, 0, major - 1); // Tick offset relative to center point
display.drawFastVLine(cx - n, cy - 5, 11, 0x210);
display.drawFastVLine(cx + n, cy - 5, 11, 0x210);
display.drawFastHLine(cx - 5, cy - n, 11, 0x210);
display.drawFastHLine(cx - 5, cy + n, 11, 0x210);
}
// Then draw sine wave over this using GFX drawPixel() function.
for (int16_t x=0; x<display.width(); x++) { // Each column of screen...
// Note the inverted Y axis here (cy-value rather than cy+value)
// because GFX, like most graphics libraries, has +Y heading down,
// vs. classic Cartesian coords which have +Y heading up.
int16_t y = cy - (int16_t)(sin((x - cx) * 0.05) * (float)minor * 0.5);
display.drawPixel(x, y, 0xFFFF);
}
delay(PAUSE);
// Next, let's draw some charts...
// NOTE: some other examples in this code take extra steps to avoid placing
// anything off in the rounded corners of certain displays. The charts do
// not. It's *possible* but would introduce a lot of complexity into code
// that's trying to show the basics. We'll leave the clipped charts here as
// a teachable moment: not all content suits all displays.
// A list of data to plot. These are Y values only; X assumed equidistant.
const uint8_t data[] = { 31, 42, 36, 58, 67, 88 }; // Percentages, 0-100
const uint8_t num_points = sizeof data / sizeof data[0]; // Length of data[] list
display.fillScreen(0); // Clear screen
display.setFont(); // Use default (built-in) font
display.setTextSize(2); // and 2X size for chart label
// Chart label is centered manually; 144 is the width in pixels of
// "Widget Sales" at 2X scale (12 chars * 6 px * 2 = 144). A later example
// shows automated centering based on string.
display.setCursor((display.width() - 144) / 2, 0);
display.print(F("Widget Sales")); // F("string") is in program memory, not RAM
// The chart-drawing code is then written to skip the top 20 rows where
// this label is located.
// First, a line chart, connecting the values point-to-point:
// Draw a grid of lines to provide scale & an interesting background.
for (uint8_t i=0; i<11; i++) {
int16_t x = map(i, 0, 10, 0, display.width() - 1); // Scale grid X to screen
display.drawFastVLine(x, 20, display.height(), 0x001F);
int16_t y = map(i, 0, 10, 20, display.height() - 1); // Scale grid Y to screen
display.drawFastHLine(0, y, display.width(), 0x001F);
}
// And then draw lines connecting data points. Load up the first point...
int16_t prev_x = 0;
int16_t prev_y = map(data[0], 0, 100, display.height() - 1, 20);
// Then connect lines to each subsequent point...
for (uint8_t i=1; i<num_points; i++) {
int16_t new_x = map(i, 0, num_points - 1, 0, display.width() - 1);
int16_t new_y = map(data[i], 0, 100, display.height() - 1, 20);
display.drawLine(prev_x, prev_y, new_x, new_y, 0x07FF);
prev_x = new_x;
prev_y = new_y;
}
// For visual interest, let's add a circle around each data point. This is
// done in a second pass so the circles are always drawn "on top" of lines.
for (uint8_t i=0; i<num_points; i++) {
int16_t x = map(i, 0, num_points - 1, 0, display.width() - 1);
int16_t y = map(data[i], 0, 100, display.height() - 1, 20);
display.drawCircle(x, y, 5, 0xFFFF);
}
delay(PAUSE);
// Then a bar chart of the same data...
// Erase the old chart but keep the label at top.
display.fillRect(0, 20, display.width(), display.height() - 20, 0);
// Just draw the Y axis lines; bar chart doesn't really need X lines.
for (uint8_t i=0; i<11; i++) {
int16_t y = map(i, 0, 10, 20, display.height() - 1);
display.drawFastHLine(0, y, display.width(), 0x001F);
}
int bar_width = display.width() / num_points - 4; // 2px pad to either side
for (uint8_t i=0; i<num_points; i++) {
int16_t x = map(i, 0, num_points, 0, display.width()) + 2; // Left edge of bar
int16_t height = map(data[i], 0, 100, 0, display.height() - 20);
// Some GFX functions (rects, H/V lines and similar) can accept negative
// width/height values. What this does is anchor the shape at the right or
// bottom coordinate (rather than the usual left/top) and draw back from
// there, hence the -height here (bar is anchored at bottom of screen):
display.fillRect(x, display.height() - 1, bar_width, -height, 0xFFE0);
}
delay(PAUSE);
} // END CHART EXAMPLES
// TEXT ALIGN FUNCTIONS ----------------------------------------------------
// Adafruit_GFX only handles left-aligned text. This is normal and by design;
// it's a rare need that would further strain AVR by incurring a ton of extra
// code to properly handle, and some details would confuse. If needed, these
// functions give a fair approximation, with the "gotchas" that multi-line
// input won't work, and this operates only as a println(), not print()
// (though, unlike println(), cursor X does not reset to column 0, instead
// returning to initial column and downward by font's line spacing). If you
// can work with those constraints, it's a modest amount of code to copy
// into a project. Or, if your project only needs one or two aligned strings,
// simply use getTextBounds() for a bounding box and work from there.
// DO NOT ATTEMPT TO MAKE THIS A GFX-NATIVE FEATURE, EVERYTHING WILL BREAK.
typedef enum { // Alignment options passed to functions below
GFX_ALIGN_LEFT,
GFX_ALIGN_CENTER,
GFX_ALIGN_RIGHT
} GFXalign;
// Draw text aligned relative to current cursor position. Arguments:
// gfx : An Adafruit_GFX-derived type (e.g. display or canvas object).
// str : String to print (as a char *).
// align : One of the GFXalign values declared above.
// GFX_ALIGN_LEFT is normal left-aligned println() behavior.
// GFX_ALIGN_CENTER prints centered on cursor pos.
// GFX_ALIGN_RIGHT prints right-aligned to cursor pos.
// Cursor advances down one line a la println(). Column is unchanged.
void print_aligned(Adafruit_GFX &gfx, const char *str,
GFXalign align = GFX_ALIGN_LEFT) {
uint16_t w, h;
int16_t x, y, cursor_x, cursor_x_save;
cursor_x = cursor_x_save = gfx.getCursorX();
gfx.getTextBounds(str, 0, gfx.getCursorY(), &x, &y, &w, &h);
if (align == GFX_ALIGN_RIGHT) cursor_x -= w;
else if (align == GFX_ALIGN_CENTER) cursor_x -= w / 2;
//gfx.drawRect(cursor_x, y, w, h, 0xF800); // Debug rect
gfx.setCursor(cursor_x - x, gfx.getCursorY()); // Center/right align
gfx.println(str);
gfx.setCursor(cursor_x_save, gfx.getCursorY()); // Restore cursor X
}
// Equivalent function for strings in flash memory (e.g. F("Foo")). Body
// appears identical to above function, but with C++ overloading it it works
// from flash instead of RAM. Any changes should be made in both places.
void print_aligned(Adafruit_GFX &gfx, const __FlashStringHelper *str,
GFXalign align = GFX_ALIGN_LEFT) {
uint16_t w, h;
int16_t x, y, cursor_x, cursor_x_save;
cursor_x = cursor_x_save = gfx.getCursorX();
gfx.getTextBounds(str, 0, gfx.getCursorY(), &x, &y, &w, &h);
if (align == GFX_ALIGN_RIGHT) cursor_x -= w;
else if (align == GFX_ALIGN_CENTER) cursor_x -= w / 2;
//gfx.drawRect(cursor_x, y, w, h, 0xF800); // Debug rect
gfx.setCursor(cursor_x - x, gfx.getCursorY()); // Center/right align
gfx.println(str);
gfx.setCursor(cursor_x_save, gfx.getCursorY()); // Restore cursor X
}
// Equivalent function for Arduino Strings; converts to C string (char *)
// and calls corresponding print_aligned() implementation.
void print_aligned(Adafruit_GFX &gfx, const String &str,
GFXalign align = GFX_ALIGN_LEFT) {
print_aligned(gfx, const_cast<char *>(str.c_str()));
}
// TEXT EXAMPLES -----------------------------------------------------------
// This section demonstrates:
// - Using the default 5x7 built-in font, including scaling in each axis.
// - How to access all characters of this font, including symbols.
// - Using a custom font, including alignment techniques that aren't a normal
// part of the GFX library (uses functions above).
void show_basic_text() {
// Show text scaling with built-in font.
display.fillScreen(0);
display.setFont(); // Use default font
display.setCursor(0, CORNER_RADIUS); // Initial cursor position
display.setTextSize(1); // Default size
display.println(F("Standard built-in font"));
display.setTextSize(2);
display.println(F("BIG TEXT"));
display.setTextSize(3);
// "BIGGER TEXT" won't fit on narrow screens, so abbreviate there.
display.println((display.width() >= 200) ? F("BIGGER TEXT") : F("BIGGER"));
display.setTextSize(2, 4);
display.println(F("TALL and"));
display.setTextSize(4, 2);
display.println(F("WIDE"));
delay(PAUSE);
} // END BASIC TEXT EXAMPLE
void show_char_map() {
// "Code Page 437" is a name given to the original IBM PC character set.
// Despite age and limited language support, still seen in small embedded
// settings as it has some useful symbols and accented characters. The
// default 5x7 pixel font of Adafruit_GFX is modeled after CP437. This
// function draws a table of all the characters & explains some issues.
// There are 256 characters in all. Draw table as 16 rows of 16 columns,
// plus hexadecimal row & column labels. How big can each cell be drawn?
const int cell_size = min(display.width(), display.height()) / 17;
if (cell_size < 8) return; // Screen is too small for table, skip example.
const int total_size = cell_size * 17; // 16 cells + 1 row or column label
// Set up for default 5x7 font at 1:1 scale. Custom fonts are NOT used
// here as most are only 128 characters to save space (the "7b" at the
// end of many GFX font names means "7 bits," i.e. 128 characters).
display.setFont();
display.setTextSize(1);
// Early Adafruit_GFX was missing one symbol, throwing off some indices!
// But fixing the library would break MANY existing sketches that relied
// on the degrees symbol and others. The default behavior is thus "broken"
// to keep older code working. New code can access the CORRECT full CP437
// table by calling this function like so:
display.cp437(true);
display.fillScreen(0);
const int16_t x = (display.width() - total_size) / 2; // Upper left corner of
int16_t y = (display.height() - total_size) / 2; // table centered on screen
if (y >= 4) { // If there's a little extra space above & below, scoot table
y += 4; // down a few pixels and show a message centered at top.
display.setCursor((display.width() - 114) / 2, 0); // 114 = pixel width
display.print(F("CP437 Character Map")); // of this message
}
const int16_t inset_x = (cell_size - 5) / 2; // To center each character within cell,
const int16_t inset_y = (cell_size - 8) / 2; // compute X & Y offset from corner.
for (uint8_t row=0; row<16; row++) { // 16 down...
// Draw row and columm headings as hexadecimal single digits. To get the
// hex value for a specific character, combine the left & top labels,
// e.g. Pi symbol is row E, column 3, thus: display.print((char)0xE3);
display.setCursor(x + (row + 1) * cell_size + inset_x, y + inset_y);
display.print(row, HEX); // This actually draws column labels
display.setCursor(x + inset_x, y + (row + 1) * cell_size + inset_y);
display.print(row, HEX); // and THIS is the row labels
for (uint8_t col=0; col<16; col++) { // 16 across...
if ((row + col) & 1) { // Fill alternating cells w/gray
display.fillRect(x + (col + 1) * cell_size, y + (row + 1) * cell_size,
cell_size, cell_size, 0x630C);
}
// drawChar() bypasses usual cursor positioning to go direct to an X/Y
// location. If foreground & background match, it's drawn transparent.
display.drawChar(x + (col + 1) * cell_size + inset_x,
y + (row + 1) * cell_size + inset_y, row * 16 + col,
0xFFFF, 0xFFFF, 1);
}
}
delay(PAUSE * 2);
} // END CHAR MAP EXAMPLE
void show_custom_text() {
// Show use of custom fonts, plus how to do center or right alignment
// using some additional functions provided earlier.
display.fillScreen(0);
display.setFont(&FreeSansBold18pt7b);
display.setTextSize(1);
display.setTextWrap(false); // Allow text off edges
// Get "M height" of custom font and move initial base line there:
uint16_t w, h;
int16_t x, y;
display.getTextBounds("M", 0, 0, &x, &y, &w, &h);
// On rounded 240x280 display in tall orientation, "Custom Font" gets
// clipped by top corners. Scoot text down a few pixels in that one case.
if (CORNER_RADIUS && (display.height() == 280)) h += 20;
display.setCursor(display.width() / 2, h);
if (display.width() >= 200) {
print_aligned(display, F("Custom Font"), GFX_ALIGN_CENTER);
display.setCursor(0, display.getCursorY() + 10);
print_aligned(display, F("Align Left"), GFX_ALIGN_LEFT);
display.setCursor(display.width() / 2, display.getCursorY());
print_aligned(display, F("Centered"), GFX_ALIGN_CENTER);
// Small rounded screen, when oriented the wide way, "Right" gets
// clipped by bottom right corner. Scoot left to compensate.
int16_t x_offset = (CORNER_RADIUS && (display.height() < 200)) ? 15 : 0;
display.setCursor(display.width() - x_offset, display.getCursorY());
print_aligned(display, F("Align Right"), GFX_ALIGN_RIGHT);
} else {
// On narrow screens, use abbreviated messages
print_aligned(display, F("Font &"), GFX_ALIGN_CENTER);
print_aligned(display, F("Align"), GFX_ALIGN_CENTER);
display.setCursor(0, display.getCursorY() + 10);
print_aligned(display, F("Left"), GFX_ALIGN_LEFT);
display.setCursor(display.width() / 2, display.getCursorY());
print_aligned(display, F("Center"), GFX_ALIGN_CENTER);
display.setCursor(display.width(), display.getCursorY());
print_aligned(display, F("Right"), GFX_ALIGN_RIGHT);
}
delay(PAUSE);
} // END CUSTOM FONT EXAMPLE
// BITMAP EXAMPLE ----------------------------------------------------------
// This section demonstrates:
// - Embedding a small bitmap in the code (flash memory).
// - Drawing that bitmap in various colors, and transparently (only '1' bits
// are drawn; '0' bits are skipped, leaving screen contents in place).
// - Use of the color565() function to decimate 24-bit RGB to 16 bits.
#define HEX_WIDTH 16 // Bitmap width in pixels
#define HEX_HEIGHT 16 // Bitmap height in pixels
// Bitmap data. PROGMEM ensures it's in flash memory (not RAM). And while
// it would be valid to leave the brackets empty here (i.e. hex_bitmap[]),
// having dimensions with a little math makes the compiler verify the
// correct number of bytes are present in the list.
PROGMEM const uint8_t hex_bitmap[(HEX_WIDTH + 7) / 8 * HEX_HEIGHT] = {
0b00000001, 0b10000000,
0b00000111, 0b11100000,
0b00011111, 0b11111000,
0b01111111, 0b11111110,
0b01111111, 0b11111110,
0b01111111, 0b11111110,
0b01111111, 0b11111110,
0b01111111, 0b11111110,
0b01111111, 0b11111110,
0b01111111, 0b11111110,
0b01111111, 0b11111110,
0b01111111, 0b11111110,
0b01111111, 0b11111110,
0b00011111, 0b11111000,
0b00000111, 0b11100000,
0b00000001, 0b10000000,
};
#define Y_SPACING (HEX_HEIGHT - 2) // Used by code below for positioning
void show_bitmap() {
display.fillScreen(0);
// Not screen center, but UL coordinates of center hexagon bitmap
const int16_t center_x = (display.width() - HEX_WIDTH) / 2;
const int16_t center_y = (display.height() - HEX_HEIGHT) / 2;
const uint8_t steps = min((display.height() - HEX_HEIGHT) / Y_SPACING,
display.width() / HEX_WIDTH - 1) / 2;
display.drawBitmap(center_x, center_y, hex_bitmap, HEX_WIDTH, HEX_HEIGHT,
0xFFFF); // Draw center hexagon in white
// Tile the hexagon bitmap repeatedly in a range of hues. Don't mind the
// bit of repetition in the math, the optimizer easily picks this up.
// Also, if math looks odd, keep in mind "PEMDAS" operator precedence;
// multiplication and division occur before addition and subtraction.
for (uint8_t a=0; a<=steps; a++) {
for (uint8_t b=1; b<=steps; b++) {
display.drawBitmap( // Right section centered red: a = green, b = blue
center_x + (a + b) * HEX_WIDTH / 2,
center_y + (a - b) * Y_SPACING,
hex_bitmap, HEX_WIDTH, HEX_HEIGHT,
display.color565(255, 255 - 255 * a / steps, 255 - 255 * b / steps));
display.drawBitmap( // UL section centered green: a = blue, b = red
center_x - b * HEX_WIDTH + a * HEX_WIDTH / 2,
center_y - a * Y_SPACING,
hex_bitmap, HEX_WIDTH, HEX_HEIGHT,
display.color565(255 - 255 * b / steps, 255, 255 - 255 * a / steps));
display.drawBitmap( // LL section centered blue: a = red, b = green
center_x - a * HEX_WIDTH + b * HEX_WIDTH / 2,
center_y + b * Y_SPACING,
hex_bitmap, HEX_WIDTH, HEX_HEIGHT,
display.color565(255 - 255 * a / steps, 255 - 255 * b / steps, 255));
}
}
delay(PAUSE);
} // END BITMAP EXAMPLE
// CANVAS EXAMPLE ----------------------------------------------------------
// This section demonstrates:
// - How to refresh changing values onscreen without erase/redraw flicker.
// - Using an offscreen canvas. It's similar to a bitmap above, but rather
// than a fixed pattern in flash memory, it's drawable like the screen.
// - More tips on text alignment, and adapting to different screen sizes.
#define PADDING 6 // Pixels between axis label and value
void show_canvas() {
// For this example, let's suppose we want to display live readings from a
// sensor such as a three-axis accelerometer, something like:
// X: (number)
// Y: (number)
// Z: (number)
// To look extra classy, we want a custom font, and the labels for each
// axis are right-aligned so the ':' characters line up...
display.setFont(&FreeSansBold18pt7b); // Use a custom font
display.setTextSize(1); // and reset to 1:1 scale
const char *label[] = { "X:", "Y:", "Z:" }; // Labels for each axis
const uint16_t color[] = { 0xF800, 0x07E0, 0x001F }; // Colors for each value
// To get the labels right-aligned, one option would be simple trial and
// error to find a column that looks good and doesn't clip anything off.
// Let's do this dynamically though, so it adapts to any font or labels!
// Start by finding the widest of the label strings:
uint16_t w, h, max_w = 0;
int16_t x, y;
for (uint8_t i=0; i<3; i++) { // For each label...
display.getTextBounds(label[i], 0, 0, &x, &y, &w, &h);
if (w > max_w) max_w = w; // Keep track of widest label
}
// Rounded corners throwing us a curve again. If needed, scoot everything
// to the right a bit on wide displays, down a bit on tall ones.
int16_t y_offset = 0;
if (display.width() > display.height()) max_w += CORNER_RADIUS;
else y_offset = CORNER_RADIUS;
// Now we have max_w for right-aligning the labels. Before we draw them
// though...in order to perform flicker-free updates, the numbers we show
// will be rendered in either a GFXcanvas1 or GFXcanvas16 object; a 1-bit
// or 16-bit offscreen bitmap, RAM permitting. The correct size for this
// canvas could also be trial-and-errored, but again let's make this adapt
// automatically. The width of the canvas will span from max_w (plus a few
// pixels for padding) to the right edge. But the height? Looking at an
// uppercase 'M' can work in many situations, but some fonts have ascenders
// and descenders on digits, and in some locales a comma (extending below
// the baseline) is the decimal separator. Feed ALL the numeric chars into
// getTextBounds() for a cumulative height:
display.setTextWrap(false); // Keep on one line
display.getTextBounds(F("0123456789.,-"), 0, 0, &x, &y, &w, &h);
// Now declare a GFXcanvas16 object based on the computed width & height:
GFXcanvas16 canvas16(display.width() - max_w - PADDING, h);
// Small devices (e.g. ATmega328p) will almost certainly lack enough RAM
// for the canvas. Check if canvas buffer exists. If not, fall back on
// using a 1-bit (rather than 16-bit) canvas. Much more RAM friendly, but
// not as fast to draw. If a project doesn't require super interactive
// updates, consider just going straight for the more compact Canvas1.
if (canvas16.getBuffer()) {
// If here, 16-bit canvas allocated successfully! Point of interest,
// only one canvas is needed for this example, we can reuse it for all
// three numbers because the regions are the same size.
// display and canvas are independent drawable objects; must explicitly
// set the same custom font to use on the canvas now:
canvas16.setFont(&FreeSansBold18pt7b);
// Clear display and print labels. Once drawn, these remain untouched.
display.fillScreen(0);
display.setCursor(max_w, -y + y_offset); // Set baseline for first row
for (uint8_t i=0; i<3; i++) print_aligned(display, label[i], GFX_ALIGN_RIGHT);
// Last part now is to print numbers on the canvas and copy the canvas to
// the display, repeating for several seconds...
uint32_t elapsed, startTime = millis();
while ((elapsed = (millis() - startTime)) <= PAUSE * 2) {
for (uint8_t i=0; i<3; i++) { // For each label...
canvas16.fillScreen(0); // fillScreen() in this case clears canvas
canvas16.setCursor(0, -y); // Reset baseline for custom font
canvas16.setTextColor(color[i]);
// These aren't real accelerometer readings, just cool-looking numbers.
// Notice we print to the canvas, NOT the display:
canvas16.print(sin(elapsed / 200.0 + (float)i * M_PI * 2.0 / 3.0), 5);
// And HERE is the secret sauce to flicker-free updates. Canvas details
// can be passed to the drawRGBBitmap() function, which fully overwrites
// prior screen contents in that area. yAdvance is font line spacing.
display.drawRGBBitmap(max_w + PADDING, i * FreeSansBold18pt7b.yAdvance +
y_offset, canvas16.getBuffer(), canvas16.width(),
canvas16.height());
}
}
} else {
// Insufficient RAM for Canvas16. Try declaring a 1-bit canvas instead...
GFXcanvas1 canvas1(display.width() - max_w - PADDING, h);
// If even this smaller object fails, can't proceed, cancel this example.
if (!canvas1.getBuffer()) return;
// Remainder here is nearly identical to the code above, simply using a
// different canvas type. It's stripped of most comments for brevity.
canvas1.setFont(&FreeSansBold18pt7b);
display.fillScreen(0);
display.setCursor(max_w, -y + y_offset);
for (uint8_t i=0; i<3; i++) print_aligned(display, label[i], GFX_ALIGN_RIGHT);
uint32_t elapsed, startTime = millis();
while ((elapsed = (millis() - startTime)) <= PAUSE * 2) {
for (uint8_t i=0; i<3; i++) {
canvas1.fillScreen(0);
canvas1.setCursor(0, -y);
canvas1.print(sin(elapsed / 200.0 + (float)i * M_PI * 2.0 / 3.0), 5);
// Here's the secret sauce to flicker-free updates with GFXcanvas1.
// Canvas details can be passed to the drawBitmap() function, and by
// specifying both a foreground AND BACKGROUND color (0), this will fully
// overwrite/erase prior screen contents in that area (vs transparent).
display.drawBitmap(max_w + PADDING, i * FreeSansBold18pt7b.yAdvance +
y_offset, canvas1.getBuffer(), canvas1.width(),
canvas1.height(), color[i], 0);
}
}
}
// Because canvas object was declared locally to this function, it's freed
// automatically when the function returns; no explicit delete needed.
} // END CANVAS EXAMPLE

View file

@ -0,0 +1,326 @@
# SPDX-FileCopyrightText: 2023 Liz Clark for Adafruit Industries
# SPDX-FileCopyrightText: Adapted from Phil B.'s 16bit_hello Arduino Code
#
# SPDX-License-Identifier: MIT
'''Graphics example for the EYESPI BFF'''
import gc
import math
from random import randint
import time
import displayio
import board
import vectorio
import terminalio
import simpleio
from adafruit_st7789 import ST7789
from adafruit_bitmap_font import bitmap_font
from adafruit_display_text import label, wrap_text_to_lines
from adafruit_display_shapes.rect import Rect
from adafruit_display_shapes.circle import Circle
from adafruit_display_shapes.roundrect import RoundRect
from adafruit_display_shapes.triangle import Triangle
from adafruit_display_shapes.line import Line
displayio.release_displays()
spi = board.SPI()
tft_cs = board.TX
tft_dc = board.RX
display_bus = displayio.FourWire(
spi, command=tft_dc, chip_select=tft_cs, reset=None
)
display = ST7789(display_bus, width=240, height=240, rowstart=80)
bitmap = displayio.Bitmap(display.width, display.height, 3)
red = 0xff0000
yellow = 0xcccc00
orange = 0xff5500
blue = 0x0000ff
pink = 0xff00ff
purple = 0x5500ff
white = 0xffffff
green = 0x00ff00
aqua = 0x125690
palette = displayio.Palette(3)
palette[0] = 0x000000 # black
palette[1] = white
palette[2] = yellow
palette.make_transparent(0)
tile_grid = displayio.TileGrid(bitmap, pixel_shader=palette)
group = displayio.Group()
def clean_up(group_name):
for _ in range(len(group_name)):
group_name.pop()
gc.collect()
def show_shapes():
gc.collect()
cx = int(display.width / 2)
cy = int(display.height / 2)
minor = min(cx, cy)
pad = 5
size = minor - pad
half = int(size / 2)
rect = Rect(cx - minor, cy - minor, size, size, stroke = 1, fill=red, outline = red)
tri = Triangle(cx + pad, cy - pad, cx + pad + half, cy - minor,
cx + minor - 1, cy - pad, fill=green, outline = green)
circ = Circle(cx - pad - half, cy + pad + half, half, fill=blue, stroke = 1, outline = blue)
rnd = RoundRect(cx + pad, cy + pad, size, size, int(size / 5), stroke = 1,
fill=yellow, outline = yellow)
group.append(rect)
group.append(tri)
group.append(circ)
group.append(rnd)
rect.fill = None
tri.fill = None
circ.fill = None
rnd.fill = None
time.sleep(2)
rect.fill = red
tri.fill = green
circ.fill = blue
rnd.fill = yellow
time.sleep(2)
clean_up(group)
del rect
del tri
del circ
del rnd
gc.collect()
def sine_chart():
gc.collect()
cx = int(display.width / 2)
cy = int(display.height / 2)
minor = min(cx, cy)
major = max(cx, cy)
group.append(Line(cx, 0, cx, display.height, blue)) # v
group.append(Line(0, cy, display.width, cy, blue)) # h
for i in range(10):
_n = simpleio.map_range(i, 0, 10, 0, major - 1)
n = int(_n)
group.append(Line(cx - n, cy - 5, cx - n, (cy - 5) + 11, blue)) # v
group.append(Line(cx + n, cy - 5, cx + n, (cy - 5) + 11, blue)) # v
group.append(Line(cx - 5, cy - n, (cx - 5) + 11, cy - n, blue)) # h
group.append(Line(cx - 5, cy + n, (cx - 5) + 11, cy + n, blue)) # h
for x in range(display.width):
y = cy - int(math.sin((x - cx) * 0.05) * float(minor * 0.5))
bitmap[x, y] = 1
group.append(tile_grid)
time.sleep(2)
clean_up(group)
def widget0():
gc.collect()
data = [31, 42, 36, 58, 67, 88]
num_points = len(data)
text_area = label.Label(terminalio.FONT, text="Widget Sales", color=white)
text_area.anchor_point = (0.5, 0.0)
text_area.anchored_position = (display.width / 2, 3)
group.append(text_area)
for i in range(11):
_x = simpleio.map_range(i, 0, 10, 0, display.width - 1)
x = int(_x)
group.append(Line(x, 20, x, display.height, blue))
_y = simpleio.map_range(i, 0, 10, 20, display.height - 1)
y = int(_y)
group.append(Line(0, y, display.width, y, blue))
prev_x = 0
_prev_y = simpleio.map_range(data[0], 0, 100, display.height - 1, 20)
prev_y = int(_prev_y)
for i in range(1, num_points):
_new_x = simpleio.map_range(i, 0, num_points - 1, 0, display.width - 1)
new_x = int(_new_x)
_new_y = simpleio.map_range(data[i], 0, 100, display.height - 1, 20)
new_y = int(_new_y)
group.append(Line(prev_x, prev_y, new_x, new_y, aqua))
prev_x = new_x
prev_y = new_y
for i in range(num_points):
_x = simpleio.map_range(i, 0, num_points - 1, 0, display.width - 1)
x = int(_x)
_y = simpleio.map_range(data[i], 0, 100, display.height - 1, 20)
y = int(_y)
group.append(Circle(x, y, 5, fill=None, stroke = 2, outline = white))
time.sleep(2)
clean_up(group)
def widget1():
gc.collect()
data = [31, 42, 36, 58, 67, 88]
num_points = len(data)
bar_width = int(display.width / num_points) - 4
x_mapped_w = display.width + 2
h_mapped_h = display.height + 20
text_area = label.Label(terminalio.FONT, text="Widget Sales", color=white)
text_area.anchor_point = (0.5, 0.0)
text_area.anchored_position = (display.width / 2, 3)
group.append(text_area)
for i in range(11):
_y = simpleio.map_range(i, 0, 10, 20, display.height - 1)
y = int(_y)
group.append(Line(0, y, display.width, y, blue))
for i in range(num_points):
_x = simpleio.map_range(i, 0, num_points, 0, x_mapped_w)
x = int(_x)
_height = simpleio.map_range(data[i], 0, 100, h_mapped_h, 0)
height = int(_height)
group.append(vectorio.Rectangle(pixel_shader=palette, width=bar_width,
height=display.height + 1, x=x, y=height, color_index = 2))
time.sleep(2)
clean_up(group)
def text_align():
gc.collect()
TEXT = "hello world"
text_area_top_left = label.Label(terminalio.FONT, text=TEXT, color=red)
text_area_top_left.anchor_point = (0.0, 0.0)
text_area_top_left.anchored_position = (0, 0)
text_area_top_middle = label.Label(terminalio.FONT, text=TEXT, color=orange)
text_area_top_middle.anchor_point = (0.5, 0.0)
text_area_top_middle.anchored_position = (display.width / 2, 0)
text_area_top_right = label.Label(terminalio.FONT, text=TEXT, color=yellow)
text_area_top_right.anchor_point = (1.0, 0.0)
text_area_top_right.anchored_position = (display.width, 0)
text_area_middle_left = label.Label(terminalio.FONT, text=TEXT, color=green)
text_area_middle_left.anchor_point = (0.0, 0.5)
text_area_middle_left.anchored_position = (0, display.height / 2)
text_area_middle_middle = label.Label(terminalio.FONT, text=TEXT, color=aqua)
text_area_middle_middle.anchor_point = (0.5, 0.5)
text_area_middle_middle.anchored_position = (display.width / 2, display.height / 2)
text_area_middle_right = label.Label(terminalio.FONT, text=TEXT, color=blue)
text_area_middle_right.anchor_point = (1.0, 0.5)
text_area_middle_right.anchored_position = (display.width, display.height / 2)
text_area_bottom_left = label.Label(terminalio.FONT, text=TEXT, color=purple)
text_area_bottom_left.anchor_point = (0.0, 1.0)
text_area_bottom_left.anchored_position = (0, display.height)
text_area_bottom_middle = label.Label(terminalio.FONT, text=TEXT, color=pink)
text_area_bottom_middle.anchor_point = (0.5, 1.0)
text_area_bottom_middle.anchored_position = (display.width / 2, display.height)
text_area_bottom_right = label.Label(terminalio.FONT, text=TEXT, color=white)
text_area_bottom_right.anchor_point = (1.0, 1.0)
text_area_bottom_right.anchored_position = (display.width, display.height)
group.append(text_area_top_middle)
group.append(text_area_top_left)
group.append(text_area_top_right)
group.append(text_area_middle_middle)
group.append(text_area_middle_left)
group.append(text_area_middle_right)
group.append(text_area_bottom_middle)
group.append(text_area_bottom_left)
group.append(text_area_bottom_right)
time.sleep(2)
clean_up(group)
def custom_font():
gc.collect()
my_font = bitmap_font.load_font("/Helvetica-Bold-16.pcf")
text_sample = "The quick brown fox jumps over the lazy dog."
text_sample = "\n".join(wrap_text_to_lines(text_sample, 28))
text_area = label.Label(my_font, text="Custom Font", color=white)
text_area.anchor_point = (0.0, 0.0)
text_area.anchored_position = (0, 0)
sample_text = label.Label(my_font, text=text_sample)
sample_text.anchor_point = (0.5, 0.5)
sample_text.anchored_position = (display.width / 2, display.height / 2)
group.append(text_area)
group.append(sample_text)
time.sleep(2)
clean_up(group)
del my_font
gc.collect()
def bitmap_example():
gc.collect()
blinka_bitmap = displayio.OnDiskBitmap("/blinka_computer.bmp")
blinka_grid = displayio.TileGrid(blinka_bitmap, pixel_shader=blinka_bitmap.pixel_shader)
gc.collect()
group.append(blinka_grid)
time.sleep(2)
clean_up(group)
del blinka_grid
del blinka_bitmap
gc.collect()
def sensor_values():
gc.collect()
text_x = "X: %d" % randint(-25, 25)
text_y = "Y: %d" % randint(-25, 25)
text_z = "Z: %d" % randint(-25, 25)
x_text = label.Label(terminalio.FONT, text=text_x, color=red)
x_text.anchor_point = (0.0, 0.0)
x_text.anchored_position = (2, 0)
y_text = label.Label(terminalio.FONT, text=text_y, color=green)
y_text.anchor_point = (0.0, 0.0)
y_text.anchored_position = (2, 10)
z_text = label.Label(terminalio.FONT, text=text_z, color=blue)
z_text.anchor_point = (0.0, 0.0)
z_text.anchored_position = (2, 20)
group.append(x_text)
group.append(y_text)
group.append(z_text)
for i in range(40):
if i == 10:
group.scale = 2
elif i == 20:
group.scale = 3
elif i == 30:
group.scale = 4
x_text.text = "X: %d" % randint(-50, 50)
y_text.text = "Y: %d" % randint(-50, 50)
z_text.text = "Z: %d" % randint(-50, 50)
time.sleep(0.1)
time.sleep(0.1)
clean_up(group)
group.scale = 1
display.show(group)
while True:
show_shapes()
sine_chart()
widget0()
widget1()
text_align()
custom_font()
bitmap_example()
sensor_values()