diff --git a/GemmaM0_Band_Jacket/DiscoBandCamp/XYmap.h b/GemmaM0_Band_Jacket/DiscoBandCamp/XYmap.h index 1355be492..17949194f 100644 --- a/GemmaM0_Band_Jacket/DiscoBandCamp/XYmap.h +++ b/GemmaM0_Band_Jacket/DiscoBandCamp/XYmap.h @@ -84,4 +84,4 @@ uint16_t XY(uint16_t x, uint16_t y, uint16_t width, uint16_t height) } // Instantiate an XYMap object -XYMap myXYMap = XYMap::constructWithUserFunction(kMatrixWidth, kMatrixHeight, XY); +fl::XYMap myXYMap = fl::XYMap::constructWithUserFunction(kMatrixWidth, kMatrixHeight, XY); diff --git a/GemmaM0_Band_Jacket/DiscoBandCamp/effects.h b/GemmaM0_Band_Jacket/DiscoBandCamp/effects.h index d72f53196..bef1f729d 100644 --- a/GemmaM0_Band_Jacket/DiscoBandCamp/effects.h +++ b/GemmaM0_Band_Jacket/DiscoBandCamp/effects.h @@ -4,7 +4,6 @@ // Selection of effects from the FastLED library & Macetech RGB Shades - // Triple Sine Waves void threeSine() { diff --git a/GemmaM0_Band_Jacket/DiscoBandCamp/utils.h b/GemmaM0_Band_Jacket/DiscoBandCamp/utils.h index b41f5b7ed..7c97afc23 100644 --- a/GemmaM0_Band_Jacket/DiscoBandCamp/utils.h +++ b/GemmaM0_Band_Jacket/DiscoBandCamp/utils.h @@ -3,7 +3,6 @@ // SPDX-License-Identifier: MIT // Assorted useful functions and variables - // Global variables boolean effectInit = false; // indicates if a pattern has been recently switched uint16_t effectDelay = 0; // time between automatic effect changes diff --git a/Newxie_TFT_Examples/Newxie_Arduino/.uno.test.only b/Newxie_TFT_Examples/Newxie_Arduino/.uno.test.only new file mode 100644 index 000000000..e69de29bb diff --git a/Newxie_TFT_Examples/Newxie_Arduino/Newxie_Arduino.ino b/Newxie_TFT_Examples/Newxie_Arduino/Newxie_Arduino.ino new file mode 100644 index 000000000..6aa3604b6 --- /dev/null +++ b/Newxie_TFT_Examples/Newxie_Arduino/Newxie_Arduino.ino @@ -0,0 +1,676 @@ +// 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 6113 // Newxie 135x240 display +// 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 10 // To display chip-select pin +#define TFT_RST -1 // To display reset pin +#define TFT_DC 8 // 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 +#include // Core graphics library +#include // A custom font +#if (SCREEN_PRODUCT_ID == 1480) || (SCREEN_PRODUCT_ID == 2090) +#include // Library for ILI9341-based screens +Adafruit_ILI9341 display(TFT_CS, TFT_DC, TFT_RST); +#else +#include // 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); +#elif (SCREEN_PRODUCT_ID == 6113) + display.init(135, 240); +#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(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 diff --git a/Newxie_TFT_Examples/Newxie_CircuitPython/Helvetica-Bold-16.pcf b/Newxie_TFT_Examples/Newxie_CircuitPython/Helvetica-Bold-16.pcf new file mode 100644 index 000000000..deb9092d1 Binary files /dev/null and b/Newxie_TFT_Examples/Newxie_CircuitPython/Helvetica-Bold-16.pcf differ diff --git a/Newxie_TFT_Examples/Newxie_CircuitPython/adabot.bmp b/Newxie_TFT_Examples/Newxie_CircuitPython/adabot.bmp new file mode 100644 index 000000000..1d24f8dcc Binary files /dev/null and b/Newxie_TFT_Examples/Newxie_CircuitPython/adabot.bmp differ diff --git a/Newxie_TFT_Examples/Newxie_CircuitPython/code.py b/Newxie_TFT_Examples/Newxie_CircuitPython/code.py new file mode 100644 index 000000000..17f510e31 --- /dev/null +++ b/Newxie_TFT_Examples/Newxie_CircuitPython/code.py @@ -0,0 +1,326 @@ +# SPDX-FileCopyrightText: 2025 Liz Clark for Adafruit Industries +# SPDX-FileCopyrightText: Adapted from Phil B.'s 16bit_hello Arduino Code +# +# SPDX-License-Identifier: MIT + +'''Graphics example for the Vertical Newxie TFT''' + +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.D5 +tft_dc = board.D6 + +display_bus = displayio.FourWire( + spi, command=tft_dc, chip_select=tft_cs, reset=None +) + +display = ST7789(display_bus, rotation=180, width=135, height=240, rowstart=40, colstart=53) + +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 = "hi!" + + 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, 15)) + 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("/adabot.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.root_group = group + +while True: + show_shapes() + sine_chart() + widget0() + widget1() + text_align() + custom_font() + bitmap_example() + sensor_values() diff --git a/Newxie_TFT_Examples/Newxie_Python_Pi/adabot.jpg b/Newxie_TFT_Examples/Newxie_Python_Pi/adabot.jpg new file mode 100644 index 000000000..8b196dc74 Binary files /dev/null and b/Newxie_TFT_Examples/Newxie_Python_Pi/adabot.jpg differ diff --git a/Newxie_TFT_Examples/Newxie_Python_Pi/code.py b/Newxie_TFT_Examples/Newxie_Python_Pi/code.py new file mode 100644 index 000000000..5f2d29ee0 --- /dev/null +++ b/Newxie_TFT_Examples/Newxie_Python_Pi/code.py @@ -0,0 +1,70 @@ +# SPDX-FileCopyrightText: 2025 Liz Clark for Adafruit Industries +# SPDX-FileCopyrightText: Adapted from Melissa LeBlanc-Williams's Pi Demo Code +# +# SPDX-License-Identifier: MIT + +'''Raspberry Pi Graphics example for the Vertical Newxie TFT''' + +import time +import digitalio +import board +from PIL import Image, ImageDraw, ImageFont +from adafruit_rgb_display import st7789 + +BORDER = 20 +FONTSIZE = 24 + +cs_pin = digitalio.DigitalInOut(board.CE0) +dc_pin = digitalio.DigitalInOut(board.D25) +reset_pin = digitalio.DigitalInOut(board.D24) + +BAUDRATE = 24000000 + +spi = board.SPI() + +disp = st7789.ST7789(spi, rotation=180, + width=135, height=240, + x_offset=53, y_offset=40, + cs=cs_pin, + dc=dc_pin, + rst=reset_pin, + baudrate=BAUDRATE, +) + +width = disp.width +height = disp.height + +# -------TEXT AND SHAPES--------- +image1 = Image.new("RGB", (width, height)) +draw1 = ImageDraw.Draw(image1) +draw1.rectangle((0, 0, width, height), fill=(0, 255, 0)) # Green background + +draw1.rectangle( + (BORDER, BORDER, width - BORDER - 1, height - BORDER - 1), fill=(170, 0, 136) +) +font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", FONTSIZE) +text = "Hello\nWorld!" +(font_width, font_height) = font.getsize(text) +draw1.text( + (30, height // 2 - font_height // 2-12), + text, + font=font, + fill=(255, 255, 0), +) + +# ------ADABOT JPEG DISPLAY---------- +image2 = Image.open("adabot.jpg") +image_ratio = image2.width / image2.height +screen_ratio = width / height +scaled_width = width +scaled_height = image2.height * width // image2.width +image2 = image2.resize((scaled_width, scaled_height), Image.BICUBIC) +x = scaled_width // 2 - width // 2 +y = scaled_height // 2 - height // 2 +image2 = image2.crop((x, y, x + width, y + height)) + +while True: + disp.image(image1) # show text + time.sleep(2) + disp.image(image2) # show adabot + time.sleep(2)