first commit
This commit is contained in:
commit
e6a42e28e9
4355 changed files with 4626605 additions and 0 deletions
|
|
@ -0,0 +1,138 @@
|
|||
// An adaption of the "UncannyEyes" sketch (see eye_functions tab)
|
||||
// for the TFT_eSPI library. As written the sketch is for driving
|
||||
// one (240x320 minimum) TFT display, showing 2 eyes. See example
|
||||
// Animated_Eyes_2 for a dual 128x128 TFT display configured sketch.
|
||||
|
||||
// The size of the displayed eye is determined by the screen size and
|
||||
// resolution. The eye image is 128 pixels wide. In humans the palpebral
|
||||
// fissure (open eye) width is about 30mm so a low resolution, large
|
||||
// pixel size display works best to show a scale eye image. Note that
|
||||
// display manufacturers usually quote the diagonal measurement, so a
|
||||
// 128 x 128 1.7" display or 128 x 160 2" display is about right.
|
||||
|
||||
// Configuration settings for the eye, eye style, display count,
|
||||
// chip selects and x offsets can be defined in the sketch "config.h" tab.
|
||||
|
||||
// Performance (frames per second = fps) can be improved by using
|
||||
// DMA (for SPI displays only) on ESP32 and STM32 processors. Use
|
||||
// as high a SPI clock rate as is supported by the display. 27MHz
|
||||
// minimum, some displays can be operated at higher clock rates in
|
||||
// the range 40-80MHz.
|
||||
|
||||
// Single defaultEye performance for different processors
|
||||
// No DMA With DMA
|
||||
// ESP8266 (160MHz CPU) 40MHz SPI 36 fps
|
||||
// ESP32 27MHz SPI 53 fps 85 fps
|
||||
// ESP32 40MHz SPI 67 fps 102 fps
|
||||
// ESP32 80MHz SPI 82 fps 116 fps // Note: Few displays work reliably at 80MHz
|
||||
// STM32F401 55MHz SPI 44 fps 90 fps
|
||||
// STM32F446 55MHz SPI 83 fps 155 fps
|
||||
// STM32F767 55MHz SPI 136 fps 197 fps
|
||||
|
||||
// DMA can be used with RP2040, STM32 and ESP32 processors when the interface
|
||||
// is SPI, uncomment the next line:
|
||||
//#define USE_DMA
|
||||
|
||||
// Load TFT driver library
|
||||
#include <SPI.h>
|
||||
#include <TFT_eSPI.h>
|
||||
TFT_eSPI tft; // A single instance is used for 1 or 2 displays
|
||||
|
||||
// A pixel buffer is used during eye rendering
|
||||
#define BUFFER_SIZE 1024 // 128 to 1024 seems optimum
|
||||
|
||||
#ifdef USE_DMA
|
||||
#define BUFFERS 2 // 2 toggle buffers with DMA
|
||||
#else
|
||||
#define BUFFERS 1 // 1 buffer for no DMA
|
||||
#endif
|
||||
|
||||
uint16_t pbuffer[BUFFERS][BUFFER_SIZE]; // Pixel rendering buffer
|
||||
bool dmaBuf = 0; // DMA buffer selection
|
||||
|
||||
// This struct is populated in config.h
|
||||
typedef struct { // Struct is defined before including config.h --
|
||||
int8_t select; // pin numbers for each eye's screen select line
|
||||
int8_t wink; // and wink button (or -1 if none) specified there,
|
||||
uint8_t rotation; // also display rotation and the x offset
|
||||
int16_t xposition; // position of eye on the screen
|
||||
} eyeInfo_t;
|
||||
|
||||
#include "config.h" // ****** CONFIGURATION IS DONE IN HERE ******
|
||||
|
||||
extern void user_setup(void); // Functions in the user*.cpp files
|
||||
extern void user_loop(void);
|
||||
|
||||
#define SCREEN_X_START 0
|
||||
#define SCREEN_X_END SCREEN_WIDTH // Badly named, actually the "eye" width!
|
||||
#define SCREEN_Y_START 0
|
||||
#define SCREEN_Y_END SCREEN_HEIGHT // Actually "eye" height
|
||||
|
||||
// A simple state machine is used to control eye blinks/winks:
|
||||
#define NOBLINK 0 // Not currently engaged in a blink
|
||||
#define ENBLINK 1 // Eyelid is currently closing
|
||||
#define DEBLINK 2 // Eyelid is currently opening
|
||||
typedef struct {
|
||||
uint8_t state; // NOBLINK/ENBLINK/DEBLINK
|
||||
uint32_t duration; // Duration of blink state (micros)
|
||||
uint32_t startTime; // Time (micros) of last state change
|
||||
} eyeBlink;
|
||||
|
||||
struct { // One-per-eye structure
|
||||
int16_t tft_cs; // Chip select pin for each display
|
||||
eyeBlink blink; // Current blink/wink state
|
||||
int16_t xposition; // x position of eye image
|
||||
} eye[NUM_EYES];
|
||||
|
||||
uint32_t startTime; // For FPS indicator
|
||||
|
||||
// INITIALIZATION -- runs once at startup ----------------------------------
|
||||
void setup(void) {
|
||||
Serial.begin(115200);
|
||||
//while (!Serial);
|
||||
Serial.println("Starting");
|
||||
|
||||
#if defined(DISPLAY_BACKLIGHT) && (DISPLAY_BACKLIGHT >= 0)
|
||||
// Enable backlight pin, initially off
|
||||
Serial.println("Backlight turned off");
|
||||
pinMode(DISPLAY_BACKLIGHT, OUTPUT);
|
||||
digitalWrite(DISPLAY_BACKLIGHT, LOW);
|
||||
#endif
|
||||
|
||||
// User call for additional features
|
||||
user_setup();
|
||||
|
||||
// Initialise the eye(s), this will set all chip selects low for the tft.init()
|
||||
initEyes();
|
||||
|
||||
// Initialise TFT
|
||||
Serial.println("Initialising displays");
|
||||
tft.init();
|
||||
|
||||
#ifdef USE_DMA
|
||||
tft.initDMA();
|
||||
#endif
|
||||
|
||||
// Raise chip select(s) so that displays can be individually configured
|
||||
digitalWrite(eye[0].tft_cs, HIGH);
|
||||
if (NUM_EYES > 1) digitalWrite(eye[1].tft_cs, HIGH);
|
||||
|
||||
for (uint8_t e = 0; e < NUM_EYES; e++) {
|
||||
digitalWrite(eye[e].tft_cs, LOW);
|
||||
tft.setRotation(eyeInfo[e].rotation);
|
||||
tft.fillScreen(TFT_BLACK);
|
||||
digitalWrite(eye[e].tft_cs, HIGH);
|
||||
}
|
||||
|
||||
#if defined(DISPLAY_BACKLIGHT) && (DISPLAY_BACKLIGHT >= 0)
|
||||
Serial.println("Backlight now on!");
|
||||
analogWrite(DISPLAY_BACKLIGHT, BACKLIGHT_MAX);
|
||||
#endif
|
||||
|
||||
startTime = millis(); // For frame-rate calculation
|
||||
}
|
||||
|
||||
// MAIN LOOP -- runs continuously after setup() ----------------------------
|
||||
void loop() {
|
||||
updateEye();
|
||||
}
|
||||
93
libraries/TFT_eSPI/examples/Generic/Animated_Eyes_1/config.h
Normal file
93
libraries/TFT_eSPI/examples/Generic/Animated_Eyes_1/config.h
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
// Pin selections here are based on the original Adafruit Learning System
|
||||
// guide for the Teensy 3.x project. Some of these pin numbers don't even
|
||||
// exist on the smaller SAMD M0 & M4 boards, so you may need to make other
|
||||
// selections:
|
||||
|
||||
// GRAPHICS SETTINGS (appearance of eye) -----------------------------------
|
||||
|
||||
// If using a SINGLE EYE, you might want this next line enabled, which
|
||||
// uses a simpler "football-shaped" eye that's left/right symmetrical.
|
||||
// Default shape includes the caruncle, creating distinct left/right eyes.
|
||||
|
||||
//#define SYMMETRICAL_EYELID
|
||||
|
||||
// Enable ONE of these #includes -- HUGE graphics tables for various eyes:
|
||||
#include "data/defaultEye.h" // Standard human-ish hazel eye -OR-
|
||||
//#include "data/dragonEye.h" // Slit pupil fiery dragon/demon eye -OR-
|
||||
//#include "data/noScleraEye.h" // Large iris, no sclera -OR-
|
||||
//#include "data/goatEye.h" // Horizontal pupil goat/Krampus eye -OR-
|
||||
//#include "data/newtEye.h" // Eye of newt -OR-
|
||||
//#include "data/terminatorEye.h" // Git to da choppah!
|
||||
//#include "data/catEye.h" // Cartoonish cat (flat "2D" colors)
|
||||
//#include "data/owlEye.h" // Minerva the owl (DISABLE TRACKING)
|
||||
//#include "data/naugaEye.h" // Nauga googly eye (DISABLE TRACKING)
|
||||
//#include "data/doeEye.h" // Cartoon deer eye (DISABLE TRACKING)
|
||||
|
||||
// DISPLAY HARDWARE SETTINGS (screen type & connections) -------------------
|
||||
#define TFT_COUNT 1 // Number of screens (1 or 2)
|
||||
#define TFT1_CS -1 // TFT 1 chip select pin (set to -1 to use TFT_eSPI setup)
|
||||
#define TFT2_CS -1 // TFT 2 chip select pin (set to -1 to use TFT_eSPI setup)
|
||||
#define TFT_1_ROT 1 // TFT 1 rotation
|
||||
#define TFT_2_ROT 1 // TFT 2 rotation
|
||||
#define EYE_1_XPOSITION 0 // x shift for eye 1 image on display
|
||||
#define EYE_2_XPOSITION 320 - 128 // x shift for eye 2 image on display
|
||||
|
||||
#define DISPLAY_BACKLIGHT -1 // Pin for backlight control (-1 for none)
|
||||
#define BACKLIGHT_MAX 255
|
||||
|
||||
// EYE LIST ----------------------------------------------------------------
|
||||
#define NUM_EYES 2 // Number of eyes to display (1 or 2)
|
||||
|
||||
#define BLINK_PIN -1 // Pin for manual blink button (BOTH eyes)
|
||||
#define LH_WINK_PIN -1 // Left wink pin (set to -1 for no pin)
|
||||
#define RH_WINK_PIN -1 // Right wink pin (set to -1 for no pin)
|
||||
|
||||
// This table contains ONE LINE PER EYE. The table MUST be present with
|
||||
// this name and contain ONE OR MORE lines. Each line contains THREE items:
|
||||
// a pin number for the corresponding TFT/OLED display's SELECT line, a pin
|
||||
// pin number for that eye's "wink" button (or -1 if not used), a screen
|
||||
// rotation value (0-3) and x position offset for that eye.
|
||||
|
||||
#if (NUM_EYES == 2)
|
||||
eyeInfo_t eyeInfo[] = {
|
||||
{ TFT1_CS, LH_WINK_PIN, TFT_1_ROT, EYE_1_XPOSITION }, // LEFT EYE chip select and wink pins, rotation and offset
|
||||
{ TFT2_CS, RH_WINK_PIN, TFT_2_ROT, EYE_2_XPOSITION }, // RIGHT EYE chip select and wink pins, rotation and offset
|
||||
};
|
||||
#else
|
||||
eyeInfo_t eyeInfo[] = {
|
||||
{ TFT1_CS, LH_WINK_PIN, TFT_1_ROT, EYE_1_XPOSITION }, // EYE chip select and wink pins, rotation and offset
|
||||
};
|
||||
#endif
|
||||
|
||||
// INPUT SETTINGS (for controlling eye motion) -----------------------------
|
||||
|
||||
// JOYSTICK_X_PIN and JOYSTICK_Y_PIN specify analog input pins for manually
|
||||
// controlling the eye with an analog joystick. If set to -1 or if not
|
||||
// defined, the eye will move on its own.
|
||||
// IRIS_PIN specifies an analog input pin for a photocell to make pupils
|
||||
// react to light (or potentiometer for manual control). If set to -1 or
|
||||
// if not defined, the pupils will change on their own.
|
||||
// BLINK_PIN specifies an input pin for a button (to ground) that will
|
||||
// make any/all eyes blink. If set to -1 or if not defined, the eyes will
|
||||
// only blink if AUTOBLINK is defined, or if the eyeInfo[] table above
|
||||
// includes wink button settings for each eye.
|
||||
|
||||
//#define JOYSTICK_X_PIN A0 // Analog pin for eye horiz pos (else auto)
|
||||
//#define JOYSTICK_Y_PIN A1 // Analog pin for eye vert position (")
|
||||
//#define JOYSTICK_X_FLIP // If defined, reverse stick X axis
|
||||
//#define JOYSTICK_Y_FLIP // If defined, reverse stick Y axis
|
||||
#define TRACKING // If defined, eyelid tracks pupil
|
||||
#define AUTOBLINK // If defined, eyes also blink autonomously
|
||||
|
||||
// #define LIGHT_PIN -1 // Light sensor pin
|
||||
#define LIGHT_CURVE 0.33 // Light sensor adjustment curve
|
||||
#define LIGHT_MIN 0 // Minimum useful reading from light sensor
|
||||
#define LIGHT_MAX 1023 // Maximum useful reading from sensor
|
||||
|
||||
#define IRIS_SMOOTH // If enabled, filter input from IRIS_PIN
|
||||
#if !defined(IRIS_MIN) // Each eye might have its own MIN/MAX
|
||||
#define IRIS_MIN 90 // Iris size (0-1023) in brightest light
|
||||
#endif
|
||||
#if !defined(IRIS_MAX)
|
||||
#define IRIS_MAX 130 // Iris size (0-1023) in darkest light
|
||||
#endif
|
||||
11600
libraries/TFT_eSPI/examples/Generic/Animated_Eyes_1/data/catEye.h
Normal file
11600
libraries/TFT_eSPI/examples/Generic/Animated_Eyes_1/data/catEye.h
Normal file
File diff suppressed because it is too large
Load diff
13349
libraries/TFT_eSPI/examples/Generic/Animated_Eyes_1/data/defaultEye.h
Normal file
13349
libraries/TFT_eSPI/examples/Generic/Animated_Eyes_1/data/defaultEye.h
Normal file
File diff suppressed because it is too large
Load diff
15646
libraries/TFT_eSPI/examples/Generic/Animated_Eyes_1/data/doeEye.h
Normal file
15646
libraries/TFT_eSPI/examples/Generic/Animated_Eyes_1/data/doeEye.h
Normal file
File diff suppressed because it is too large
Load diff
17018
libraries/TFT_eSPI/examples/Generic/Animated_Eyes_1/data/dragonEye.h
Normal file
17018
libraries/TFT_eSPI/examples/Generic/Animated_Eyes_1/data/dragonEye.h
Normal file
File diff suppressed because it is too large
Load diff
12807
libraries/TFT_eSPI/examples/Generic/Animated_Eyes_1/data/goatEye.h
Normal file
12807
libraries/TFT_eSPI/examples/Generic/Animated_Eyes_1/data/goatEye.h
Normal file
File diff suppressed because it is too large
Load diff
101
libraries/TFT_eSPI/examples/Generic/Animated_Eyes_1/data/logo.h
Normal file
101
libraries/TFT_eSPI/examples/Generic/Animated_Eyes_1/data/logo.h
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
// Logo helps with screen orientation & positioning
|
||||
|
||||
#define LOGO_TOP_WIDTH 59
|
||||
#define LOGO_TOP_HEIGHT 59
|
||||
|
||||
const uint8_t logo_top[472] PROGMEM= {
|
||||
0X00, 0X00, 0X00, 0X01, 0XC0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X03,
|
||||
0XC0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X03, 0XE0, 0X00, 0X00, 0X00,
|
||||
0X00, 0X00, 0X00, 0X07, 0XE0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X0F,
|
||||
0XF0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X0F, 0XF0, 0X00, 0X00, 0X00,
|
||||
0X00, 0X00, 0X00, 0X1F, 0XF0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X3F,
|
||||
0XF0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X7F, 0XF8, 0X00, 0X00, 0X00,
|
||||
0X00, 0X00, 0X00, 0X7F, 0XF8, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0XFF,
|
||||
0XF8, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, 0XFF, 0XFC, 0X00, 0X00, 0X00,
|
||||
0X00, 0X00, 0X01, 0XFF, 0XFC, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, 0XFF,
|
||||
0XFC, 0X00, 0X00, 0X00, 0X00, 0X00, 0X03, 0XFF, 0XFC, 0X00, 0X00, 0X00,
|
||||
0X00, 0X00, 0X03, 0XFF, 0XFE, 0X00, 0X00, 0X00, 0XFF, 0XFF, 0X83, 0XFF,
|
||||
0XFE, 0X00, 0X00, 0X00, 0XFF, 0XFF, 0XF3, 0XFF, 0XFE, 0X00, 0X00, 0X00,
|
||||
0XFF, 0XFF, 0XFB, 0XFF, 0XFC, 0X00, 0X00, 0X00, 0X7F, 0XFF, 0XFF, 0XFF,
|
||||
0XFC, 0X00, 0X00, 0X00, 0X7F, 0XFF, 0XFF, 0XFB, 0XFC, 0X30, 0X00, 0X00,
|
||||
0X3F, 0XFF, 0XFF, 0XF1, 0XFB, 0XFF, 0X00, 0X00, 0X1F, 0XFF, 0XFF, 0XF1,
|
||||
0XFF, 0XFF, 0XE0, 0X00, 0X1F, 0XFF, 0XFF, 0XE1, 0XFF, 0XFF, 0XFE, 0X00,
|
||||
0X0F, 0XFF, 0XFF, 0XE1, 0XFF, 0XFF, 0XFF, 0X80, 0X07, 0XFF, 0XEF, 0XE1,
|
||||
0XFF, 0XFF, 0XFF, 0XE0, 0X03, 0XFF, 0XC1, 0XE3, 0XFF, 0XFF, 0XFF, 0XE0,
|
||||
0X03, 0XFF, 0XC0, 0XF3, 0XFF, 0XFF, 0XFF, 0XE0, 0X01, 0XFF, 0XF0, 0X7F,
|
||||
0XC3, 0XFF, 0XFF, 0XC0, 0X00, 0XFF, 0XF8, 0X7F, 0X01, 0XFF, 0XFF, 0X00,
|
||||
0X00, 0X7F, 0XFF, 0XFE, 0X03, 0XFF, 0XFE, 0X00, 0X00, 0X1F, 0XFF, 0XFF,
|
||||
0X0F, 0XFF, 0XFC, 0X00, 0X00, 0X07, 0XFF, 0XFF, 0XFF, 0XFF, 0XF0, 0X00,
|
||||
0X00, 0X01, 0XFF, 0X3F, 0XFF, 0XFF, 0XE0, 0X00, 0X00, 0X07, 0XFC, 0X39,
|
||||
0XFF, 0XFF, 0X80, 0X00, 0X00, 0X0F, 0XF8, 0X38, 0XFF, 0XFF, 0X00, 0X00,
|
||||
0X00, 0X1F, 0XF0, 0X78, 0X7F, 0XFC, 0X00, 0X00, 0X00, 0X3F, 0XF0, 0XF8,
|
||||
0X7F, 0X00, 0X00, 0X00, 0X00, 0X3F, 0XF1, 0XFC, 0X7F, 0X80, 0X00, 0X00,
|
||||
0X00, 0X7F, 0XFF, 0XFE, 0X3F, 0XC0, 0X00, 0X00, 0X00, 0X7F, 0XFF, 0XFE,
|
||||
0X3F, 0XC0, 0X00, 0X00, 0X00, 0XFF, 0XFF, 0XFF, 0XFF, 0XE0, 0X00, 0X00,
|
||||
0X00, 0XFF, 0XFF, 0XFF, 0XFF, 0XE0, 0X00, 0X00, 0X00, 0XFF, 0XFF, 0XFF,
|
||||
0XFF, 0XE0, 0X00, 0X00, 0X01, 0XFF, 0XFF, 0XBF, 0XFF, 0XE0, 0X00, 0X00,
|
||||
0X01, 0XFF, 0XFF, 0XBF, 0XFF, 0XE0, 0X00, 0X00, 0X01, 0XFF, 0XFF, 0X1F,
|
||||
0XFF, 0XE0, 0X00, 0X00, 0X03, 0XFF, 0XFE, 0X1F, 0XFF, 0XE0, 0X00, 0X00,
|
||||
0X03, 0XFF, 0XFC, 0X0F, 0XFF, 0XE0, 0X00, 0X00, 0X03, 0XFF, 0XF0, 0X0F,
|
||||
0XFF, 0XE0, 0X00, 0X00, 0X03, 0XFF, 0XC0, 0X07, 0XFF, 0XE0, 0X00, 0X00,
|
||||
0X07, 0XFE, 0X00, 0X03, 0XFF, 0XE0, 0X00, 0X00, 0X07, 0XF0, 0X00, 0X01,
|
||||
0XFF, 0XE0, 0X00, 0X00, 0X03, 0X80, 0X00, 0X00, 0X7F, 0XE0, 0X00, 0X00,
|
||||
0X00, 0X00, 0X00, 0X00, 0X3F, 0XE0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00,
|
||||
0X0F, 0XE0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X07, 0XE0, 0X00, 0X00,
|
||||
0X00, 0X00, 0X00, 0X00, 0X03, 0XE0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00,
|
||||
0X00, 0XE0, 0X00, 0X00 };
|
||||
|
||||
#define LOGO_BOTTOM_WIDTH 128
|
||||
#define LOGO_BOTTOM_HEIGHT 37
|
||||
|
||||
const uint8_t logo_bottom[592] PROGMEM= {
|
||||
0X00, 0X00, 0X00, 0X00, 0X3E, 0X00, 0X00, 0X00, 0X7F, 0X00, 0X00, 0X00,
|
||||
0X00, 0X03, 0XE0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X3E, 0X00, 0X00, 0X00,
|
||||
0XFF, 0X00, 0X00, 0X00, 0X00, 0X03, 0XE0, 0X00, 0X00, 0X00, 0X00, 0X00,
|
||||
0X3E, 0X00, 0X00, 0X01, 0XFF, 0X00, 0X00, 0X00, 0X00, 0X03, 0XE0, 0X00,
|
||||
0X00, 0X00, 0X00, 0X00, 0X3E, 0X00, 0X00, 0X01, 0XFF, 0X00, 0X00, 0X00,
|
||||
0X00, 0X03, 0XE0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X3E, 0X00, 0X00, 0X01,
|
||||
0XF8, 0X00, 0X00, 0X00, 0X00, 0X00, 0X03, 0XE0, 0X00, 0X00, 0X00, 0X00,
|
||||
0X3E, 0X00, 0X00, 0X01, 0XF0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X03, 0XE0,
|
||||
0X00, 0X00, 0X00, 0X00, 0X3E, 0X00, 0X00, 0X01, 0XF0, 0X00, 0X00, 0X00,
|
||||
0X00, 0X00, 0X03, 0XE0, 0X1F, 0XFF, 0X00, 0XFE, 0X3E, 0X07, 0XFF, 0XC1,
|
||||
0XFF, 0X1F, 0X0E, 0X7C, 0X03, 0XE3, 0XE3, 0XFF, 0X3F, 0XFF, 0X81, 0XFF,
|
||||
0XBE, 0X0F, 0XFF, 0XE1, 0XFF, 0X1F, 0X3E, 0X7C, 0X03, 0XE3, 0XE3, 0XFF,
|
||||
0X7F, 0XFF, 0XC3, 0XFF, 0XFE, 0X1F, 0XFF, 0XF1, 0XFF, 0X1F, 0X7E, 0X7C,
|
||||
0X03, 0XE3, 0XE3, 0XFF, 0X7F, 0XFF, 0XC7, 0XFF, 0XFE, 0X1F, 0XFF, 0XF1,
|
||||
0XFF, 0X1F, 0XFE, 0X7C, 0X03, 0XE3, 0XE3, 0XFF, 0X7E, 0X0F, 0XC7, 0XFF,
|
||||
0XFE, 0X1F, 0X83, 0XF1, 0XFF, 0X1F, 0XFE, 0X7C, 0X03, 0XE3, 0XE3, 0XFF,
|
||||
0X7C, 0X07, 0XC7, 0XE0, 0X3E, 0X1F, 0X01, 0XF1, 0XF0, 0X1F, 0XFE, 0X7C,
|
||||
0X03, 0XE3, 0XE3, 0XE0, 0X7C, 0X07, 0XC7, 0XE0, 0X3E, 0X1F, 0X01, 0XF1,
|
||||
0XF0, 0X1F, 0X80, 0X7C, 0X03, 0XE3, 0XE3, 0XE0, 0X00, 0X07, 0XC7, 0XC0,
|
||||
0X3E, 0X00, 0X01, 0XF1, 0XF0, 0X1F, 0X00, 0X7C, 0X03, 0XE3, 0XE3, 0XE0,
|
||||
0X00, 0X07, 0XC7, 0XC0, 0X3E, 0X00, 0X01, 0XF1, 0XF0, 0X1F, 0X00, 0X7C,
|
||||
0X03, 0XE3, 0XE3, 0XE0, 0X3F, 0XFF, 0XC7, 0XC0, 0X3E, 0X0F, 0XFF, 0XF1,
|
||||
0XF0, 0X1F, 0X00, 0X7C, 0X03, 0XE3, 0XE3, 0XE0, 0X7F, 0XFF, 0XC7, 0XC0,
|
||||
0X3E, 0X1F, 0XFF, 0XF1, 0XF0, 0X1F, 0X00, 0X7C, 0X03, 0XE3, 0XE3, 0XE0,
|
||||
0XFF, 0XFF, 0XC7, 0XC0, 0X3E, 0X3F, 0XFF, 0XF1, 0XF0, 0X1F, 0X00, 0X7C,
|
||||
0X03, 0XE3, 0XE3, 0XE0, 0XFC, 0X07, 0XC7, 0XC0, 0X3E, 0X3F, 0X01, 0XF1,
|
||||
0XF0, 0X1F, 0X00, 0X7C, 0X03, 0XE3, 0XE3, 0XE0, 0XF8, 0X07, 0XC7, 0XC0,
|
||||
0X3E, 0X3E, 0X01, 0XF1, 0XF0, 0X1F, 0X00, 0X7C, 0X03, 0XE3, 0XE3, 0XE0,
|
||||
0XF8, 0X07, 0XC7, 0XE0, 0X3E, 0X3E, 0X01, 0XF1, 0XF0, 0X1F, 0X00, 0X7C,
|
||||
0X03, 0XE3, 0XE3, 0XE0, 0XF8, 0X07, 0XC7, 0XE0, 0X7E, 0X3E, 0X01, 0XF1,
|
||||
0XF0, 0X1F, 0X00, 0X7E, 0X03, 0XE3, 0XE3, 0XE0, 0XFC, 0X1F, 0XC7, 0XFF,
|
||||
0XFE, 0X3F, 0X07, 0XF1, 0XF0, 0X1F, 0X00, 0X7F, 0XFF, 0XE3, 0XE3, 0XFF,
|
||||
0XFF, 0XFF, 0XC7, 0XFF, 0XFE, 0X3F, 0XFF, 0XF1, 0XF0, 0X1F, 0X00, 0X7F,
|
||||
0XFF, 0XE3, 0XE3, 0XFF, 0XFF, 0XFF, 0XC3, 0XFF, 0XBE, 0X3F, 0XFF, 0XF1,
|
||||
0XF0, 0X1F, 0X00, 0X7F, 0XFF, 0XE3, 0XE3, 0XFF, 0X7F, 0XE7, 0XC3, 0XFF,
|
||||
0X3E, 0X1F, 0XF9, 0XF1, 0XF0, 0X1F, 0X00, 0X3F, 0XE3, 0XE3, 0XE1, 0XFF,
|
||||
0X1F, 0X87, 0XC0, 0XFC, 0X3E, 0X07, 0XE1, 0XF1, 0XF0, 0X1F, 0X00, 0X0F,
|
||||
0XC1, 0XE3, 0XE0, 0XFC, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00,
|
||||
0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00,
|
||||
0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00,
|
||||
0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00,
|
||||
0X00, 0X00, 0X00, 0X00, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF,
|
||||
0XB7, 0X63, 0XDD, 0XC6, 0X08, 0X76, 0X1C, 0X7F, 0XFF, 0XFF, 0XFF, 0XFF,
|
||||
0XFF, 0XFF, 0XFF, 0XFF, 0XB3, 0X6D, 0XDD, 0XBB, 0XBB, 0XB6, 0XFB, 0XBF,
|
||||
0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XB3, 0X6E, 0XDD, 0XBF,
|
||||
0XBB, 0XB6, 0XFB, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF,
|
||||
0XB5, 0X6E, 0XDD, 0XC7, 0XB8, 0X76, 0X3C, 0X7F, 0XFF, 0XFF, 0XFF, 0XFF,
|
||||
0XFF, 0XFF, 0XFF, 0XFF, 0XB5, 0X6E, 0XDD, 0XFB, 0XBB, 0XB6, 0XFF, 0XBF,
|
||||
0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XB6, 0X6D, 0XEB, 0XBB,
|
||||
0XBB, 0XB6, 0XFB, 0XBF };
|
||||
7609
libraries/TFT_eSPI/examples/Generic/Animated_Eyes_1/data/naugaEye.h
Normal file
7609
libraries/TFT_eSPI/examples/Generic/Animated_Eyes_1/data/naugaEye.h
Normal file
File diff suppressed because it is too large
Load diff
13346
libraries/TFT_eSPI/examples/Generic/Animated_Eyes_1/data/newtEye.h
Normal file
13346
libraries/TFT_eSPI/examples/Generic/Animated_Eyes_1/data/newtEye.h
Normal file
File diff suppressed because it is too large
Load diff
17018
libraries/TFT_eSPI/examples/Generic/Animated_Eyes_1/data/noScleraEye.h
Normal file
17018
libraries/TFT_eSPI/examples/Generic/Animated_Eyes_1/data/noScleraEye.h
Normal file
File diff suppressed because it is too large
Load diff
7609
libraries/TFT_eSPI/examples/Generic/Animated_Eyes_1/data/owlEye.h
Normal file
7609
libraries/TFT_eSPI/examples/Generic/Animated_Eyes_1/data/owlEye.h
Normal file
File diff suppressed because it is too large
Load diff
13346
libraries/TFT_eSPI/examples/Generic/Animated_Eyes_1/data/terminatorEye.h
Normal file
13346
libraries/TFT_eSPI/examples/Generic/Animated_Eyes_1/data/terminatorEye.h
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,429 @@
|
|||
//
|
||||
// Code adapted by Bodmer as an example for TFT_eSPI, this runs on any
|
||||
// TFT_eSPI compatible processor so ignore the technical limitations
|
||||
// detailed in the original header below. Assorted changes have been
|
||||
// made including removal of the display mirror kludge.
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Uncanny eyes for Adafruit 1.5" OLED (product #1431) or 1.44" TFT LCD
|
||||
// (#2088). Works on PJRC Teensy 3.x and on Adafruit M0 and M4 boards
|
||||
// (Feather, Metro, etc.). This code uses features specific to these
|
||||
// boards and WILL NOT work on normal Arduino or other boards!
|
||||
//
|
||||
// SEE FILE "config.h" FOR MOST CONFIGURATION (graphics, pins, display type,
|
||||
// etc.). Probably won't need to edit THIS file unless you're doing some
|
||||
// extremely custom modifications.
|
||||
//
|
||||
// Adafruit invests time and resources providing this open source code,
|
||||
// please support Adafruit and open-source hardware by purchasing products
|
||||
// from Adafruit!
|
||||
//
|
||||
// Written by Phil Burgess / Paint Your Dragon for Adafruit Industries.
|
||||
// MIT license. SPI FIFO insight from Paul Stoffregen's ILI9341_t3 library.
|
||||
// Inspired by David Boccabella's (Marcwolf) hybrid servo/OLED eye concept.
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
#if !defined(LIGHT_PIN) || (LIGHT_PIN < 0)
|
||||
// Autonomous iris motion uses a fractal behavior to similate both the major
|
||||
// reaction of the eye plus the continuous smaller adjustments that occur.
|
||||
uint16_t oldIris = (IRIS_MIN + IRIS_MAX) / 2, newIris;
|
||||
#endif
|
||||
|
||||
// Initialise eyes ---------------------------------------------------------
|
||||
void initEyes(void)
|
||||
{
|
||||
Serial.println("Initialise eye objects");
|
||||
|
||||
// Initialise eye objects based on eyeInfo list in config.h:
|
||||
for (uint8_t e = 0; e < NUM_EYES; e++) {
|
||||
Serial.print("Create display #"); Serial.println(e);
|
||||
|
||||
eye[e].tft_cs = eyeInfo[e].select;
|
||||
eye[e].blink.state = NOBLINK;
|
||||
eye[e].xposition = eyeInfo[e].xposition;
|
||||
|
||||
pinMode(eye[e].tft_cs, OUTPUT);
|
||||
digitalWrite(eye[e].tft_cs, LOW);
|
||||
|
||||
// Also set up an individual eye-wink pin if defined:
|
||||
if (eyeInfo[e].wink >= 0) pinMode(eyeInfo[e].wink, INPUT_PULLUP);
|
||||
}
|
||||
|
||||
#if defined(BLINK_PIN) && (BLINK_PIN >= 0)
|
||||
pinMode(BLINK_PIN, INPUT_PULLUP); // Ditto for all-eyes blink pin
|
||||
#endif
|
||||
}
|
||||
|
||||
// UPDATE EYE --------------------------------------------------------------
|
||||
void updateEye (void)
|
||||
{
|
||||
#if defined(LIGHT_PIN) && (LIGHT_PIN >= 0) // Interactive iris
|
||||
|
||||
int16_t v = analogRead(LIGHT_PIN); // Raw dial/photocell reading
|
||||
#ifdef LIGHT_PIN_FLIP
|
||||
v = 1023 - v; // Reverse reading from sensor
|
||||
#endif
|
||||
if (v < LIGHT_MIN) v = LIGHT_MIN; // Clamp light sensor range
|
||||
else if (v > LIGHT_MAX) v = LIGHT_MAX;
|
||||
v -= LIGHT_MIN; // 0 to (LIGHT_MAX - LIGHT_MIN)
|
||||
#ifdef LIGHT_CURVE // Apply gamma curve to sensor input?
|
||||
v = (int16_t)(pow((double)v / (double)(LIGHT_MAX - LIGHT_MIN),
|
||||
LIGHT_CURVE) * (double)(LIGHT_MAX - LIGHT_MIN));
|
||||
#endif
|
||||
// And scale to iris range (IRIS_MAX is size at LIGHT_MIN)
|
||||
v = map(v, 0, (LIGHT_MAX - LIGHT_MIN), IRIS_MAX, IRIS_MIN);
|
||||
#ifdef IRIS_SMOOTH // Filter input (gradual motion)
|
||||
static int16_t irisValue = (IRIS_MIN + IRIS_MAX) / 2;
|
||||
irisValue = ((irisValue * 15) + v) / 16;
|
||||
frame(irisValue);
|
||||
#else // Unfiltered (immediate motion)
|
||||
frame(v);
|
||||
#endif // IRIS_SMOOTH
|
||||
|
||||
#else // Autonomous iris scaling -- invoke recursive function
|
||||
|
||||
newIris = random(IRIS_MIN, IRIS_MAX);
|
||||
split(oldIris, newIris, micros(), 10000000L, IRIS_MAX - IRIS_MIN);
|
||||
oldIris = newIris;
|
||||
|
||||
#endif // LIGHT_PIN
|
||||
}
|
||||
|
||||
// EYE-RENDERING FUNCTION --------------------------------------------------
|
||||
void drawEye( // Renders one eye. Inputs must be pre-clipped & valid.
|
||||
// Use native 32-bit variables where possible as this is 10% faster!
|
||||
uint8_t e, // Eye array index; 0 or 1 for left/right
|
||||
uint32_t iScale, // Scale factor for iris
|
||||
uint32_t scleraX, // First pixel X offset into sclera image
|
||||
uint32_t scleraY, // First pixel Y offset into sclera image
|
||||
uint32_t uT, // Upper eyelid threshold value
|
||||
uint32_t lT) { // Lower eyelid threshold value
|
||||
|
||||
uint32_t screenX, screenY, scleraXsave;
|
||||
int32_t irisX, irisY;
|
||||
uint32_t p, a;
|
||||
uint32_t d;
|
||||
|
||||
uint32_t pixels = 0;
|
||||
|
||||
// Set up raw pixel dump to entire screen. Although such writes can wrap
|
||||
// around automatically from end of rect back to beginning, the region is
|
||||
// reset on each frame here in case of an SPI glitch.
|
||||
digitalWrite(eye[e].tft_cs, LOW);
|
||||
tft.startWrite();
|
||||
tft.setAddrWindow(eye[e].xposition, 0, 128, 128);
|
||||
|
||||
// Now just issue raw 16-bit values for every pixel...
|
||||
|
||||
scleraXsave = scleraX; // Save initial X value to reset on each line
|
||||
irisY = scleraY - (SCLERA_HEIGHT - IRIS_HEIGHT) / 2;
|
||||
|
||||
// Eyelid image is left<>right swapped for two displays
|
||||
uint16_t lidX = 0;
|
||||
uint16_t dlidX = -1;
|
||||
if (e) dlidX = 1;
|
||||
for (screenY = 0; screenY < SCREEN_HEIGHT; screenY++, scleraY++, irisY++) {
|
||||
scleraX = scleraXsave;
|
||||
irisX = scleraXsave - (SCLERA_WIDTH - IRIS_WIDTH) / 2;
|
||||
if (e) lidX = 0; else lidX = SCREEN_WIDTH - 1;
|
||||
for (screenX = 0; screenX < SCREEN_WIDTH; screenX++, scleraX++, irisX++, lidX += dlidX) {
|
||||
if ((pgm_read_byte(lower + screenY * SCREEN_WIDTH + lidX) <= lT) ||
|
||||
(pgm_read_byte(upper + screenY * SCREEN_WIDTH + lidX) <= uT)) { // Covered by eyelid
|
||||
p = 0;
|
||||
} else if ((irisY < 0) || (irisY >= IRIS_HEIGHT) ||
|
||||
(irisX < 0) || (irisX >= IRIS_WIDTH)) { // In sclera
|
||||
p = pgm_read_word(sclera + scleraY * SCLERA_WIDTH + scleraX);
|
||||
} else { // Maybe iris...
|
||||
p = pgm_read_word(polar + irisY * IRIS_WIDTH + irisX); // Polar angle/dist
|
||||
d = (iScale * (p & 0x7F)) / 128; // Distance (Y)
|
||||
if (d < IRIS_MAP_HEIGHT) { // Within iris area
|
||||
a = (IRIS_MAP_WIDTH * (p >> 7)) / 512; // Angle (X)
|
||||
p = pgm_read_word(iris + d * IRIS_MAP_WIDTH + a); // Pixel = iris
|
||||
} else { // Not in iris
|
||||
p = pgm_read_word(sclera + scleraY * SCLERA_WIDTH + scleraX); // Pixel = sclera
|
||||
}
|
||||
}
|
||||
*(&pbuffer[dmaBuf][0] + pixels++) = p >> 8 | p << 8;
|
||||
|
||||
if (pixels >= BUFFER_SIZE) {
|
||||
yield();
|
||||
#ifdef USE_DMA
|
||||
tft.pushPixelsDMA(&pbuffer[dmaBuf][0], pixels);
|
||||
dmaBuf = !dmaBuf;
|
||||
#else
|
||||
tft.pushPixels(pbuffer, pixels);
|
||||
#endif
|
||||
pixels = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pixels) {
|
||||
#ifdef USE_DMA
|
||||
tft.pushPixelsDMA(&pbuffer[dmaBuf][0], pixels);
|
||||
#else
|
||||
tft.pushPixels(pbuffer, pixels);
|
||||
#endif
|
||||
}
|
||||
tft.endWrite();
|
||||
digitalWrite(eye[e].tft_cs, HIGH);
|
||||
}
|
||||
|
||||
// EYE ANIMATION -----------------------------------------------------------
|
||||
|
||||
const uint8_t ease[] = { // Ease in/out curve for eye movements 3*t^2-2*t^3
|
||||
0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 3, // T
|
||||
3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 7, 8, 9, 9, 10, 10, // h
|
||||
11, 12, 12, 13, 14, 15, 15, 16, 17, 18, 18, 19, 20, 21, 22, 23, // x
|
||||
24, 25, 26, 27, 27, 28, 29, 30, 31, 33, 34, 35, 36, 37, 38, 39, // 2
|
||||
40, 41, 42, 44, 45, 46, 47, 48, 50, 51, 52, 53, 54, 56, 57, 58, // A
|
||||
60, 61, 62, 63, 65, 66, 67, 69, 70, 72, 73, 74, 76, 77, 78, 80, // l
|
||||
81, 83, 84, 85, 87, 88, 90, 91, 93, 94, 96, 97, 98, 100, 101, 103, // e
|
||||
104, 106, 107, 109, 110, 112, 113, 115, 116, 118, 119, 121, 122, 124, 125, 127, // c
|
||||
128, 130, 131, 133, 134, 136, 137, 139, 140, 142, 143, 145, 146, 148, 149, 151, // J
|
||||
152, 154, 155, 157, 158, 159, 161, 162, 164, 165, 167, 168, 170, 171, 172, 174, // a
|
||||
175, 177, 178, 179, 181, 182, 183, 185, 186, 188, 189, 190, 192, 193, 194, 195, // c
|
||||
197, 198, 199, 201, 202, 203, 204, 205, 207, 208, 209, 210, 211, 213, 214, 215, // o
|
||||
216, 217, 218, 219, 220, 221, 222, 224, 225, 226, 227, 228, 228, 229, 230, 231, // b
|
||||
232, 233, 234, 235, 236, 237, 237, 238, 239, 240, 240, 241, 242, 243, 243, 244, // s
|
||||
245, 245, 246, 246, 247, 248, 248, 249, 249, 250, 250, 251, 251, 251, 252, 252, // o
|
||||
252, 253, 253, 253, 254, 254, 254, 254, 254, 255, 255, 255, 255, 255, 255, 255
|
||||
}; // n
|
||||
|
||||
#ifdef AUTOBLINK
|
||||
uint32_t timeOfLastBlink = 0L, timeToNextBlink = 0L;
|
||||
#endif
|
||||
|
||||
// Process motion for a single frame of left or right eye
|
||||
void frame(uint16_t iScale) // Iris scale (0-1023)
|
||||
{
|
||||
static uint32_t frames = 0; // Used in frame rate calculation
|
||||
static uint8_t eyeIndex = 0; // eye[] array counter
|
||||
int16_t eyeX, eyeY;
|
||||
uint32_t t = micros(); // Time at start of function
|
||||
|
||||
if (!(++frames & 255)) { // Every 256 frames...
|
||||
float elapsed = (millis() - startTime) / 1000.0;
|
||||
if (elapsed) Serial.println((uint16_t)(frames / elapsed)); // Print FPS
|
||||
}
|
||||
|
||||
if (++eyeIndex >= NUM_EYES) eyeIndex = 0; // Cycle through eyes, 1 per call
|
||||
|
||||
// X/Y movement
|
||||
|
||||
#if defined(JOYSTICK_X_PIN) && (JOYSTICK_X_PIN >= 0) && \
|
||||
defined(JOYSTICK_Y_PIN) && (JOYSTICK_Y_PIN >= 0)
|
||||
|
||||
// Read X/Y from joystick, constrain to circle
|
||||
int16_t dx, dy;
|
||||
int32_t d;
|
||||
eyeX = analogRead(JOYSTICK_X_PIN); // Raw (unclipped) X/Y reading
|
||||
eyeY = analogRead(JOYSTICK_Y_PIN);
|
||||
#ifdef JOYSTICK_X_FLIP
|
||||
eyeX = 1023 - eyeX;
|
||||
#endif
|
||||
#ifdef JOYSTICK_Y_FLIP
|
||||
eyeY = 1023 - eyeY;
|
||||
#endif
|
||||
dx = (eyeX * 2) - 1023; // A/D exact center is at 511.5. Scale coords
|
||||
dy = (eyeY * 2) - 1023; // X2 so range is -1023 to +1023 w/center at 0.
|
||||
if ((d = (dx * dx + dy * dy)) > (1023 * 1023)) { // Outside circle
|
||||
d = (int32_t)sqrt((float)d); // Distance from center
|
||||
eyeX = ((dx * 1023 / d) + 1023) / 2; // Clip to circle edge,
|
||||
eyeY = ((dy * 1023 / d) + 1023) / 2; // scale back to 0-1023
|
||||
}
|
||||
|
||||
#else // Autonomous X/Y eye motion
|
||||
// Periodically initiates motion to a new random point, random speed,
|
||||
// holds there for random period until next motion.
|
||||
|
||||
static bool eyeInMotion = false;
|
||||
static int16_t eyeOldX = 512, eyeOldY = 512, eyeNewX = 512, eyeNewY = 512;
|
||||
static uint32_t eyeMoveStartTime = 0L;
|
||||
static int32_t eyeMoveDuration = 0L;
|
||||
|
||||
int32_t dt = t - eyeMoveStartTime; // uS elapsed since last eye event
|
||||
if (eyeInMotion) { // Currently moving?
|
||||
if (dt >= eyeMoveDuration) { // Time up? Destination reached.
|
||||
eyeInMotion = false; // Stop moving
|
||||
eyeMoveDuration = random(3000000); // 0-3 sec stop
|
||||
eyeMoveStartTime = t; // Save initial time of stop
|
||||
eyeX = eyeOldX = eyeNewX; // Save position
|
||||
eyeY = eyeOldY = eyeNewY;
|
||||
} else { // Move time's not yet fully elapsed -- interpolate position
|
||||
int16_t e = ease[255 * dt / eyeMoveDuration] + 1; // Ease curve
|
||||
eyeX = eyeOldX + (((eyeNewX - eyeOldX) * e) / 256); // Interp X
|
||||
eyeY = eyeOldY + (((eyeNewY - eyeOldY) * e) / 256); // and Y
|
||||
}
|
||||
} else { // Eye stopped
|
||||
eyeX = eyeOldX;
|
||||
eyeY = eyeOldY;
|
||||
if (dt > eyeMoveDuration) { // Time up? Begin new move.
|
||||
int16_t dx, dy;
|
||||
uint32_t d;
|
||||
do { // Pick new dest in circle
|
||||
eyeNewX = random(1024);
|
||||
eyeNewY = random(1024);
|
||||
dx = (eyeNewX * 2) - 1023;
|
||||
dy = (eyeNewY * 2) - 1023;
|
||||
} while ((d = (dx * dx + dy * dy)) > (1023 * 1023)); // Keep trying
|
||||
eyeMoveDuration = random(72000, 144000); // ~1/14 - ~1/7 sec
|
||||
eyeMoveStartTime = t; // Save initial time of move
|
||||
eyeInMotion = true; // Start move on next frame
|
||||
}
|
||||
}
|
||||
#endif // JOYSTICK_X_PIN etc.
|
||||
|
||||
// Blinking
|
||||
#ifdef AUTOBLINK
|
||||
// Similar to the autonomous eye movement above -- blink start times
|
||||
// and durations are random (within ranges).
|
||||
if ((t - timeOfLastBlink) >= timeToNextBlink) { // Start new blink?
|
||||
timeOfLastBlink = t;
|
||||
uint32_t blinkDuration = random(36000, 72000); // ~1/28 - ~1/14 sec
|
||||
// Set up durations for both eyes (if not already winking)
|
||||
for (uint8_t e = 0; e < NUM_EYES; e++) {
|
||||
if (eye[e].blink.state == NOBLINK) {
|
||||
eye[e].blink.state = ENBLINK;
|
||||
eye[e].blink.startTime = t;
|
||||
eye[e].blink.duration = blinkDuration;
|
||||
}
|
||||
}
|
||||
timeToNextBlink = blinkDuration * 3 + random(4000000);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (eye[eyeIndex].blink.state) { // Eye currently blinking?
|
||||
// Check if current blink state time has elapsed
|
||||
if ((t - eye[eyeIndex].blink.startTime) >= eye[eyeIndex].blink.duration) {
|
||||
// Yes -- increment blink state, unless...
|
||||
if ((eye[eyeIndex].blink.state == ENBLINK) && ( // Enblinking and...
|
||||
#if defined(BLINK_PIN) && (BLINK_PIN >= 0)
|
||||
(digitalRead(BLINK_PIN) == LOW) || // blink or wink held...
|
||||
#endif
|
||||
((eyeInfo[eyeIndex].wink >= 0) &&
|
||||
digitalRead(eyeInfo[eyeIndex].wink) == LOW) )) {
|
||||
// Don't advance state yet -- eye is held closed instead
|
||||
} else { // No buttons, or other state...
|
||||
if (++eye[eyeIndex].blink.state > DEBLINK) { // Deblinking finished?
|
||||
eye[eyeIndex].blink.state = NOBLINK; // No longer blinking
|
||||
} else { // Advancing from ENBLINK to DEBLINK mode
|
||||
eye[eyeIndex].blink.duration *= 2; // DEBLINK is 1/2 ENBLINK speed
|
||||
eye[eyeIndex].blink.startTime = t;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else { // Not currently blinking...check buttons!
|
||||
#if defined(BLINK_PIN) && (BLINK_PIN >= 0)
|
||||
if (digitalRead(BLINK_PIN) == LOW) {
|
||||
// Manually-initiated blinks have random durations like auto-blink
|
||||
uint32_t blinkDuration = random(36000, 72000);
|
||||
for (uint8_t e = 0; e < NUM_EYES; e++) {
|
||||
if (eye[e].blink.state == NOBLINK) {
|
||||
eye[e].blink.state = ENBLINK;
|
||||
eye[e].blink.startTime = t;
|
||||
eye[e].blink.duration = blinkDuration;
|
||||
}
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
if ((eyeInfo[eyeIndex].wink >= 0) &&
|
||||
(digitalRead(eyeInfo[eyeIndex].wink) == LOW)) { // Wink!
|
||||
eye[eyeIndex].blink.state = ENBLINK;
|
||||
eye[eyeIndex].blink.startTime = t;
|
||||
eye[eyeIndex].blink.duration = random(45000, 90000);
|
||||
}
|
||||
}
|
||||
|
||||
// Process motion, blinking and iris scale into renderable values
|
||||
|
||||
// Scale eye X/Y positions (0-1023) to pixel units used by drawEye()
|
||||
eyeX = map(eyeX, 0, 1023, 0, SCLERA_WIDTH - 128);
|
||||
eyeY = map(eyeY, 0, 1023, 0, SCLERA_HEIGHT - 128);
|
||||
|
||||
// Horizontal position is offset so that eyes are very slightly crossed
|
||||
// to appear fixated (converged) at a conversational distance. Number
|
||||
// here was extracted from my posterior and not mathematically based.
|
||||
// I suppose one could get all clever with a range sensor, but for now...
|
||||
if (NUM_EYES > 1) {
|
||||
if (eyeIndex == 1) eyeX += 4;
|
||||
else eyeX -= 4;
|
||||
}
|
||||
if (eyeX > (SCLERA_WIDTH - 128)) eyeX = (SCLERA_WIDTH - 128);
|
||||
|
||||
// Eyelids are rendered using a brightness threshold image. This same
|
||||
// map can be used to simplify another problem: making the upper eyelid
|
||||
// track the pupil (eyes tend to open only as much as needed -- e.g. look
|
||||
// down and the upper eyelid drops). Just sample a point in the upper
|
||||
// lid map slightly above the pupil to determine the rendering threshold.
|
||||
static uint8_t uThreshold = 128;
|
||||
uint8_t lThreshold, n;
|
||||
#ifdef TRACKING
|
||||
int16_t sampleX = SCLERA_WIDTH / 2 - (eyeX / 2), // Reduce X influence
|
||||
sampleY = SCLERA_HEIGHT / 2 - (eyeY + IRIS_HEIGHT / 4);
|
||||
// Eyelid is slightly asymmetrical, so two readings are taken, averaged
|
||||
if (sampleY < 0) n = 0;
|
||||
else n = (pgm_read_byte(upper + sampleY * SCREEN_WIDTH + sampleX) +
|
||||
pgm_read_byte(upper + sampleY * SCREEN_WIDTH + (SCREEN_WIDTH - 1 - sampleX))) / 2;
|
||||
uThreshold = (uThreshold * 3 + n) / 4; // Filter/soften motion
|
||||
// Lower eyelid doesn't track the same way, but seems to be pulled upward
|
||||
// by tension from the upper lid.
|
||||
lThreshold = 254 - uThreshold;
|
||||
#else // No tracking -- eyelids full open unless blink modifies them
|
||||
uThreshold = lThreshold = 0;
|
||||
#endif
|
||||
|
||||
// The upper/lower thresholds are then scaled relative to the current
|
||||
// blink position so that blinks work together with pupil tracking.
|
||||
if (eye[eyeIndex].blink.state) { // Eye currently blinking?
|
||||
uint32_t s = (t - eye[eyeIndex].blink.startTime);
|
||||
if (s >= eye[eyeIndex].blink.duration) s = 255; // At or past blink end
|
||||
else s = 255 * s / eye[eyeIndex].blink.duration; // Mid-blink
|
||||
s = (eye[eyeIndex].blink.state == DEBLINK) ? 1 + s : 256 - s;
|
||||
n = (uThreshold * s + 254 * (257 - s)) / 256;
|
||||
lThreshold = (lThreshold * s + 254 * (257 - s)) / 256;
|
||||
} else {
|
||||
n = uThreshold;
|
||||
}
|
||||
|
||||
// Pass all the derived values to the eye-rendering function:
|
||||
drawEye(eyeIndex, iScale, eyeX, eyeY, n, lThreshold);
|
||||
|
||||
if (eyeIndex == (NUM_EYES - 1)) {
|
||||
user_loop(); // Call user code after rendering last eye
|
||||
}
|
||||
}
|
||||
|
||||
// AUTONOMOUS IRIS SCALING (if no photocell or dial) -----------------------
|
||||
|
||||
#if !defined(LIGHT_PIN) || (LIGHT_PIN < 0)
|
||||
|
||||
// Autonomous iris motion uses a fractal behavior to similate both the major
|
||||
// reaction of the eye plus the continuous smaller adjustments that occur.
|
||||
|
||||
void split( // Subdivides motion path into two sub-paths w/randimization
|
||||
int16_t startValue, // Iris scale value (IRIS_MIN to IRIS_MAX) at start
|
||||
int16_t endValue, // Iris scale value at end
|
||||
uint32_t startTime, // micros() at start
|
||||
int32_t duration, // Start-to-end time, in microseconds
|
||||
int16_t range) { // Allowable scale value variance when subdividing
|
||||
|
||||
if (range >= 8) { // Limit subdvision count, because recursion
|
||||
range /= 2; // Split range & time in half for subdivision,
|
||||
duration /= 2; // then pick random center point within range:
|
||||
int16_t midValue = (startValue + endValue - range) / 2 + random(range);
|
||||
uint32_t midTime = startTime + duration;
|
||||
split(startValue, midValue, startTime, duration, range); // First half
|
||||
split(midValue , endValue, midTime , duration, range); // Second half
|
||||
} else { // No more subdivisons, do iris motion...
|
||||
int32_t dt; // Time (micros) since start of motion
|
||||
int16_t v; // Interim value
|
||||
while ((dt = (micros() - startTime)) < duration) {
|
||||
v = startValue + (((endValue - startValue) * dt) / duration);
|
||||
if (v < IRIS_MIN) v = IRIS_MIN; // Clip just in case
|
||||
else if (v > IRIS_MAX) v = IRIS_MAX;
|
||||
frame(v); // Draw frame w/interim iris scale value
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // !LIGHT_PIN
|
||||
65
libraries/TFT_eSPI/examples/Generic/Animated_Eyes_1/user.cpp
Normal file
65
libraries/TFT_eSPI/examples/Generic/Animated_Eyes_1/user.cpp
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
#if 1 // Change to 0 to disable this code (must enable ONE user*.cpp only!)
|
||||
|
||||
// This file provides a crude way to "drop in" user code to the eyes,
|
||||
// allowing concurrent operations without having to maintain a bunch of
|
||||
// special derivatives of the eye code (which is still undergoing a lot
|
||||
// of development). Just replace the source code contents of THIS TAB ONLY,
|
||||
// compile and upload to board. Shouldn't need to modify other eye code.
|
||||
|
||||
// User globals can go here, recommend declaring as static, e.g.:
|
||||
// static int foo = 42;
|
||||
|
||||
// Called once near the end of the setup() function.
|
||||
void user_setup(void) {
|
||||
}
|
||||
|
||||
// Called periodically during eye animation. This is invoked in the
|
||||
// interval before starting drawing on the last eye so it won't exacerbate
|
||||
// visible tearing in eye rendering.
|
||||
// This function BLOCKS, it does NOT multitask with the eye animation code,
|
||||
// and performance here will have a direct impact on overall refresh rates,
|
||||
// so keep it simple. Avoid loops (e.g. if animating something like a servo
|
||||
// or NeoPixels in response to some trigger) and instead rely on state
|
||||
// machines or similar. Additionally, calls to this function are NOT time-
|
||||
// constant -- eye rendering time can vary frame to frame, so animation or
|
||||
// other over-time operations won't look very good using simple +/-
|
||||
// increments, it's better to use millis() or micros() and work
|
||||
// algebraically with elapsed times instead.
|
||||
void user_loop(void) {
|
||||
/*
|
||||
Suppose we have a global bool "animating" (meaning something is in
|
||||
motion) and global uint32_t's "startTime" (the initial time at which
|
||||
something triggered movement) and "transitionTime" (the total time
|
||||
over which movement should occur, expressed in microseconds).
|
||||
Maybe it's servos, maybe NeoPixels, or something different altogether.
|
||||
This function might resemble something like (pseudocode):
|
||||
|
||||
if(!animating) {
|
||||
Not in motion, check sensor for trigger...
|
||||
if(read some sensor) {
|
||||
Motion is triggered! Record startTime, set transition
|
||||
to 1.5 seconds and set animating flag:
|
||||
startTime = micros();
|
||||
transitionTime = 1500000;
|
||||
animating = true;
|
||||
No motion actually takes place yet, that will begin on
|
||||
the next pass through this function.
|
||||
}
|
||||
} else {
|
||||
Currently in motion, ignore trigger and move things instead...
|
||||
uint32_t elapsed = millis() - startTime;
|
||||
if(elapsed < transitionTime) {
|
||||
Part way through motion...how far along?
|
||||
float ratio = (float)elapsed / (float)transitionTime;
|
||||
Do something here based on ratio, 0.0 = start, 1.0 = end
|
||||
} else {
|
||||
End of motion reached.
|
||||
Take whatever steps here to move into final position (1.0),
|
||||
and then clear the "animating" flag:
|
||||
animating = false;
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
#endif // 0
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
// SERVO BAT: flapping paper-cutout bat (attached to servo on SERVO_PIN)
|
||||
// triggered by contact-sensitive conductive thread on CAPTOUCH_PIN.
|
||||
// See user.cpp for basics of connecting user code to animated eyes.
|
||||
|
||||
#if 0 // Change to 1 to enable this code (must enable ONE user*.cpp only!)
|
||||
|
||||
#include "Adafruit_FreeTouch.h"
|
||||
#include <Servo.h>
|
||||
|
||||
#define CAPTOUCH_PIN A5 // Capacitive touch pin - attach conductive thread here
|
||||
#define SERVO_PIN 4 // Servo plugged in here
|
||||
|
||||
// Set up capacitive touch button using the FreeTouch library
|
||||
static Adafruit_FreeTouch touch(CAPTOUCH_PIN, OVERSAMPLE_4, RESISTOR_50K, FREQ_MODE_NONE);
|
||||
static long oldState; // Last-read touch value
|
||||
static bool isTouched = false; // When true, bat is flapping
|
||||
static uint32_t touchTime = 0; // millis() time when flapping started
|
||||
static uint32_t touchThreshold;
|
||||
|
||||
Servo servo;
|
||||
|
||||
void user_setup(void) {
|
||||
if (!touch.begin())
|
||||
Serial.println("Cap touch init failed");
|
||||
servo.attach(SERVO_PIN);
|
||||
servo.write(0); // Move servo to idle position
|
||||
servo.detach();
|
||||
|
||||
// Attempt to auto-calibrate the touch threshold
|
||||
// (assumes thread is NOT touched on startup!)
|
||||
touchThreshold = 0;
|
||||
for(int i=0; i<10; i++) {
|
||||
touchThreshold += touch.measure(); // Accumulate 10 readings
|
||||
delay(50);
|
||||
}
|
||||
touchThreshold /= 10; // Average "not touched" value
|
||||
touchThreshold = ((touchThreshold * 127) + 1023) / 128; // Threshold = ~1% toward max
|
||||
|
||||
oldState = touch.measure();
|
||||
}
|
||||
|
||||
#define FLAP_TIME_RISING 900 // 0-to-180 degree servo sweep time, in milliseconds
|
||||
#define FLAP_TIME_FALLING 1200 // 180-to-0 servo sweep time
|
||||
#define FLAP_REPS 3 // Number of times to flap
|
||||
#define FLAP_TIME_PER (FLAP_TIME_RISING + FLAP_TIME_FALLING)
|
||||
#define FLAP_TIME_TOTAL (FLAP_TIME_PER * FLAP_REPS)
|
||||
|
||||
void user_loop(void) {
|
||||
long newState = touch.measure();
|
||||
Serial.println(newState);
|
||||
|
||||
if (isTouched) {
|
||||
uint32_t elapsed = millis() - touchTime;
|
||||
if (elapsed >= FLAP_TIME_TOTAL) { // After all flaps are completed
|
||||
isTouched = false; // Bat goes idle again
|
||||
servo.write(0);
|
||||
servo.detach();
|
||||
} else {
|
||||
elapsed %= FLAP_TIME_PER; // Time within current flap cycle
|
||||
if (elapsed < FLAP_TIME_RISING) { // Over the course of 0 to FLAP_TIME_RISING...
|
||||
servo.write(elapsed * 180 / FLAP_TIME_RISING); // Move 0 to 180 degrees
|
||||
} else { // Over course of FLAP_TIME_FALLING, return to 0
|
||||
servo.write(180 - ((elapsed - FLAP_TIME_RISING) * 180 / FLAP_TIME_FALLING));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Bat is idle...check for capacitive touch...
|
||||
if (newState > touchThreshold && oldState < touchThreshold) {
|
||||
delay(100); // Short delay to debounce
|
||||
newState = touch.measure(); // Verify whether still touched
|
||||
if (newState > touchThreshold) { // It is!
|
||||
isTouched = true; // Start a new flap session
|
||||
touchTime = millis();
|
||||
servo.attach(SERVO_PIN);
|
||||
servo.write(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
oldState = newState; // Save cap touch state
|
||||
}
|
||||
|
||||
#endif // 0
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
#if 0 // Change to 1 to enable this code (must enable ONE user*.cpp only!)
|
||||
|
||||
// Christmas demo for eye + NeoPixels. Randomly sets pixels in holiday-themed colors.
|
||||
|
||||
#include <Adafruit_NeoPixel.h>
|
||||
|
||||
// Pin 8 is the built-in NeoPixels on Circuit Playground Express & Bluetooth.
|
||||
// With a TFT Gizmo attached, you can use A1 or A2 to easily connect a strand.
|
||||
#define LED_PIN 8
|
||||
#define LED_COUNT 10
|
||||
#define LED_BRIGHTNESS 50 // about 1/5 brightness (max = 255)
|
||||
#define TWINKLE_INTERVAL 333 // Every 333 ms (1/3 second), change a pixel
|
||||
#define LIT_PIXELS (LED_COUNT / 3) // Must be LESS than LED_COUNT/2
|
||||
|
||||
Adafruit_NeoPixel pixels(LED_COUNT, LED_PIN);
|
||||
|
||||
|
||||
uint32_t timeOfLastTwinkle = 0; // Used for timing pixel changes
|
||||
uint8_t litPixel[LIT_PIXELS]; // Indices of which pixels are lit
|
||||
uint8_t pixelIndex = LIT_PIXELS; // Index of currently-changing litPixel
|
||||
|
||||
uint32_t colors[] = { 0xFF0000, 0x00FF00, 0xFFFFFF }; // Red, green, white
|
||||
#define NUM_COLORS (sizeof colors / sizeof colors[0])
|
||||
|
||||
void user_setup(void) {
|
||||
pixels.begin(); // INITIALIZE NeoPixel strip object (REQUIRED)
|
||||
pixels.show(); // Turn OFF all pixels ASAP
|
||||
pixels.setBrightness(LED_BRIGHTNESS);
|
||||
memset(litPixel, 255, sizeof litPixel); // Fill with out-of-range nonsense
|
||||
}
|
||||
|
||||
void user_loop(void) {
|
||||
uint32_t t = millis();
|
||||
|
||||
if((t - timeOfLastTwinkle) >= TWINKLE_INTERVAL) { // Time to update pixels?
|
||||
timeOfLastTwinkle = t;
|
||||
if(++pixelIndex >= LIT_PIXELS) pixelIndex = 0;
|
||||
|
||||
// Pick a NEW pixel that's not currently lit and not adjacent to a lit one.
|
||||
// This just brute-force randomly tries pixels until a valid one is found,
|
||||
// no mathematical cleverness. Should only take a few iterations and won't
|
||||
// significantly slow down the eyes.
|
||||
int newPixel, pixelAfter, pixelBefore;
|
||||
do {
|
||||
newPixel = random(LED_COUNT);
|
||||
pixelAfter = (newPixel + 1) % LED_COUNT;
|
||||
pixelBefore = (newPixel - 1);
|
||||
if(pixelBefore < 0) pixelBefore = LED_COUNT - 1;
|
||||
} while(pixels.getPixelColor(newPixel) ||
|
||||
pixels.getPixelColor(pixelAfter) ||
|
||||
pixels.getPixelColor(pixelBefore));
|
||||
|
||||
// Turn OFF litPixel[pixelIndex]
|
||||
pixels.setPixelColor(litPixel[pixelIndex], 0);
|
||||
// 'newPixel' is the winner. Save in the litPixel[] array for later...
|
||||
litPixel[pixelIndex] = newPixel;
|
||||
// Turn ON newPixel with a random color from the colors[] list.
|
||||
pixels.setPixelColor(newPixel, colors[random(NUM_COLORS)]);
|
||||
|
||||
pixels.show();
|
||||
}
|
||||
}
|
||||
|
||||
#endif // 0
|
||||
|
|
@ -0,0 +1,146 @@
|
|||
// An adaption of the "UncannyEyes" sketch (see eye_functions tab)
|
||||
// for the TFT_eSPI library. As written the sketch is for driving
|
||||
// two TFT displays.
|
||||
|
||||
// The size of the displayed eye is determined by the screen size and
|
||||
// resolution. The eye image is 128 pixels wide. In humans the palpebral
|
||||
// fissure (open eye) width is about 30mm so a low resolution, large
|
||||
// pixel size display works best to show a scale eye image. Note that
|
||||
// display manufacturers usually quote the diagonal measurement, so a
|
||||
// 128 x 128 1.7" display or 128 x 160 2" display is about right.
|
||||
|
||||
// The number of displays and chip selects used are defined in the
|
||||
// config.h tab. The display count can be set to 1. If using one
|
||||
// TFT and the chip select for that display is already defined in
|
||||
// the TFT_eSPI library then change the chip select pins to -1 in the
|
||||
// "config.h" tab.
|
||||
|
||||
// The wiring for 2 TFT displays to an ESP32 is described in the
|
||||
// "wiring" tab of this sketch.
|
||||
|
||||
// Configuration settings for the eye, eye style, display count,
|
||||
// chip selects and x offsets can be defined in the sketch "config.h" tab.
|
||||
|
||||
// Performance (frames per second = fps) can be improved by using
|
||||
// DMA (for SPI displays only) on ESP32 and STM32 processors. Use
|
||||
// as high a SPI clock rate as is supported by the display. 27MHz
|
||||
// minimum, some displays can be operated at higher clock rates in
|
||||
// the range 40-80MHz.
|
||||
|
||||
// Single defaultEye performance for different processors
|
||||
// No DMA With DMA
|
||||
// ESP8266 (160MHz CPU) 40MHz SPI 36 fps
|
||||
// ESP32 27MHz SPI 53 fps 85 fps
|
||||
// ESP32 40MHz SPI 67 fps 102 fps
|
||||
// ESP32 80MHz SPI 82 fps 116 fps // Note: Few displays work reliably at 80MHz
|
||||
// STM32F401 55MHz SPI 44 fps 90 fps
|
||||
// STM32F446 55MHz SPI 83 fps 155 fps
|
||||
// STM32F767 55MHz SPI 136 fps 197 fps
|
||||
|
||||
// DMA can be used with RP2040, STM32 and ESP32 processors when the interface
|
||||
// is SPI, uncomment the next line:
|
||||
//#define USE_DMA
|
||||
|
||||
// Load TFT driver library
|
||||
#include <SPI.h>
|
||||
#include <TFT_eSPI.h>
|
||||
TFT_eSPI tft; // A single instance is used for 1 or 2 displays
|
||||
|
||||
// A pixel buffer is used during eye rendering
|
||||
#define BUFFER_SIZE 1024 // 128 to 1024 seems optimum
|
||||
|
||||
#ifdef USE_DMA
|
||||
#define BUFFERS 2 // 2 toggle buffers with DMA
|
||||
#else
|
||||
#define BUFFERS 1 // 1 buffer for no DMA
|
||||
#endif
|
||||
|
||||
uint16_t pbuffer[BUFFERS][BUFFER_SIZE]; // Pixel rendering buffer
|
||||
bool dmaBuf = 0; // DMA buffer selection
|
||||
|
||||
// This struct is populated in config.h
|
||||
typedef struct { // Struct is defined before including config.h --
|
||||
int8_t select; // pin numbers for each eye's screen select line
|
||||
int8_t wink; // and wink button (or -1 if none) specified there,
|
||||
uint8_t rotation; // also display rotation and the x offset
|
||||
int16_t xposition; // position of eye on the screen
|
||||
} eyeInfo_t;
|
||||
|
||||
#include "config.h" // ****** CONFIGURATION IS DONE IN HERE ******
|
||||
|
||||
extern void user_setup(void); // Functions in the user*.cpp files
|
||||
extern void user_loop(void);
|
||||
|
||||
#define SCREEN_X_START 0
|
||||
#define SCREEN_X_END SCREEN_WIDTH // Badly named, actually the "eye" width!
|
||||
#define SCREEN_Y_START 0
|
||||
#define SCREEN_Y_END SCREEN_HEIGHT // Actually "eye" height
|
||||
|
||||
// A simple state machine is used to control eye blinks/winks:
|
||||
#define NOBLINK 0 // Not currently engaged in a blink
|
||||
#define ENBLINK 1 // Eyelid is currently closing
|
||||
#define DEBLINK 2 // Eyelid is currently opening
|
||||
typedef struct {
|
||||
uint8_t state; // NOBLINK/ENBLINK/DEBLINK
|
||||
uint32_t duration; // Duration of blink state (micros)
|
||||
uint32_t startTime; // Time (micros) of last state change
|
||||
} eyeBlink;
|
||||
|
||||
struct { // One-per-eye structure
|
||||
int16_t tft_cs; // Chip select pin for each display
|
||||
eyeBlink blink; // Current blink/wink state
|
||||
int16_t xposition; // x position of eye image
|
||||
} eye[NUM_EYES];
|
||||
|
||||
uint32_t startTime; // For FPS indicator
|
||||
|
||||
// INITIALIZATION -- runs once at startup ----------------------------------
|
||||
void setup(void) {
|
||||
Serial.begin(115200);
|
||||
//while (!Serial);
|
||||
Serial.println("Starting");
|
||||
|
||||
#if defined(DISPLAY_BACKLIGHT) && (DISPLAY_BACKLIGHT >= 0)
|
||||
// Enable backlight pin, initially off
|
||||
Serial.println("Backlight turned off");
|
||||
pinMode(DISPLAY_BACKLIGHT, OUTPUT);
|
||||
digitalWrite(DISPLAY_BACKLIGHT, LOW);
|
||||
#endif
|
||||
|
||||
// User call for additional features
|
||||
user_setup();
|
||||
|
||||
// Initialise the eye(s), this will set all chip selects low for the tft.init()
|
||||
initEyes();
|
||||
|
||||
// Initialise TFT
|
||||
Serial.println("Initialising displays");
|
||||
tft.init();
|
||||
|
||||
#ifdef USE_DMA
|
||||
tft.initDMA();
|
||||
#endif
|
||||
|
||||
// Raise chip select(s) so that displays can be individually configured
|
||||
digitalWrite(eye[0].tft_cs, HIGH);
|
||||
if (NUM_EYES > 1) digitalWrite(eye[1].tft_cs, HIGH);
|
||||
|
||||
for (uint8_t e = 0; e < NUM_EYES; e++) {
|
||||
digitalWrite(eye[e].tft_cs, LOW);
|
||||
tft.setRotation(eyeInfo[e].rotation);
|
||||
tft.fillScreen(TFT_BLACK);
|
||||
digitalWrite(eye[e].tft_cs, HIGH);
|
||||
}
|
||||
|
||||
#if defined(DISPLAY_BACKLIGHT) && (DISPLAY_BACKLIGHT >= 0)
|
||||
Serial.println("Backlight now on!");
|
||||
analogWrite(DISPLAY_BACKLIGHT, BACKLIGHT_MAX);
|
||||
#endif
|
||||
|
||||
startTime = millis(); // For frame-rate calculation
|
||||
}
|
||||
|
||||
// MAIN LOOP -- runs continuously after setup() ----------------------------
|
||||
void loop() {
|
||||
updateEye();
|
||||
}
|
||||
93
libraries/TFT_eSPI/examples/Generic/Animated_Eyes_2/config.h
Normal file
93
libraries/TFT_eSPI/examples/Generic/Animated_Eyes_2/config.h
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
// Pin selections here are based on the original Adafruit Learning System
|
||||
// guide for the Teensy 3.x project. Some of these pin numbers don't even
|
||||
// exist on the smaller SAMD M0 & M4 boards, so you may need to make other
|
||||
// selections:
|
||||
|
||||
// GRAPHICS SETTINGS (appearance of eye) -----------------------------------
|
||||
|
||||
// If using a SINGLE EYE, you might want this next line enabled, which
|
||||
// uses a simpler "football-shaped" eye that's left/right symmetrical.
|
||||
// Default shape includes the caruncle, creating distinct left/right eyes.
|
||||
|
||||
//#define SYMMETRICAL_EYELID
|
||||
|
||||
// Enable ONE of these #includes -- HUGE graphics tables for various eyes:
|
||||
#include "data/defaultEye.h" // Standard human-ish hazel eye -OR-
|
||||
//#include "data/dragonEye.h" // Slit pupil fiery dragon/demon eye -OR-
|
||||
//#include "data/noScleraEye.h" // Large iris, no sclera -OR-
|
||||
//#include "data/goatEye.h" // Horizontal pupil goat/Krampus eye -OR-
|
||||
//#include "data/newtEye.h" // Eye of newt -OR-
|
||||
//#include "data/terminatorEye.h" // Git to da choppah!
|
||||
//#include "data/catEye.h" // Cartoonish cat (flat "2D" colors)
|
||||
//#include "data/owlEye.h" // Minerva the owl (DISABLE TRACKING)
|
||||
//#include "data/naugaEye.h" // Nauga googly eye (DISABLE TRACKING)
|
||||
//#include "data/doeEye.h" // Cartoon deer eye (DISABLE TRACKING)
|
||||
|
||||
// DISPLAY HARDWARE SETTINGS (screen type & connections) -------------------
|
||||
#define TFT_COUNT 2 // Number of screens (1 or 2)
|
||||
#define TFT1_CS 22 // TFT 1 chip select pin (set to -1 to use TFT_eSPI setup)
|
||||
#define TFT2_CS 21 // TFT 2 chip select pin (set to -1 to use TFT_eSPI setup)
|
||||
#define TFT_1_ROT 1 // TFT 1 rotation
|
||||
#define TFT_2_ROT 3 // TFT 2 rotation
|
||||
#define EYE_1_XPOSITION 0 // x shift for eye 1 image on display
|
||||
#define EYE_2_XPOSITION 0 // x shift for eye 2 image on display
|
||||
|
||||
#define DISPLAY_BACKLIGHT -1 // Pin for backlight control (-1 for none)
|
||||
#define BACKLIGHT_MAX 255
|
||||
|
||||
// EYE LIST ----------------------------------------------------------------
|
||||
#define NUM_EYES 2 // Number of eyes to display (1 or 2)
|
||||
|
||||
#define BLINK_PIN -1 // Pin for manual blink button (BOTH eyes)
|
||||
#define LH_WINK_PIN -1 // Left wink pin (set to -1 for no pin)
|
||||
#define RH_WINK_PIN -1 // Right wink pin (set to -1 for no pin)
|
||||
|
||||
// This table contains ONE LINE PER EYE. The table MUST be present with
|
||||
// this name and contain ONE OR MORE lines. Each line contains THREE items:
|
||||
// a pin number for the corresponding TFT/OLED display's SELECT line, a pin
|
||||
// pin number for that eye's "wink" button (or -1 if not used), a screen
|
||||
// rotation value (0-3) and x position offset for that eye.
|
||||
|
||||
#if (NUM_EYES == 2)
|
||||
eyeInfo_t eyeInfo[] = {
|
||||
{ TFT1_CS, LH_WINK_PIN, TFT_1_ROT, EYE_1_XPOSITION }, // LEFT EYE chip select and wink pins, rotation and offset
|
||||
{ TFT2_CS, RH_WINK_PIN, TFT_2_ROT, EYE_2_XPOSITION }, // RIGHT EYE chip select and wink pins, rotation and offset
|
||||
};
|
||||
#else
|
||||
eyeInfo_t eyeInfo[] = {
|
||||
{ TFT1_CS, LH_WINK_PIN, TFT_1_ROT, EYE_1_XPOSITION }, // EYE chip select and wink pins, rotation and offset
|
||||
};
|
||||
#endif
|
||||
|
||||
// INPUT SETTINGS (for controlling eye motion) -----------------------------
|
||||
|
||||
// JOYSTICK_X_PIN and JOYSTICK_Y_PIN specify analog input pins for manually
|
||||
// controlling the eye with an analog joystick. If set to -1 or if not
|
||||
// defined, the eye will move on its own.
|
||||
// IRIS_PIN specifies an analog input pin for a photocell to make pupils
|
||||
// react to light (or potentiometer for manual control). If set to -1 or
|
||||
// if not defined, the pupils will change on their own.
|
||||
// BLINK_PIN specifies an input pin for a button (to ground) that will
|
||||
// make any/all eyes blink. If set to -1 or if not defined, the eyes will
|
||||
// only blink if AUTOBLINK is defined, or if the eyeInfo[] table above
|
||||
// includes wink button settings for each eye.
|
||||
|
||||
//#define JOYSTICK_X_PIN A0 // Analog pin for eye horiz pos (else auto)
|
||||
//#define JOYSTICK_Y_PIN A1 // Analog pin for eye vert position (")
|
||||
//#define JOYSTICK_X_FLIP // If defined, reverse stick X axis
|
||||
//#define JOYSTICK_Y_FLIP // If defined, reverse stick Y axis
|
||||
#define TRACKING // If defined, eyelid tracks pupil
|
||||
#define AUTOBLINK // If defined, eyes also blink autonomously
|
||||
|
||||
// #define LIGHT_PIN -1 // Light sensor pin
|
||||
#define LIGHT_CURVE 0.33 // Light sensor adjustment curve
|
||||
#define LIGHT_MIN 0 // Minimum useful reading from light sensor
|
||||
#define LIGHT_MAX 1023 // Maximum useful reading from sensor
|
||||
|
||||
#define IRIS_SMOOTH // If enabled, filter input from IRIS_PIN
|
||||
#if !defined(IRIS_MIN) // Each eye might have its own MIN/MAX
|
||||
#define IRIS_MIN 90 // Iris size (0-1023) in brightest light
|
||||
#endif
|
||||
#if !defined(IRIS_MAX)
|
||||
#define IRIS_MAX 130 // Iris size (0-1023) in darkest light
|
||||
#endif
|
||||
11600
libraries/TFT_eSPI/examples/Generic/Animated_Eyes_2/data/catEye.h
Normal file
11600
libraries/TFT_eSPI/examples/Generic/Animated_Eyes_2/data/catEye.h
Normal file
File diff suppressed because it is too large
Load diff
13349
libraries/TFT_eSPI/examples/Generic/Animated_Eyes_2/data/defaultEye.h
Normal file
13349
libraries/TFT_eSPI/examples/Generic/Animated_Eyes_2/data/defaultEye.h
Normal file
File diff suppressed because it is too large
Load diff
15646
libraries/TFT_eSPI/examples/Generic/Animated_Eyes_2/data/doeEye.h
Normal file
15646
libraries/TFT_eSPI/examples/Generic/Animated_Eyes_2/data/doeEye.h
Normal file
File diff suppressed because it is too large
Load diff
17018
libraries/TFT_eSPI/examples/Generic/Animated_Eyes_2/data/dragonEye.h
Normal file
17018
libraries/TFT_eSPI/examples/Generic/Animated_Eyes_2/data/dragonEye.h
Normal file
File diff suppressed because it is too large
Load diff
12807
libraries/TFT_eSPI/examples/Generic/Animated_Eyes_2/data/goatEye.h
Normal file
12807
libraries/TFT_eSPI/examples/Generic/Animated_Eyes_2/data/goatEye.h
Normal file
File diff suppressed because it is too large
Load diff
101
libraries/TFT_eSPI/examples/Generic/Animated_Eyes_2/data/logo.h
Normal file
101
libraries/TFT_eSPI/examples/Generic/Animated_Eyes_2/data/logo.h
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
// Logo helps with screen orientation & positioning
|
||||
|
||||
#define LOGO_TOP_WIDTH 59
|
||||
#define LOGO_TOP_HEIGHT 59
|
||||
|
||||
const uint8_t logo_top[472] PROGMEM= {
|
||||
0X00, 0X00, 0X00, 0X01, 0XC0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X03,
|
||||
0XC0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X03, 0XE0, 0X00, 0X00, 0X00,
|
||||
0X00, 0X00, 0X00, 0X07, 0XE0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X0F,
|
||||
0XF0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X0F, 0XF0, 0X00, 0X00, 0X00,
|
||||
0X00, 0X00, 0X00, 0X1F, 0XF0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X3F,
|
||||
0XF0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X7F, 0XF8, 0X00, 0X00, 0X00,
|
||||
0X00, 0X00, 0X00, 0X7F, 0XF8, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0XFF,
|
||||
0XF8, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, 0XFF, 0XFC, 0X00, 0X00, 0X00,
|
||||
0X00, 0X00, 0X01, 0XFF, 0XFC, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, 0XFF,
|
||||
0XFC, 0X00, 0X00, 0X00, 0X00, 0X00, 0X03, 0XFF, 0XFC, 0X00, 0X00, 0X00,
|
||||
0X00, 0X00, 0X03, 0XFF, 0XFE, 0X00, 0X00, 0X00, 0XFF, 0XFF, 0X83, 0XFF,
|
||||
0XFE, 0X00, 0X00, 0X00, 0XFF, 0XFF, 0XF3, 0XFF, 0XFE, 0X00, 0X00, 0X00,
|
||||
0XFF, 0XFF, 0XFB, 0XFF, 0XFC, 0X00, 0X00, 0X00, 0X7F, 0XFF, 0XFF, 0XFF,
|
||||
0XFC, 0X00, 0X00, 0X00, 0X7F, 0XFF, 0XFF, 0XFB, 0XFC, 0X30, 0X00, 0X00,
|
||||
0X3F, 0XFF, 0XFF, 0XF1, 0XFB, 0XFF, 0X00, 0X00, 0X1F, 0XFF, 0XFF, 0XF1,
|
||||
0XFF, 0XFF, 0XE0, 0X00, 0X1F, 0XFF, 0XFF, 0XE1, 0XFF, 0XFF, 0XFE, 0X00,
|
||||
0X0F, 0XFF, 0XFF, 0XE1, 0XFF, 0XFF, 0XFF, 0X80, 0X07, 0XFF, 0XEF, 0XE1,
|
||||
0XFF, 0XFF, 0XFF, 0XE0, 0X03, 0XFF, 0XC1, 0XE3, 0XFF, 0XFF, 0XFF, 0XE0,
|
||||
0X03, 0XFF, 0XC0, 0XF3, 0XFF, 0XFF, 0XFF, 0XE0, 0X01, 0XFF, 0XF0, 0X7F,
|
||||
0XC3, 0XFF, 0XFF, 0XC0, 0X00, 0XFF, 0XF8, 0X7F, 0X01, 0XFF, 0XFF, 0X00,
|
||||
0X00, 0X7F, 0XFF, 0XFE, 0X03, 0XFF, 0XFE, 0X00, 0X00, 0X1F, 0XFF, 0XFF,
|
||||
0X0F, 0XFF, 0XFC, 0X00, 0X00, 0X07, 0XFF, 0XFF, 0XFF, 0XFF, 0XF0, 0X00,
|
||||
0X00, 0X01, 0XFF, 0X3F, 0XFF, 0XFF, 0XE0, 0X00, 0X00, 0X07, 0XFC, 0X39,
|
||||
0XFF, 0XFF, 0X80, 0X00, 0X00, 0X0F, 0XF8, 0X38, 0XFF, 0XFF, 0X00, 0X00,
|
||||
0X00, 0X1F, 0XF0, 0X78, 0X7F, 0XFC, 0X00, 0X00, 0X00, 0X3F, 0XF0, 0XF8,
|
||||
0X7F, 0X00, 0X00, 0X00, 0X00, 0X3F, 0XF1, 0XFC, 0X7F, 0X80, 0X00, 0X00,
|
||||
0X00, 0X7F, 0XFF, 0XFE, 0X3F, 0XC0, 0X00, 0X00, 0X00, 0X7F, 0XFF, 0XFE,
|
||||
0X3F, 0XC0, 0X00, 0X00, 0X00, 0XFF, 0XFF, 0XFF, 0XFF, 0XE0, 0X00, 0X00,
|
||||
0X00, 0XFF, 0XFF, 0XFF, 0XFF, 0XE0, 0X00, 0X00, 0X00, 0XFF, 0XFF, 0XFF,
|
||||
0XFF, 0XE0, 0X00, 0X00, 0X01, 0XFF, 0XFF, 0XBF, 0XFF, 0XE0, 0X00, 0X00,
|
||||
0X01, 0XFF, 0XFF, 0XBF, 0XFF, 0XE0, 0X00, 0X00, 0X01, 0XFF, 0XFF, 0X1F,
|
||||
0XFF, 0XE0, 0X00, 0X00, 0X03, 0XFF, 0XFE, 0X1F, 0XFF, 0XE0, 0X00, 0X00,
|
||||
0X03, 0XFF, 0XFC, 0X0F, 0XFF, 0XE0, 0X00, 0X00, 0X03, 0XFF, 0XF0, 0X0F,
|
||||
0XFF, 0XE0, 0X00, 0X00, 0X03, 0XFF, 0XC0, 0X07, 0XFF, 0XE0, 0X00, 0X00,
|
||||
0X07, 0XFE, 0X00, 0X03, 0XFF, 0XE0, 0X00, 0X00, 0X07, 0XF0, 0X00, 0X01,
|
||||
0XFF, 0XE0, 0X00, 0X00, 0X03, 0X80, 0X00, 0X00, 0X7F, 0XE0, 0X00, 0X00,
|
||||
0X00, 0X00, 0X00, 0X00, 0X3F, 0XE0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00,
|
||||
0X0F, 0XE0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X07, 0XE0, 0X00, 0X00,
|
||||
0X00, 0X00, 0X00, 0X00, 0X03, 0XE0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00,
|
||||
0X00, 0XE0, 0X00, 0X00 };
|
||||
|
||||
#define LOGO_BOTTOM_WIDTH 128
|
||||
#define LOGO_BOTTOM_HEIGHT 37
|
||||
|
||||
const uint8_t logo_bottom[592] PROGMEM= {
|
||||
0X00, 0X00, 0X00, 0X00, 0X3E, 0X00, 0X00, 0X00, 0X7F, 0X00, 0X00, 0X00,
|
||||
0X00, 0X03, 0XE0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X3E, 0X00, 0X00, 0X00,
|
||||
0XFF, 0X00, 0X00, 0X00, 0X00, 0X03, 0XE0, 0X00, 0X00, 0X00, 0X00, 0X00,
|
||||
0X3E, 0X00, 0X00, 0X01, 0XFF, 0X00, 0X00, 0X00, 0X00, 0X03, 0XE0, 0X00,
|
||||
0X00, 0X00, 0X00, 0X00, 0X3E, 0X00, 0X00, 0X01, 0XFF, 0X00, 0X00, 0X00,
|
||||
0X00, 0X03, 0XE0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X3E, 0X00, 0X00, 0X01,
|
||||
0XF8, 0X00, 0X00, 0X00, 0X00, 0X00, 0X03, 0XE0, 0X00, 0X00, 0X00, 0X00,
|
||||
0X3E, 0X00, 0X00, 0X01, 0XF0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X03, 0XE0,
|
||||
0X00, 0X00, 0X00, 0X00, 0X3E, 0X00, 0X00, 0X01, 0XF0, 0X00, 0X00, 0X00,
|
||||
0X00, 0X00, 0X03, 0XE0, 0X1F, 0XFF, 0X00, 0XFE, 0X3E, 0X07, 0XFF, 0XC1,
|
||||
0XFF, 0X1F, 0X0E, 0X7C, 0X03, 0XE3, 0XE3, 0XFF, 0X3F, 0XFF, 0X81, 0XFF,
|
||||
0XBE, 0X0F, 0XFF, 0XE1, 0XFF, 0X1F, 0X3E, 0X7C, 0X03, 0XE3, 0XE3, 0XFF,
|
||||
0X7F, 0XFF, 0XC3, 0XFF, 0XFE, 0X1F, 0XFF, 0XF1, 0XFF, 0X1F, 0X7E, 0X7C,
|
||||
0X03, 0XE3, 0XE3, 0XFF, 0X7F, 0XFF, 0XC7, 0XFF, 0XFE, 0X1F, 0XFF, 0XF1,
|
||||
0XFF, 0X1F, 0XFE, 0X7C, 0X03, 0XE3, 0XE3, 0XFF, 0X7E, 0X0F, 0XC7, 0XFF,
|
||||
0XFE, 0X1F, 0X83, 0XF1, 0XFF, 0X1F, 0XFE, 0X7C, 0X03, 0XE3, 0XE3, 0XFF,
|
||||
0X7C, 0X07, 0XC7, 0XE0, 0X3E, 0X1F, 0X01, 0XF1, 0XF0, 0X1F, 0XFE, 0X7C,
|
||||
0X03, 0XE3, 0XE3, 0XE0, 0X7C, 0X07, 0XC7, 0XE0, 0X3E, 0X1F, 0X01, 0XF1,
|
||||
0XF0, 0X1F, 0X80, 0X7C, 0X03, 0XE3, 0XE3, 0XE0, 0X00, 0X07, 0XC7, 0XC0,
|
||||
0X3E, 0X00, 0X01, 0XF1, 0XF0, 0X1F, 0X00, 0X7C, 0X03, 0XE3, 0XE3, 0XE0,
|
||||
0X00, 0X07, 0XC7, 0XC0, 0X3E, 0X00, 0X01, 0XF1, 0XF0, 0X1F, 0X00, 0X7C,
|
||||
0X03, 0XE3, 0XE3, 0XE0, 0X3F, 0XFF, 0XC7, 0XC0, 0X3E, 0X0F, 0XFF, 0XF1,
|
||||
0XF0, 0X1F, 0X00, 0X7C, 0X03, 0XE3, 0XE3, 0XE0, 0X7F, 0XFF, 0XC7, 0XC0,
|
||||
0X3E, 0X1F, 0XFF, 0XF1, 0XF0, 0X1F, 0X00, 0X7C, 0X03, 0XE3, 0XE3, 0XE0,
|
||||
0XFF, 0XFF, 0XC7, 0XC0, 0X3E, 0X3F, 0XFF, 0XF1, 0XF0, 0X1F, 0X00, 0X7C,
|
||||
0X03, 0XE3, 0XE3, 0XE0, 0XFC, 0X07, 0XC7, 0XC0, 0X3E, 0X3F, 0X01, 0XF1,
|
||||
0XF0, 0X1F, 0X00, 0X7C, 0X03, 0XE3, 0XE3, 0XE0, 0XF8, 0X07, 0XC7, 0XC0,
|
||||
0X3E, 0X3E, 0X01, 0XF1, 0XF0, 0X1F, 0X00, 0X7C, 0X03, 0XE3, 0XE3, 0XE0,
|
||||
0XF8, 0X07, 0XC7, 0XE0, 0X3E, 0X3E, 0X01, 0XF1, 0XF0, 0X1F, 0X00, 0X7C,
|
||||
0X03, 0XE3, 0XE3, 0XE0, 0XF8, 0X07, 0XC7, 0XE0, 0X7E, 0X3E, 0X01, 0XF1,
|
||||
0XF0, 0X1F, 0X00, 0X7E, 0X03, 0XE3, 0XE3, 0XE0, 0XFC, 0X1F, 0XC7, 0XFF,
|
||||
0XFE, 0X3F, 0X07, 0XF1, 0XF0, 0X1F, 0X00, 0X7F, 0XFF, 0XE3, 0XE3, 0XFF,
|
||||
0XFF, 0XFF, 0XC7, 0XFF, 0XFE, 0X3F, 0XFF, 0XF1, 0XF0, 0X1F, 0X00, 0X7F,
|
||||
0XFF, 0XE3, 0XE3, 0XFF, 0XFF, 0XFF, 0XC3, 0XFF, 0XBE, 0X3F, 0XFF, 0XF1,
|
||||
0XF0, 0X1F, 0X00, 0X7F, 0XFF, 0XE3, 0XE3, 0XFF, 0X7F, 0XE7, 0XC3, 0XFF,
|
||||
0X3E, 0X1F, 0XF9, 0XF1, 0XF0, 0X1F, 0X00, 0X3F, 0XE3, 0XE3, 0XE1, 0XFF,
|
||||
0X1F, 0X87, 0XC0, 0XFC, 0X3E, 0X07, 0XE1, 0XF1, 0XF0, 0X1F, 0X00, 0X0F,
|
||||
0XC1, 0XE3, 0XE0, 0XFC, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00,
|
||||
0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00,
|
||||
0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00,
|
||||
0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00,
|
||||
0X00, 0X00, 0X00, 0X00, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF,
|
||||
0XB7, 0X63, 0XDD, 0XC6, 0X08, 0X76, 0X1C, 0X7F, 0XFF, 0XFF, 0XFF, 0XFF,
|
||||
0XFF, 0XFF, 0XFF, 0XFF, 0XB3, 0X6D, 0XDD, 0XBB, 0XBB, 0XB6, 0XFB, 0XBF,
|
||||
0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XB3, 0X6E, 0XDD, 0XBF,
|
||||
0XBB, 0XB6, 0XFB, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF,
|
||||
0XB5, 0X6E, 0XDD, 0XC7, 0XB8, 0X76, 0X3C, 0X7F, 0XFF, 0XFF, 0XFF, 0XFF,
|
||||
0XFF, 0XFF, 0XFF, 0XFF, 0XB5, 0X6E, 0XDD, 0XFB, 0XBB, 0XB6, 0XFF, 0XBF,
|
||||
0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XB6, 0X6D, 0XEB, 0XBB,
|
||||
0XBB, 0XB6, 0XFB, 0XBF };
|
||||
7609
libraries/TFT_eSPI/examples/Generic/Animated_Eyes_2/data/naugaEye.h
Normal file
7609
libraries/TFT_eSPI/examples/Generic/Animated_Eyes_2/data/naugaEye.h
Normal file
File diff suppressed because it is too large
Load diff
13346
libraries/TFT_eSPI/examples/Generic/Animated_Eyes_2/data/newtEye.h
Normal file
13346
libraries/TFT_eSPI/examples/Generic/Animated_Eyes_2/data/newtEye.h
Normal file
File diff suppressed because it is too large
Load diff
17018
libraries/TFT_eSPI/examples/Generic/Animated_Eyes_2/data/noScleraEye.h
Normal file
17018
libraries/TFT_eSPI/examples/Generic/Animated_Eyes_2/data/noScleraEye.h
Normal file
File diff suppressed because it is too large
Load diff
7609
libraries/TFT_eSPI/examples/Generic/Animated_Eyes_2/data/owlEye.h
Normal file
7609
libraries/TFT_eSPI/examples/Generic/Animated_Eyes_2/data/owlEye.h
Normal file
File diff suppressed because it is too large
Load diff
13346
libraries/TFT_eSPI/examples/Generic/Animated_Eyes_2/data/terminatorEye.h
Normal file
13346
libraries/TFT_eSPI/examples/Generic/Animated_Eyes_2/data/terminatorEye.h
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,429 @@
|
|||
//
|
||||
// Code adapted by Bodmer as an example for TFT_eSPI, this runs on any
|
||||
// TFT_eSPI compatible processor so ignore the technical limitations
|
||||
// detailed in the original header below. Assorted changes have been
|
||||
// made including removal of the display mirror kludge.
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Uncanny eyes for Adafruit 1.5" OLED (product #1431) or 1.44" TFT LCD
|
||||
// (#2088). Works on PJRC Teensy 3.x and on Adafruit M0 and M4 boards
|
||||
// (Feather, Metro, etc.). This code uses features specific to these
|
||||
// boards and WILL NOT work on normal Arduino or other boards!
|
||||
//
|
||||
// SEE FILE "config.h" FOR MOST CONFIGURATION (graphics, pins, display type,
|
||||
// etc.). Probably won't need to edit THIS file unless you're doing some
|
||||
// extremely custom modifications.
|
||||
//
|
||||
// Adafruit invests time and resources providing this open source code,
|
||||
// please support Adafruit and open-source hardware by purchasing products
|
||||
// from Adafruit!
|
||||
//
|
||||
// Written by Phil Burgess / Paint Your Dragon for Adafruit Industries.
|
||||
// MIT license. SPI FIFO insight from Paul Stoffregen's ILI9341_t3 library.
|
||||
// Inspired by David Boccabella's (Marcwolf) hybrid servo/OLED eye concept.
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
#if !defined(LIGHT_PIN) || (LIGHT_PIN < 0)
|
||||
// Autonomous iris motion uses a fractal behavior to similate both the major
|
||||
// reaction of the eye plus the continuous smaller adjustments that occur.
|
||||
uint16_t oldIris = (IRIS_MIN + IRIS_MAX) / 2, newIris;
|
||||
#endif
|
||||
|
||||
// Initialise eyes ---------------------------------------------------------
|
||||
void initEyes(void)
|
||||
{
|
||||
Serial.println("Initialise eye objects");
|
||||
|
||||
// Initialise eye objects based on eyeInfo list in config.h:
|
||||
for (uint8_t e = 0; e < NUM_EYES; e++) {
|
||||
Serial.print("Create display #"); Serial.println(e);
|
||||
|
||||
eye[e].tft_cs = eyeInfo[e].select;
|
||||
eye[e].blink.state = NOBLINK;
|
||||
eye[e].xposition = eyeInfo[e].xposition;
|
||||
|
||||
pinMode(eye[e].tft_cs, OUTPUT);
|
||||
digitalWrite(eye[e].tft_cs, LOW);
|
||||
|
||||
// Also set up an individual eye-wink pin if defined:
|
||||
if (eyeInfo[e].wink >= 0) pinMode(eyeInfo[e].wink, INPUT_PULLUP);
|
||||
}
|
||||
|
||||
#if defined(BLINK_PIN) && (BLINK_PIN >= 0)
|
||||
pinMode(BLINK_PIN, INPUT_PULLUP); // Ditto for all-eyes blink pin
|
||||
#endif
|
||||
}
|
||||
|
||||
// UPDATE EYE --------------------------------------------------------------
|
||||
void updateEye (void)
|
||||
{
|
||||
#if defined(LIGHT_PIN) && (LIGHT_PIN >= 0) // Interactive iris
|
||||
|
||||
int16_t v = analogRead(LIGHT_PIN); // Raw dial/photocell reading
|
||||
#ifdef LIGHT_PIN_FLIP
|
||||
v = 1023 - v; // Reverse reading from sensor
|
||||
#endif
|
||||
if (v < LIGHT_MIN) v = LIGHT_MIN; // Clamp light sensor range
|
||||
else if (v > LIGHT_MAX) v = LIGHT_MAX;
|
||||
v -= LIGHT_MIN; // 0 to (LIGHT_MAX - LIGHT_MIN)
|
||||
#ifdef LIGHT_CURVE // Apply gamma curve to sensor input?
|
||||
v = (int16_t)(pow((double)v / (double)(LIGHT_MAX - LIGHT_MIN),
|
||||
LIGHT_CURVE) * (double)(LIGHT_MAX - LIGHT_MIN));
|
||||
#endif
|
||||
// And scale to iris range (IRIS_MAX is size at LIGHT_MIN)
|
||||
v = map(v, 0, (LIGHT_MAX - LIGHT_MIN), IRIS_MAX, IRIS_MIN);
|
||||
#ifdef IRIS_SMOOTH // Filter input (gradual motion)
|
||||
static int16_t irisValue = (IRIS_MIN + IRIS_MAX) / 2;
|
||||
irisValue = ((irisValue * 15) + v) / 16;
|
||||
frame(irisValue);
|
||||
#else // Unfiltered (immediate motion)
|
||||
frame(v);
|
||||
#endif // IRIS_SMOOTH
|
||||
|
||||
#else // Autonomous iris scaling -- invoke recursive function
|
||||
|
||||
newIris = random(IRIS_MIN, IRIS_MAX);
|
||||
split(oldIris, newIris, micros(), 10000000L, IRIS_MAX - IRIS_MIN);
|
||||
oldIris = newIris;
|
||||
|
||||
#endif // LIGHT_PIN
|
||||
}
|
||||
|
||||
// EYE-RENDERING FUNCTION --------------------------------------------------
|
||||
void drawEye( // Renders one eye. Inputs must be pre-clipped & valid.
|
||||
// Use native 32-bit variables where possible as this is 10% faster!
|
||||
uint8_t e, // Eye array index; 0 or 1 for left/right
|
||||
uint32_t iScale, // Scale factor for iris
|
||||
uint32_t scleraX, // First pixel X offset into sclera image
|
||||
uint32_t scleraY, // First pixel Y offset into sclera image
|
||||
uint32_t uT, // Upper eyelid threshold value
|
||||
uint32_t lT) { // Lower eyelid threshold value
|
||||
|
||||
uint32_t screenX, screenY, scleraXsave;
|
||||
int32_t irisX, irisY;
|
||||
uint32_t p, a;
|
||||
uint32_t d;
|
||||
|
||||
uint32_t pixels = 0;
|
||||
|
||||
// Set up raw pixel dump to entire screen. Although such writes can wrap
|
||||
// around automatically from end of rect back to beginning, the region is
|
||||
// reset on each frame here in case of an SPI glitch.
|
||||
digitalWrite(eye[e].tft_cs, LOW);
|
||||
tft.startWrite();
|
||||
tft.setAddrWindow(eye[e].xposition, 0, 128, 128);
|
||||
|
||||
// Now just issue raw 16-bit values for every pixel...
|
||||
|
||||
scleraXsave = scleraX; // Save initial X value to reset on each line
|
||||
irisY = scleraY - (SCLERA_HEIGHT - IRIS_HEIGHT) / 2;
|
||||
|
||||
// Eyelid image is left<>right swapped for two displays
|
||||
uint16_t lidX = 0;
|
||||
uint16_t dlidX = -1;
|
||||
if (e) dlidX = 1;
|
||||
for (screenY = 0; screenY < SCREEN_HEIGHT; screenY++, scleraY++, irisY++) {
|
||||
scleraX = scleraXsave;
|
||||
irisX = scleraXsave - (SCLERA_WIDTH - IRIS_WIDTH) / 2;
|
||||
if (e) lidX = 0; else lidX = SCREEN_WIDTH - 1;
|
||||
for (screenX = 0; screenX < SCREEN_WIDTH; screenX++, scleraX++, irisX++, lidX += dlidX) {
|
||||
if ((pgm_read_byte(lower + screenY * SCREEN_WIDTH + lidX) <= lT) ||
|
||||
(pgm_read_byte(upper + screenY * SCREEN_WIDTH + lidX) <= uT)) { // Covered by eyelid
|
||||
p = 0;
|
||||
} else if ((irisY < 0) || (irisY >= IRIS_HEIGHT) ||
|
||||
(irisX < 0) || (irisX >= IRIS_WIDTH)) { // In sclera
|
||||
p = pgm_read_word(sclera + scleraY * SCLERA_WIDTH + scleraX);
|
||||
} else { // Maybe iris...
|
||||
p = pgm_read_word(polar + irisY * IRIS_WIDTH + irisX); // Polar angle/dist
|
||||
d = (iScale * (p & 0x7F)) / 128; // Distance (Y)
|
||||
if (d < IRIS_MAP_HEIGHT) { // Within iris area
|
||||
a = (IRIS_MAP_WIDTH * (p >> 7)) / 512; // Angle (X)
|
||||
p = pgm_read_word(iris + d * IRIS_MAP_WIDTH + a); // Pixel = iris
|
||||
} else { // Not in iris
|
||||
p = pgm_read_word(sclera + scleraY * SCLERA_WIDTH + scleraX); // Pixel = sclera
|
||||
}
|
||||
}
|
||||
*(&pbuffer[dmaBuf][0] + pixels++) = p >> 8 | p << 8;
|
||||
|
||||
if (pixels >= BUFFER_SIZE) {
|
||||
yield();
|
||||
#ifdef USE_DMA
|
||||
tft.pushPixelsDMA(&pbuffer[dmaBuf][0], pixels);
|
||||
dmaBuf = !dmaBuf;
|
||||
#else
|
||||
tft.pushPixels(pbuffer, pixels);
|
||||
#endif
|
||||
pixels = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pixels) {
|
||||
#ifdef USE_DMA
|
||||
tft.pushPixelsDMA(&pbuffer[dmaBuf][0], pixels);
|
||||
#else
|
||||
tft.pushPixels(pbuffer, pixels);
|
||||
#endif
|
||||
}
|
||||
tft.endWrite();
|
||||
digitalWrite(eye[e].tft_cs, HIGH);
|
||||
}
|
||||
|
||||
// EYE ANIMATION -----------------------------------------------------------
|
||||
|
||||
const uint8_t ease[] = { // Ease in/out curve for eye movements 3*t^2-2*t^3
|
||||
0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 3, // T
|
||||
3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 7, 8, 9, 9, 10, 10, // h
|
||||
11, 12, 12, 13, 14, 15, 15, 16, 17, 18, 18, 19, 20, 21, 22, 23, // x
|
||||
24, 25, 26, 27, 27, 28, 29, 30, 31, 33, 34, 35, 36, 37, 38, 39, // 2
|
||||
40, 41, 42, 44, 45, 46, 47, 48, 50, 51, 52, 53, 54, 56, 57, 58, // A
|
||||
60, 61, 62, 63, 65, 66, 67, 69, 70, 72, 73, 74, 76, 77, 78, 80, // l
|
||||
81, 83, 84, 85, 87, 88, 90, 91, 93, 94, 96, 97, 98, 100, 101, 103, // e
|
||||
104, 106, 107, 109, 110, 112, 113, 115, 116, 118, 119, 121, 122, 124, 125, 127, // c
|
||||
128, 130, 131, 133, 134, 136, 137, 139, 140, 142, 143, 145, 146, 148, 149, 151, // J
|
||||
152, 154, 155, 157, 158, 159, 161, 162, 164, 165, 167, 168, 170, 171, 172, 174, // a
|
||||
175, 177, 178, 179, 181, 182, 183, 185, 186, 188, 189, 190, 192, 193, 194, 195, // c
|
||||
197, 198, 199, 201, 202, 203, 204, 205, 207, 208, 209, 210, 211, 213, 214, 215, // o
|
||||
216, 217, 218, 219, 220, 221, 222, 224, 225, 226, 227, 228, 228, 229, 230, 231, // b
|
||||
232, 233, 234, 235, 236, 237, 237, 238, 239, 240, 240, 241, 242, 243, 243, 244, // s
|
||||
245, 245, 246, 246, 247, 248, 248, 249, 249, 250, 250, 251, 251, 251, 252, 252, // o
|
||||
252, 253, 253, 253, 254, 254, 254, 254, 254, 255, 255, 255, 255, 255, 255, 255
|
||||
}; // n
|
||||
|
||||
#ifdef AUTOBLINK
|
||||
uint32_t timeOfLastBlink = 0L, timeToNextBlink = 0L;
|
||||
#endif
|
||||
|
||||
// Process motion for a single frame of left or right eye
|
||||
void frame(uint16_t iScale) // Iris scale (0-1023)
|
||||
{
|
||||
static uint32_t frames = 0; // Used in frame rate calculation
|
||||
static uint8_t eyeIndex = 0; // eye[] array counter
|
||||
int16_t eyeX, eyeY;
|
||||
uint32_t t = micros(); // Time at start of function
|
||||
|
||||
if (!(++frames & 255)) { // Every 256 frames...
|
||||
float elapsed = (millis() - startTime) / 1000.0;
|
||||
if (elapsed) Serial.println((uint16_t)(frames / elapsed)); // Print FPS
|
||||
}
|
||||
|
||||
if (++eyeIndex >= NUM_EYES) eyeIndex = 0; // Cycle through eyes, 1 per call
|
||||
|
||||
// X/Y movement
|
||||
|
||||
#if defined(JOYSTICK_X_PIN) && (JOYSTICK_X_PIN >= 0) && \
|
||||
defined(JOYSTICK_Y_PIN) && (JOYSTICK_Y_PIN >= 0)
|
||||
|
||||
// Read X/Y from joystick, constrain to circle
|
||||
int16_t dx, dy;
|
||||
int32_t d;
|
||||
eyeX = analogRead(JOYSTICK_X_PIN); // Raw (unclipped) X/Y reading
|
||||
eyeY = analogRead(JOYSTICK_Y_PIN);
|
||||
#ifdef JOYSTICK_X_FLIP
|
||||
eyeX = 1023 - eyeX;
|
||||
#endif
|
||||
#ifdef JOYSTICK_Y_FLIP
|
||||
eyeY = 1023 - eyeY;
|
||||
#endif
|
||||
dx = (eyeX * 2) - 1023; // A/D exact center is at 511.5. Scale coords
|
||||
dy = (eyeY * 2) - 1023; // X2 so range is -1023 to +1023 w/center at 0.
|
||||
if ((d = (dx * dx + dy * dy)) > (1023 * 1023)) { // Outside circle
|
||||
d = (int32_t)sqrt((float)d); // Distance from center
|
||||
eyeX = ((dx * 1023 / d) + 1023) / 2; // Clip to circle edge,
|
||||
eyeY = ((dy * 1023 / d) + 1023) / 2; // scale back to 0-1023
|
||||
}
|
||||
|
||||
#else // Autonomous X/Y eye motion
|
||||
// Periodically initiates motion to a new random point, random speed,
|
||||
// holds there for random period until next motion.
|
||||
|
||||
static bool eyeInMotion = false;
|
||||
static int16_t eyeOldX = 512, eyeOldY = 512, eyeNewX = 512, eyeNewY = 512;
|
||||
static uint32_t eyeMoveStartTime = 0L;
|
||||
static int32_t eyeMoveDuration = 0L;
|
||||
|
||||
int32_t dt = t - eyeMoveStartTime; // uS elapsed since last eye event
|
||||
if (eyeInMotion) { // Currently moving?
|
||||
if (dt >= eyeMoveDuration) { // Time up? Destination reached.
|
||||
eyeInMotion = false; // Stop moving
|
||||
eyeMoveDuration = random(3000000); // 0-3 sec stop
|
||||
eyeMoveStartTime = t; // Save initial time of stop
|
||||
eyeX = eyeOldX = eyeNewX; // Save position
|
||||
eyeY = eyeOldY = eyeNewY;
|
||||
} else { // Move time's not yet fully elapsed -- interpolate position
|
||||
int16_t e = ease[255 * dt / eyeMoveDuration] + 1; // Ease curve
|
||||
eyeX = eyeOldX + (((eyeNewX - eyeOldX) * e) / 256); // Interp X
|
||||
eyeY = eyeOldY + (((eyeNewY - eyeOldY) * e) / 256); // and Y
|
||||
}
|
||||
} else { // Eye stopped
|
||||
eyeX = eyeOldX;
|
||||
eyeY = eyeOldY;
|
||||
if (dt > eyeMoveDuration) { // Time up? Begin new move.
|
||||
int16_t dx, dy;
|
||||
uint32_t d;
|
||||
do { // Pick new dest in circle
|
||||
eyeNewX = random(1024);
|
||||
eyeNewY = random(1024);
|
||||
dx = (eyeNewX * 2) - 1023;
|
||||
dy = (eyeNewY * 2) - 1023;
|
||||
} while ((d = (dx * dx + dy * dy)) > (1023 * 1023)); // Keep trying
|
||||
eyeMoveDuration = random(72000, 144000); // ~1/14 - ~1/7 sec
|
||||
eyeMoveStartTime = t; // Save initial time of move
|
||||
eyeInMotion = true; // Start move on next frame
|
||||
}
|
||||
}
|
||||
#endif // JOYSTICK_X_PIN etc.
|
||||
|
||||
// Blinking
|
||||
#ifdef AUTOBLINK
|
||||
// Similar to the autonomous eye movement above -- blink start times
|
||||
// and durations are random (within ranges).
|
||||
if ((t - timeOfLastBlink) >= timeToNextBlink) { // Start new blink?
|
||||
timeOfLastBlink = t;
|
||||
uint32_t blinkDuration = random(36000, 72000); // ~1/28 - ~1/14 sec
|
||||
// Set up durations for both eyes (if not already winking)
|
||||
for (uint8_t e = 0; e < NUM_EYES; e++) {
|
||||
if (eye[e].blink.state == NOBLINK) {
|
||||
eye[e].blink.state = ENBLINK;
|
||||
eye[e].blink.startTime = t;
|
||||
eye[e].blink.duration = blinkDuration;
|
||||
}
|
||||
}
|
||||
timeToNextBlink = blinkDuration * 3 + random(4000000);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (eye[eyeIndex].blink.state) { // Eye currently blinking?
|
||||
// Check if current blink state time has elapsed
|
||||
if ((t - eye[eyeIndex].blink.startTime) >= eye[eyeIndex].blink.duration) {
|
||||
// Yes -- increment blink state, unless...
|
||||
if ((eye[eyeIndex].blink.state == ENBLINK) && ( // Enblinking and...
|
||||
#if defined(BLINK_PIN) && (BLINK_PIN >= 0)
|
||||
(digitalRead(BLINK_PIN) == LOW) || // blink or wink held...
|
||||
#endif
|
||||
((eyeInfo[eyeIndex].wink >= 0) &&
|
||||
digitalRead(eyeInfo[eyeIndex].wink) == LOW) )) {
|
||||
// Don't advance state yet -- eye is held closed instead
|
||||
} else { // No buttons, or other state...
|
||||
if (++eye[eyeIndex].blink.state > DEBLINK) { // Deblinking finished?
|
||||
eye[eyeIndex].blink.state = NOBLINK; // No longer blinking
|
||||
} else { // Advancing from ENBLINK to DEBLINK mode
|
||||
eye[eyeIndex].blink.duration *= 2; // DEBLINK is 1/2 ENBLINK speed
|
||||
eye[eyeIndex].blink.startTime = t;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else { // Not currently blinking...check buttons!
|
||||
#if defined(BLINK_PIN) && (BLINK_PIN >= 0)
|
||||
if (digitalRead(BLINK_PIN) == LOW) {
|
||||
// Manually-initiated blinks have random durations like auto-blink
|
||||
uint32_t blinkDuration = random(36000, 72000);
|
||||
for (uint8_t e = 0; e < NUM_EYES; e++) {
|
||||
if (eye[e].blink.state == NOBLINK) {
|
||||
eye[e].blink.state = ENBLINK;
|
||||
eye[e].blink.startTime = t;
|
||||
eye[e].blink.duration = blinkDuration;
|
||||
}
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
if ((eyeInfo[eyeIndex].wink >= 0) &&
|
||||
(digitalRead(eyeInfo[eyeIndex].wink) == LOW)) { // Wink!
|
||||
eye[eyeIndex].blink.state = ENBLINK;
|
||||
eye[eyeIndex].blink.startTime = t;
|
||||
eye[eyeIndex].blink.duration = random(45000, 90000);
|
||||
}
|
||||
}
|
||||
|
||||
// Process motion, blinking and iris scale into renderable values
|
||||
|
||||
// Scale eye X/Y positions (0-1023) to pixel units used by drawEye()
|
||||
eyeX = map(eyeX, 0, 1023, 0, SCLERA_WIDTH - 128);
|
||||
eyeY = map(eyeY, 0, 1023, 0, SCLERA_HEIGHT - 128);
|
||||
|
||||
// Horizontal position is offset so that eyes are very slightly crossed
|
||||
// to appear fixated (converged) at a conversational distance. Number
|
||||
// here was extracted from my posterior and not mathematically based.
|
||||
// I suppose one could get all clever with a range sensor, but for now...
|
||||
if (NUM_EYES > 1) {
|
||||
if (eyeIndex == 1) eyeX += 4;
|
||||
else eyeX -= 4;
|
||||
}
|
||||
if (eyeX > (SCLERA_WIDTH - 128)) eyeX = (SCLERA_WIDTH - 128);
|
||||
|
||||
// Eyelids are rendered using a brightness threshold image. This same
|
||||
// map can be used to simplify another problem: making the upper eyelid
|
||||
// track the pupil (eyes tend to open only as much as needed -- e.g. look
|
||||
// down and the upper eyelid drops). Just sample a point in the upper
|
||||
// lid map slightly above the pupil to determine the rendering threshold.
|
||||
static uint8_t uThreshold = 128;
|
||||
uint8_t lThreshold, n;
|
||||
#ifdef TRACKING
|
||||
int16_t sampleX = SCLERA_WIDTH / 2 - (eyeX / 2), // Reduce X influence
|
||||
sampleY = SCLERA_HEIGHT / 2 - (eyeY + IRIS_HEIGHT / 4);
|
||||
// Eyelid is slightly asymmetrical, so two readings are taken, averaged
|
||||
if (sampleY < 0) n = 0;
|
||||
else n = (pgm_read_byte(upper + sampleY * SCREEN_WIDTH + sampleX) +
|
||||
pgm_read_byte(upper + sampleY * SCREEN_WIDTH + (SCREEN_WIDTH - 1 - sampleX))) / 2;
|
||||
uThreshold = (uThreshold * 3 + n) / 4; // Filter/soften motion
|
||||
// Lower eyelid doesn't track the same way, but seems to be pulled upward
|
||||
// by tension from the upper lid.
|
||||
lThreshold = 254 - uThreshold;
|
||||
#else // No tracking -- eyelids full open unless blink modifies them
|
||||
uThreshold = lThreshold = 0;
|
||||
#endif
|
||||
|
||||
// The upper/lower thresholds are then scaled relative to the current
|
||||
// blink position so that blinks work together with pupil tracking.
|
||||
if (eye[eyeIndex].blink.state) { // Eye currently blinking?
|
||||
uint32_t s = (t - eye[eyeIndex].blink.startTime);
|
||||
if (s >= eye[eyeIndex].blink.duration) s = 255; // At or past blink end
|
||||
else s = 255 * s / eye[eyeIndex].blink.duration; // Mid-blink
|
||||
s = (eye[eyeIndex].blink.state == DEBLINK) ? 1 + s : 256 - s;
|
||||
n = (uThreshold * s + 254 * (257 - s)) / 256;
|
||||
lThreshold = (lThreshold * s + 254 * (257 - s)) / 256;
|
||||
} else {
|
||||
n = uThreshold;
|
||||
}
|
||||
|
||||
// Pass all the derived values to the eye-rendering function:
|
||||
drawEye(eyeIndex, iScale, eyeX, eyeY, n, lThreshold);
|
||||
|
||||
if (eyeIndex == (NUM_EYES - 1)) {
|
||||
user_loop(); // Call user code after rendering last eye
|
||||
}
|
||||
}
|
||||
|
||||
// AUTONOMOUS IRIS SCALING (if no photocell or dial) -----------------------
|
||||
|
||||
#if !defined(LIGHT_PIN) || (LIGHT_PIN < 0)
|
||||
|
||||
// Autonomous iris motion uses a fractal behavior to similate both the major
|
||||
// reaction of the eye plus the continuous smaller adjustments that occur.
|
||||
|
||||
void split( // Subdivides motion path into two sub-paths w/randimization
|
||||
int16_t startValue, // Iris scale value (IRIS_MIN to IRIS_MAX) at start
|
||||
int16_t endValue, // Iris scale value at end
|
||||
uint32_t startTime, // micros() at start
|
||||
int32_t duration, // Start-to-end time, in microseconds
|
||||
int16_t range) { // Allowable scale value variance when subdividing
|
||||
|
||||
if (range >= 8) { // Limit subdvision count, because recursion
|
||||
range /= 2; // Split range & time in half for subdivision,
|
||||
duration /= 2; // then pick random center point within range:
|
||||
int16_t midValue = (startValue + endValue - range) / 2 + random(range);
|
||||
uint32_t midTime = startTime + duration;
|
||||
split(startValue, midValue, startTime, duration, range); // First half
|
||||
split(midValue , endValue, midTime , duration, range); // Second half
|
||||
} else { // No more subdivisons, do iris motion...
|
||||
int32_t dt; // Time (micros) since start of motion
|
||||
int16_t v; // Interim value
|
||||
while ((dt = (micros() - startTime)) < duration) {
|
||||
v = startValue + (((endValue - startValue) * dt) / duration);
|
||||
if (v < IRIS_MIN) v = IRIS_MIN; // Clip just in case
|
||||
else if (v > IRIS_MAX) v = IRIS_MAX;
|
||||
frame(v); // Draw frame w/interim iris scale value
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // !LIGHT_PIN
|
||||
65
libraries/TFT_eSPI/examples/Generic/Animated_Eyes_2/user.cpp
Normal file
65
libraries/TFT_eSPI/examples/Generic/Animated_Eyes_2/user.cpp
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
#if 1 // Change to 0 to disable this code (must enable ONE user*.cpp only!)
|
||||
|
||||
// This file provides a crude way to "drop in" user code to the eyes,
|
||||
// allowing concurrent operations without having to maintain a bunch of
|
||||
// special derivatives of the eye code (which is still undergoing a lot
|
||||
// of development). Just replace the source code contents of THIS TAB ONLY,
|
||||
// compile and upload to board. Shouldn't need to modify other eye code.
|
||||
|
||||
// User globals can go here, recommend declaring as static, e.g.:
|
||||
// static int foo = 42;
|
||||
|
||||
// Called once near the end of the setup() function.
|
||||
void user_setup(void) {
|
||||
}
|
||||
|
||||
// Called periodically during eye animation. This is invoked in the
|
||||
// interval before starting drawing on the last eye so it won't exacerbate
|
||||
// visible tearing in eye rendering.
|
||||
// This function BLOCKS, it does NOT multitask with the eye animation code,
|
||||
// and performance here will have a direct impact on overall refresh rates,
|
||||
// so keep it simple. Avoid loops (e.g. if animating something like a servo
|
||||
// or NeoPixels in response to some trigger) and instead rely on state
|
||||
// machines or similar. Additionally, calls to this function are NOT time-
|
||||
// constant -- eye rendering time can vary frame to frame, so animation or
|
||||
// other over-time operations won't look very good using simple +/-
|
||||
// increments, it's better to use millis() or micros() and work
|
||||
// algebraically with elapsed times instead.
|
||||
void user_loop(void) {
|
||||
/*
|
||||
Suppose we have a global bool "animating" (meaning something is in
|
||||
motion) and global uint32_t's "startTime" (the initial time at which
|
||||
something triggered movement) and "transitionTime" (the total time
|
||||
over which movement should occur, expressed in microseconds).
|
||||
Maybe it's servos, maybe NeoPixels, or something different altogether.
|
||||
This function might resemble something like (pseudocode):
|
||||
|
||||
if(!animating) {
|
||||
Not in motion, check sensor for trigger...
|
||||
if(read some sensor) {
|
||||
Motion is triggered! Record startTime, set transition
|
||||
to 1.5 seconds and set animating flag:
|
||||
startTime = micros();
|
||||
transitionTime = 1500000;
|
||||
animating = true;
|
||||
No motion actually takes place yet, that will begin on
|
||||
the next pass through this function.
|
||||
}
|
||||
} else {
|
||||
Currently in motion, ignore trigger and move things instead...
|
||||
uint32_t elapsed = millis() - startTime;
|
||||
if(elapsed < transitionTime) {
|
||||
Part way through motion...how far along?
|
||||
float ratio = (float)elapsed / (float)transitionTime;
|
||||
Do something here based on ratio, 0.0 = start, 1.0 = end
|
||||
} else {
|
||||
End of motion reached.
|
||||
Take whatever steps here to move into final position (1.0),
|
||||
and then clear the "animating" flag:
|
||||
animating = false;
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
#endif // 0
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
// SERVO BAT: flapping paper-cutout bat (attached to servo on SERVO_PIN)
|
||||
// triggered by contact-sensitive conductive thread on CAPTOUCH_PIN.
|
||||
// See user.cpp for basics of connecting user code to animated eyes.
|
||||
|
||||
#if 0 // Change to 1 to enable this code (must enable ONE user*.cpp only!)
|
||||
|
||||
#include "Adafruit_FreeTouch.h"
|
||||
#include <Servo.h>
|
||||
|
||||
#define CAPTOUCH_PIN A5 // Capacitive touch pin - attach conductive thread here
|
||||
#define SERVO_PIN 4 // Servo plugged in here
|
||||
|
||||
// Set up capacitive touch button using the FreeTouch library
|
||||
static Adafruit_FreeTouch touch(CAPTOUCH_PIN, OVERSAMPLE_4, RESISTOR_50K, FREQ_MODE_NONE);
|
||||
static long oldState; // Last-read touch value
|
||||
static bool isTouched = false; // When true, bat is flapping
|
||||
static uint32_t touchTime = 0; // millis() time when flapping started
|
||||
static uint32_t touchThreshold;
|
||||
|
||||
Servo servo;
|
||||
|
||||
void user_setup(void) {
|
||||
if (!touch.begin())
|
||||
Serial.println("Cap touch init failed");
|
||||
servo.attach(SERVO_PIN);
|
||||
servo.write(0); // Move servo to idle position
|
||||
servo.detach();
|
||||
|
||||
// Attempt to auto-calibrate the touch threshold
|
||||
// (assumes thread is NOT touched on startup!)
|
||||
touchThreshold = 0;
|
||||
for(int i=0; i<10; i++) {
|
||||
touchThreshold += touch.measure(); // Accumulate 10 readings
|
||||
delay(50);
|
||||
}
|
||||
touchThreshold /= 10; // Average "not touched" value
|
||||
touchThreshold = ((touchThreshold * 127) + 1023) / 128; // Threshold = ~1% toward max
|
||||
|
||||
oldState = touch.measure();
|
||||
}
|
||||
|
||||
#define FLAP_TIME_RISING 900 // 0-to-180 degree servo sweep time, in milliseconds
|
||||
#define FLAP_TIME_FALLING 1200 // 180-to-0 servo sweep time
|
||||
#define FLAP_REPS 3 // Number of times to flap
|
||||
#define FLAP_TIME_PER (FLAP_TIME_RISING + FLAP_TIME_FALLING)
|
||||
#define FLAP_TIME_TOTAL (FLAP_TIME_PER * FLAP_REPS)
|
||||
|
||||
void user_loop(void) {
|
||||
long newState = touch.measure();
|
||||
Serial.println(newState);
|
||||
|
||||
if (isTouched) {
|
||||
uint32_t elapsed = millis() - touchTime;
|
||||
if (elapsed >= FLAP_TIME_TOTAL) { // After all flaps are completed
|
||||
isTouched = false; // Bat goes idle again
|
||||
servo.write(0);
|
||||
servo.detach();
|
||||
} else {
|
||||
elapsed %= FLAP_TIME_PER; // Time within current flap cycle
|
||||
if (elapsed < FLAP_TIME_RISING) { // Over the course of 0 to FLAP_TIME_RISING...
|
||||
servo.write(elapsed * 180 / FLAP_TIME_RISING); // Move 0 to 180 degrees
|
||||
} else { // Over course of FLAP_TIME_FALLING, return to 0
|
||||
servo.write(180 - ((elapsed - FLAP_TIME_RISING) * 180 / FLAP_TIME_FALLING));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Bat is idle...check for capacitive touch...
|
||||
if (newState > touchThreshold && oldState < touchThreshold) {
|
||||
delay(100); // Short delay to debounce
|
||||
newState = touch.measure(); // Verify whether still touched
|
||||
if (newState > touchThreshold) { // It is!
|
||||
isTouched = true; // Start a new flap session
|
||||
touchTime = millis();
|
||||
servo.attach(SERVO_PIN);
|
||||
servo.write(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
oldState = newState; // Save cap touch state
|
||||
}
|
||||
|
||||
#endif // 0
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
#if 0 // Change to 1 to enable this code (must enable ONE user*.cpp only!)
|
||||
|
||||
// Christmas demo for eye + NeoPixels. Randomly sets pixels in holiday-themed colors.
|
||||
|
||||
#include <Adafruit_NeoPixel.h>
|
||||
|
||||
// Pin 8 is the built-in NeoPixels on Circuit Playground Express & Bluetooth.
|
||||
// With a TFT Gizmo attached, you can use A1 or A2 to easily connect a strand.
|
||||
#define LED_PIN 8
|
||||
#define LED_COUNT 10
|
||||
#define LED_BRIGHTNESS 50 // about 1/5 brightness (max = 255)
|
||||
#define TWINKLE_INTERVAL 333 // Every 333 ms (1/3 second), change a pixel
|
||||
#define LIT_PIXELS (LED_COUNT / 3) // Must be LESS than LED_COUNT/2
|
||||
|
||||
Adafruit_NeoPixel pixels(LED_COUNT, LED_PIN);
|
||||
|
||||
|
||||
uint32_t timeOfLastTwinkle = 0; // Used for timing pixel changes
|
||||
uint8_t litPixel[LIT_PIXELS]; // Indices of which pixels are lit
|
||||
uint8_t pixelIndex = LIT_PIXELS; // Index of currently-changing litPixel
|
||||
|
||||
uint32_t colors[] = { 0xFF0000, 0x00FF00, 0xFFFFFF }; // Red, green, white
|
||||
#define NUM_COLORS (sizeof colors / sizeof colors[0])
|
||||
|
||||
void user_setup(void) {
|
||||
pixels.begin(); // INITIALIZE NeoPixel strip object (REQUIRED)
|
||||
pixels.show(); // Turn OFF all pixels ASAP
|
||||
pixels.setBrightness(LED_BRIGHTNESS);
|
||||
memset(litPixel, 255, sizeof litPixel); // Fill with out-of-range nonsense
|
||||
}
|
||||
|
||||
void user_loop(void) {
|
||||
uint32_t t = millis();
|
||||
|
||||
if((t - timeOfLastTwinkle) >= TWINKLE_INTERVAL) { // Time to update pixels?
|
||||
timeOfLastTwinkle = t;
|
||||
if(++pixelIndex >= LIT_PIXELS) pixelIndex = 0;
|
||||
|
||||
// Pick a NEW pixel that's not currently lit and not adjacent to a lit one.
|
||||
// This just brute-force randomly tries pixels until a valid one is found,
|
||||
// no mathematical cleverness. Should only take a few iterations and won't
|
||||
// significantly slow down the eyes.
|
||||
int newPixel, pixelAfter, pixelBefore;
|
||||
do {
|
||||
newPixel = random(LED_COUNT);
|
||||
pixelAfter = (newPixel + 1) % LED_COUNT;
|
||||
pixelBefore = (newPixel - 1);
|
||||
if(pixelBefore < 0) pixelBefore = LED_COUNT - 1;
|
||||
} while(pixels.getPixelColor(newPixel) ||
|
||||
pixels.getPixelColor(pixelAfter) ||
|
||||
pixels.getPixelColor(pixelBefore));
|
||||
|
||||
// Turn OFF litPixel[pixelIndex]
|
||||
pixels.setPixelColor(litPixel[pixelIndex], 0);
|
||||
// 'newPixel' is the winner. Save in the litPixel[] array for later...
|
||||
litPixel[pixelIndex] = newPixel;
|
||||
// Turn ON newPixel with a random color from the colors[] list.
|
||||
pixels.setPixelColor(newPixel, colors[random(NUM_COLORS)]);
|
||||
|
||||
pixels.show();
|
||||
}
|
||||
}
|
||||
|
||||
#endif // 0
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
This is the example wiring used for the sketch testing.
|
||||
|
||||
You must not define the TFT_CS pin in the TFT_eSPI library if you are
|
||||
using two independant displays. Instead the chip selects (CS) must be
|
||||
defined in the "config.h" tab of this sketch. The sketch can then select
|
||||
the dispay to send graphics to.
|
||||
|
||||
If you are only using one display, then TFT_CS can be defined in the
|
||||
TFT_eSPI library.
|
||||
|
||||
The "Setup47_ST7735.h" file was used for the two TFT test using the wiring
|
||||
as shown below:
|
||||
|
||||
Function ESP32 pin TFT 1 TFT 2
|
||||
MOSI 23 -> SDA -> SDA // The TFT pin may be named DIN
|
||||
MISO 19 // Not connected
|
||||
SCLK 18 -> CLK -> CLK // The TFT pin may be named SCK
|
||||
TFT_DC 2 -> DC -> DC // The TFT pin may be named AO
|
||||
TFT_RST 4 -> RST -> RST
|
||||
CS 1 22 -> CS // Connected to TFT 1 only
|
||||
CS 2 21 -> CS // Connected to TFT 2 only
|
||||
+5V/VIN -> VCC -> VCC
|
||||
0V -> GND -> GND
|
||||
+5V/VIN -> LED -> LED // Some displays do not have a backlight BL/LED pin
|
||||
|
||||
The displays used for testing were 128x128 ST7735 displays, the TFT_eSPI library setup file may need
|
||||
to be changed as these displays come in many configuration variants.
|
||||
|
||||
|
||||
*/
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 24 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 20 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 6.5 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 19 KiB |
|
|
@ -0,0 +1,268 @@
|
|||
// This sketch if for an ESP32, it draws Jpeg images pulled from an SD Card
|
||||
// onto the TFT.
|
||||
|
||||
// As well as the TFT_eSPI library you will need the JPEG Decoder library.
|
||||
// A copy can be downloaded here, it is based on the library by Makoto Kurauchi.
|
||||
// https://github.com/Bodmer/JPEGDecoder
|
||||
|
||||
// Images on SD Card must be put in the root folder (top level) to be found
|
||||
// Use the SD library examples to verify your SD Card interface works!
|
||||
|
||||
// The example images used to test this sketch can be found in the library
|
||||
// JPEGDecoder/extras folder
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
|
||||
#include <SPI.h>
|
||||
|
||||
#include <FS.h>
|
||||
#include <SD.h>
|
||||
|
||||
#include <TFT_eSPI.h>
|
||||
TFT_eSPI tft = TFT_eSPI();
|
||||
|
||||
// JPEG decoder library
|
||||
#include <JPEGDecoder.h>
|
||||
|
||||
//####################################################################################################
|
||||
// Setup
|
||||
//####################################################################################################
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
// Set all chip selects high to avoid bus contention during initialisation of each peripheral
|
||||
digitalWrite(22, HIGH); // Touch controller chip select (if used)
|
||||
digitalWrite(15, HIGH); // TFT screen chip select
|
||||
digitalWrite( 5, HIGH); // SD card chips select, must use GPIO 5 (ESP32 SS)
|
||||
|
||||
tft.begin();
|
||||
|
||||
if (!SD.begin(5, tft.getSPIinstance())) {
|
||||
Serial.println("Card Mount Failed");
|
||||
return;
|
||||
}
|
||||
uint8_t cardType = SD.cardType();
|
||||
|
||||
if (cardType == CARD_NONE) {
|
||||
Serial.println("No SD card attached");
|
||||
return;
|
||||
}
|
||||
|
||||
Serial.print("SD Card Type: ");
|
||||
if (cardType == CARD_MMC) {
|
||||
Serial.println("MMC");
|
||||
} else if (cardType == CARD_SD) {
|
||||
Serial.println("SDSC");
|
||||
} else if (cardType == CARD_SDHC) {
|
||||
Serial.println("SDHC");
|
||||
} else {
|
||||
Serial.println("UNKNOWN");
|
||||
}
|
||||
|
||||
uint64_t cardSize = SD.cardSize() / (1024 * 1024);
|
||||
Serial.printf("SD Card Size: %lluMB\n", cardSize);
|
||||
|
||||
Serial.println("initialisation done.");
|
||||
}
|
||||
|
||||
//####################################################################################################
|
||||
// Main loop
|
||||
//####################################################################################################
|
||||
void loop() {
|
||||
|
||||
tft.setRotation(2); // portrait
|
||||
tft.fillScreen(random(0xFFFF));
|
||||
|
||||
// The image is 300 x 300 pixels so we do some sums to position image in the middle of the screen!
|
||||
// Doing this by reading the image width and height from the jpeg info is left as an exercise!
|
||||
int x = (tft.width() - 300) / 2 - 1;
|
||||
int y = (tft.height() - 300) / 2 - 1;
|
||||
|
||||
drawSdJpeg("/EagleEye.jpg", x, y); // This draws a jpeg pulled off the SD Card
|
||||
delay(2000);
|
||||
|
||||
tft.setRotation(2); // portrait
|
||||
tft.fillScreen(random(0xFFFF));
|
||||
drawSdJpeg("/Baboon40.jpg", 0, 0); // This draws a jpeg pulled off the SD Card
|
||||
delay(2000);
|
||||
|
||||
tft.setRotation(2); // portrait
|
||||
tft.fillScreen(random(0xFFFF));
|
||||
drawSdJpeg("/lena20k.jpg", 0, 0); // This draws a jpeg pulled off the SD Card
|
||||
delay(2000);
|
||||
|
||||
tft.setRotation(1); // landscape
|
||||
tft.fillScreen(random(0xFFFF));
|
||||
drawSdJpeg("/Mouse480.jpg", 0, 0); // This draws a jpeg pulled off the SD Card
|
||||
|
||||
delay(2000);
|
||||
|
||||
while(1); // Wait here
|
||||
}
|
||||
|
||||
//####################################################################################################
|
||||
// Draw a JPEG on the TFT pulled from SD Card
|
||||
//####################################################################################################
|
||||
// xpos, ypos is top left corner of plotted image
|
||||
void drawSdJpeg(const char *filename, int xpos, int ypos) {
|
||||
|
||||
// Open the named file (the Jpeg decoder library will close it)
|
||||
File jpegFile = SD.open( filename, FILE_READ); // or, file handle reference for SD library
|
||||
|
||||
if ( !jpegFile ) {
|
||||
Serial.print("ERROR: File \""); Serial.print(filename); Serial.println ("\" not found!");
|
||||
return;
|
||||
}
|
||||
|
||||
Serial.println("===========================");
|
||||
Serial.print("Drawing file: "); Serial.println(filename);
|
||||
Serial.println("===========================");
|
||||
|
||||
// Use one of the following methods to initialise the decoder:
|
||||
bool decoded = JpegDec.decodeSdFile(jpegFile); // Pass the SD file handle to the decoder,
|
||||
//bool decoded = JpegDec.decodeSdFile(filename); // or pass the filename (String or character array)
|
||||
|
||||
if (decoded) {
|
||||
// print information about the image to the serial port
|
||||
jpegInfo();
|
||||
// render the image onto the screen at given coordinates
|
||||
jpegRender(xpos, ypos);
|
||||
}
|
||||
else {
|
||||
Serial.println("Jpeg file format not supported!");
|
||||
}
|
||||
}
|
||||
|
||||
//####################################################################################################
|
||||
// Draw a JPEG on the TFT, images will be cropped on the right/bottom sides if they do not fit
|
||||
//####################################################################################################
|
||||
// This function assumes xpos,ypos is a valid screen coordinate. For convenience images that do not
|
||||
// fit totally on the screen are cropped to the nearest MCU size and may leave right/bottom borders.
|
||||
void jpegRender(int xpos, int ypos) {
|
||||
|
||||
//jpegInfo(); // Print information from the JPEG file (could comment this line out)
|
||||
|
||||
uint16_t *pImg;
|
||||
uint16_t mcu_w = JpegDec.MCUWidth;
|
||||
uint16_t mcu_h = JpegDec.MCUHeight;
|
||||
uint32_t max_x = JpegDec.width;
|
||||
uint32_t max_y = JpegDec.height;
|
||||
|
||||
bool swapBytes = tft.getSwapBytes();
|
||||
tft.setSwapBytes(true);
|
||||
|
||||
// Jpeg images are draw as a set of image block (tiles) called Minimum Coding Units (MCUs)
|
||||
// Typically these MCUs are 16x16 pixel blocks
|
||||
// Determine the width and height of the right and bottom edge image blocks
|
||||
uint32_t min_w = jpg_min(mcu_w, max_x % mcu_w);
|
||||
uint32_t min_h = jpg_min(mcu_h, max_y % mcu_h);
|
||||
|
||||
// save the current image block size
|
||||
uint32_t win_w = mcu_w;
|
||||
uint32_t win_h = mcu_h;
|
||||
|
||||
// record the current time so we can measure how long it takes to draw an image
|
||||
uint32_t drawTime = millis();
|
||||
|
||||
// save the coordinate of the right and bottom edges to assist image cropping
|
||||
// to the screen size
|
||||
max_x += xpos;
|
||||
max_y += ypos;
|
||||
|
||||
// Fetch data from the file, decode and display
|
||||
while (JpegDec.read()) { // While there is more data in the file
|
||||
pImg = JpegDec.pImage ; // Decode a MCU (Minimum Coding Unit, typically a 8x8 or 16x16 pixel block)
|
||||
|
||||
// Calculate coordinates of top left corner of current MCU
|
||||
int mcu_x = JpegDec.MCUx * mcu_w + xpos;
|
||||
int mcu_y = JpegDec.MCUy * mcu_h + ypos;
|
||||
|
||||
// check if the image block size needs to be changed for the right edge
|
||||
if (mcu_x + mcu_w <= max_x) win_w = mcu_w;
|
||||
else win_w = min_w;
|
||||
|
||||
// check if the image block size needs to be changed for the bottom edge
|
||||
if (mcu_y + mcu_h <= max_y) win_h = mcu_h;
|
||||
else win_h = min_h;
|
||||
|
||||
// copy pixels into a contiguous block
|
||||
if (win_w != mcu_w)
|
||||
{
|
||||
uint16_t *cImg;
|
||||
int p = 0;
|
||||
cImg = pImg + win_w;
|
||||
for (int h = 1; h < win_h; h++)
|
||||
{
|
||||
p += mcu_w;
|
||||
for (int w = 0; w < win_w; w++)
|
||||
{
|
||||
*cImg = *(pImg + w + p);
|
||||
cImg++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// calculate how many pixels must be drawn
|
||||
uint32_t mcu_pixels = win_w * win_h;
|
||||
|
||||
// draw image MCU block only if it will fit on the screen
|
||||
if (( mcu_x + win_w ) <= tft.width() && ( mcu_y + win_h ) <= tft.height())
|
||||
tft.pushImage(mcu_x, mcu_y, win_w, win_h, pImg);
|
||||
else if ( (mcu_y + win_h) >= tft.height())
|
||||
JpegDec.abort(); // Image has run off bottom of screen so abort decoding
|
||||
}
|
||||
|
||||
tft.setSwapBytes(swapBytes);
|
||||
|
||||
showTime(millis() - drawTime); // These lines are for sketch testing only
|
||||
}
|
||||
|
||||
//####################################################################################################
|
||||
// Print image information to the serial port (optional)
|
||||
//####################################################################################################
|
||||
// JpegDec.decodeFile(...) or JpegDec.decodeArray(...) must be called before this info is available!
|
||||
void jpegInfo() {
|
||||
|
||||
// Print information extracted from the JPEG file
|
||||
Serial.println("JPEG image info");
|
||||
Serial.println("===============");
|
||||
Serial.print("Width :");
|
||||
Serial.println(JpegDec.width);
|
||||
Serial.print("Height :");
|
||||
Serial.println(JpegDec.height);
|
||||
Serial.print("Components :");
|
||||
Serial.println(JpegDec.comps);
|
||||
Serial.print("MCU / row :");
|
||||
Serial.println(JpegDec.MCUSPerRow);
|
||||
Serial.print("MCU / col :");
|
||||
Serial.println(JpegDec.MCUSPerCol);
|
||||
Serial.print("Scan type :");
|
||||
Serial.println(JpegDec.scanType);
|
||||
Serial.print("MCU width :");
|
||||
Serial.println(JpegDec.MCUWidth);
|
||||
Serial.print("MCU height :");
|
||||
Serial.println(JpegDec.MCUHeight);
|
||||
Serial.println("===============");
|
||||
Serial.println("");
|
||||
}
|
||||
|
||||
//####################################################################################################
|
||||
// Show the execution time (optional)
|
||||
//####################################################################################################
|
||||
// WARNING: for UNO/AVR legacy reasons printing text to the screen with the Mega might not work for
|
||||
// sketch sizes greater than ~70KBytes because 16-bit address pointers are used in some libraries.
|
||||
|
||||
// The Due will work fine with the HX8357_Due library.
|
||||
|
||||
void showTime(uint32_t msTime) {
|
||||
//tft.setCursor(0, 0);
|
||||
//tft.setTextFont(1);
|
||||
//tft.setTextSize(2);
|
||||
//tft.setTextColor(TFT_WHITE, TFT_BLACK);
|
||||
//tft.print(F(" JPEG drawn in "));
|
||||
//tft.print(msTime);
|
||||
//tft.println(F(" ms "));
|
||||
Serial.print(F(" JPEG drawn in "));
|
||||
Serial.print(msTime);
|
||||
Serial.println(F(" ms "));
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,442 @@
|
|||
// Adapted by Bodmer to work with a NodeMCU and ILI9341 or ST7735 display.
|
||||
//
|
||||
// This code currently does not "blink" the eye!
|
||||
//
|
||||
// Library used is here:
|
||||
// https://github.com/Bodmer/TFT_eSPI
|
||||
//
|
||||
// To do, maybe, one day:
|
||||
// 1. Get the eye to blink
|
||||
// 2. Add another screen for another eye
|
||||
// 3. Add variable to set how wide open the eye is
|
||||
// 4. Add a reflected highlight to the cornea
|
||||
// 5. Add top eyelid shadow to eye surface
|
||||
// 6. Add aliasing to blur mask edge
|
||||
//
|
||||
// With one lidded eye drawn the code runs at 28-33fps (at 27-40MHz SPI clock)
|
||||
// which is quite reasonable. Operation at an 80MHz SPI clock is possible but
|
||||
// the display may not be able to cope with a clock rate that high and the
|
||||
// performance improvement is small. Operate the ESP8266 at 160MHz for best
|
||||
// frame rate. Note the images are stored in SPI FLASH (PROGMEM) so performance
|
||||
// will be constrained by the increased memory access time.
|
||||
|
||||
// Original header for this sketch is below. Note: the technical aspects of the
|
||||
// text no longer apply to this modified version of the sketch:
|
||||
/*
|
||||
//--------------------------------------------------------------------------
|
||||
// Uncanny eyes for PJRC Teensy 3.1 with Adafruit 1.5" OLED (product #1431)
|
||||
// or 1.44" TFT LCD (#2088). This uses Teensy-3.1-specific features and
|
||||
// WILL NOT work on normal Arduino or other boards! Use 72 MHz (Optimized)
|
||||
// board speed -- OLED does not work at 96 MHz.
|
||||
//
|
||||
// Adafruit invests time and resources providing this open source code,
|
||||
// please support Adafruit and open-source hardware by purchasing products
|
||||
// from Adafruit!
|
||||
//
|
||||
// Written by Phil Burgess / Paint Your Dragon for Adafruit Industries.
|
||||
// MIT license. SPI FIFO insight from Paul Stoffregen's ILI9341_t3 library.
|
||||
// Inspired by David Boccabella's (Marcwolf) hybrid servo/OLED eye concept.
|
||||
//--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include <SPI.h>
|
||||
#include <TFT_eSPI.h> // Hardware-specific library
|
||||
|
||||
// Enable ONE of these #includes for the various eyes:
|
||||
#include "defaultEye.h" // Standard human-ish hazel eye
|
||||
//#include "noScleraEye.h" // Large iris, no sclera
|
||||
//#include "dragonEye.h" // Slit pupil fiery dragon/demon eye
|
||||
//#include "goatEye.h" // Horizontal pupil goat/Krampus eye
|
||||
|
||||
|
||||
#define DISPLAY_DC D3 // Data/command pin for BOTH displays
|
||||
#define DISPLAY_RESET D4 // Reset pin for BOTH displays
|
||||
#define SELECT_L_PIN D8 // LEFT eye chip select pin
|
||||
#define SELECT_R_PIN D8 // RIGHT eye chip select pin
|
||||
|
||||
// INPUT CONFIG (for eye motion -- enable or comment out as needed) --------
|
||||
|
||||
// The ESP8266 is rather constrained here as it only has one analogue port.
|
||||
// An I2C ADC could be used for more analogue channels
|
||||
//#define JOYSTICK_X_PIN A0 // Analogue pin for eye horiz pos (else auto)
|
||||
//#define JOYSTICK_Y_PIN A0 // Analogue pin for eye vert position (")
|
||||
//#define JOYSTICK_X_FLIP // If set, reverse stick X axis
|
||||
//#define JOYSTICK_Y_FLIP // If set, reverse stick Y axis
|
||||
#define TRACKING // If enabled, eyelid tracks pupil
|
||||
//#define IRIS_PIN A0 // Photocell or potentiometer (else auto iris)
|
||||
//#define IRIS_PIN_FLIP // If set, reverse reading from dial/photocell
|
||||
//#define IRIS_SMOOTH // If enabled, filter input from IRIS_PIN
|
||||
#define IRIS_MIN 140 // Clip lower analogRead() range from IRIS_PIN
|
||||
#define IRIS_MAX 260 // Clip upper "
|
||||
#define WINK_L_PIN 0 // Pin for LEFT eye wink button
|
||||
#define BLINK_PIN 1 // Pin for blink button (BOTH eyes)
|
||||
#define WINK_R_PIN 2 // Pin for RIGHT eye wink button
|
||||
#define AUTOBLINK // If enabled, eyes blink autonomously
|
||||
|
||||
// Probably don't need to edit any config below this line, -----------------
|
||||
// unless building a single-eye project (pendant, etc.), in which case one
|
||||
// of the two elements in the eye[] array further down can be commented out.
|
||||
|
||||
// Eye blinks are a tiny 3-state machine. Per-eye allows winks + blinks.
|
||||
#define NOBLINK 0 // Not currently engaged in a blink
|
||||
#define ENBLINK 1 // Eyelid is currently closing
|
||||
#define DEBLINK 2 // Eyelid is currently opening
|
||||
typedef struct {
|
||||
int8_t pin; // Optional button here for indiv. wink
|
||||
uint8_t state; // NOBLINK/ENBLINK/DEBLINK
|
||||
int32_t duration; // Duration of blink state (micros)
|
||||
uint32_t startTime; // Time (micros) of last state change
|
||||
} eyeBlink;
|
||||
|
||||
struct {
|
||||
TFT_eSPI tft; // OLED/eye[e].tft object
|
||||
uint8_t cs; // Chip select pin
|
||||
eyeBlink blink; // Current blink state
|
||||
} eye[] = { // OK to comment out one of these for single-eye display:
|
||||
TFT_eSPI(),SELECT_L_PIN,{WINK_L_PIN,NOBLINK},
|
||||
//TFT_eSPI(),SELECT_R_PIN,{WINK_R_PIN,NOBLINK},
|
||||
};
|
||||
|
||||
#define NUM_EYES (sizeof(eye) / sizeof(eye[0]))
|
||||
|
||||
uint32_t fstart = 0; // start time to improve frame rate calculation at startup
|
||||
|
||||
// INITIALIZATION -- runs once at startup ----------------------------------
|
||||
|
||||
void setup(void) {
|
||||
uint8_t e = 0;
|
||||
|
||||
Serial.begin(250000);
|
||||
randomSeed(analogRead(A0)); // Seed random() from floating analogue input
|
||||
|
||||
eye[e].tft.init();
|
||||
eye[e].tft.fillScreen(TFT_BLACK);
|
||||
eye[e].tft.setRotation(0);
|
||||
|
||||
fstart = millis()-1; // Subtract 1 to avoid divide by zero later
|
||||
}
|
||||
|
||||
|
||||
// EYE-RENDERING FUNCTION --------------------------------------------------
|
||||
#define BUFFER_SIZE 256 // 64 to 512 seems optimum = 30 fps for default eye
|
||||
void drawEye( // Renders one eye. Inputs must be pre-clipped & valid.
|
||||
// Use native 32-bit variables where possible as this is 10% faster!
|
||||
uint8_t e, // Eye array index; 0 or 1 for left/right
|
||||
uint32_t iScale, // Scale factor for iris
|
||||
uint32_t scleraX, // First pixel X offset into sclera image
|
||||
uint32_t scleraY, // First pixel Y offset into sclera image
|
||||
uint32_t uT, // Upper eyelid threshold value
|
||||
uint32_t lT) { // Lower eyelid threshold value
|
||||
|
||||
uint32_t screenX, screenY, scleraXsave;
|
||||
int32_t irisX, irisY;
|
||||
uint32_t p, a;
|
||||
uint32_t d;
|
||||
|
||||
uint32_t pixels = 0;
|
||||
uint16_t pbuffer[BUFFER_SIZE]; // This one needs to be 16-bit
|
||||
|
||||
// Set up raw pixel dump to entire screen. Although such writes can wrap
|
||||
// around automatically from end of rect back to beginning, the region is
|
||||
// reset on each frame here in case of an SPI glitch.
|
||||
|
||||
//eye[e].tft.setAddrWindow(319-127, 0, 319, 127);
|
||||
eye[e].tft.setAddrWindow(0, 0, 128, 128);
|
||||
|
||||
//digitalWrite(eye[e].cs, LOW); // Chip select
|
||||
|
||||
// Now just issue raw 16-bit values for every pixel...
|
||||
|
||||
scleraXsave = scleraX; // Save initial X value to reset on each line
|
||||
irisY = scleraY - (SCLERA_HEIGHT - IRIS_HEIGHT) / 2;
|
||||
for(screenY=0; screenY<SCREEN_HEIGHT; screenY++, scleraY++, irisY++) {
|
||||
scleraX = scleraXsave;
|
||||
irisX = scleraXsave - (SCLERA_WIDTH - IRIS_WIDTH) / 2;
|
||||
for(screenX=0; screenX<SCREEN_WIDTH; screenX++, scleraX++, irisX++) {
|
||||
if((pgm_read_byte(lower + screenY * SCREEN_WIDTH + screenX) <= lT) ||
|
||||
(pgm_read_byte(upper + screenY * SCREEN_WIDTH + screenX) <= uT)) { // Covered by eyelid
|
||||
p = 0;
|
||||
} else if((irisY < 0) || (irisY >= IRIS_HEIGHT) ||
|
||||
(irisX < 0) || (irisX >= IRIS_WIDTH)) { // In sclera
|
||||
p = pgm_read_word(sclera + scleraY * SCLERA_WIDTH + scleraX);
|
||||
} else { // Maybe iris...
|
||||
p = pgm_read_word(polar + irisY * IRIS_WIDTH + irisX); // Polar angle/dist
|
||||
d = (iScale * (p & 0x7F)) / 128; // Distance (Y)
|
||||
if(d < IRIS_MAP_HEIGHT) { // Within iris area
|
||||
a = (IRIS_MAP_WIDTH * (p >> 7)) / 512; // Angle (X)
|
||||
p = pgm_read_word(iris + d * IRIS_MAP_WIDTH + a); // Pixel = iris
|
||||
} else { // Not in iris
|
||||
p = pgm_read_word(sclera + scleraY * SCLERA_WIDTH + scleraX); // Pixel = sclera
|
||||
}
|
||||
}
|
||||
*(pbuffer + pixels++) = p>>8 | p<<8;
|
||||
|
||||
if (pixels >= BUFFER_SIZE) { yield(); eye[e].tft.pushColors((uint8_t*)pbuffer, pixels*2); pixels = 0;}
|
||||
}
|
||||
}
|
||||
|
||||
if (pixels) { eye[e].tft.pushColors(pbuffer, pixels); pixels = 0;}
|
||||
}
|
||||
|
||||
|
||||
// EYE ANIMATION -----------------------------------------------------------
|
||||
|
||||
const uint8_t ease[] = { // Ease in/out curve for eye movements 3*t^2-2*t^3
|
||||
0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 3, // T
|
||||
3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 7, 8, 9, 9, 10, 10, // h
|
||||
11, 12, 12, 13, 14, 15, 15, 16, 17, 18, 18, 19, 20, 21, 22, 23, // x
|
||||
24, 25, 26, 27, 27, 28, 29, 30, 31, 33, 34, 35, 36, 37, 38, 39, // 2
|
||||
40, 41, 42, 44, 45, 46, 47, 48, 50, 51, 52, 53, 54, 56, 57, 58, // A
|
||||
60, 61, 62, 63, 65, 66, 67, 69, 70, 72, 73, 74, 76, 77, 78, 80, // l
|
||||
81, 83, 84, 85, 87, 88, 90, 91, 93, 94, 96, 97, 98,100,101,103, // e
|
||||
104,106,107,109,110,112,113,115,116,118,119,121,122,124,125,127, // c
|
||||
128,130,131,133,134,136,137,139,140,142,143,145,146,148,149,151, // J
|
||||
152,154,155,157,158,159,161,162,164,165,167,168,170,171,172,174, // a
|
||||
175,177,178,179,181,182,183,185,186,188,189,190,192,193,194,195, // c
|
||||
197,198,199,201,202,203,204,205,207,208,209,210,211,213,214,215, // o
|
||||
216,217,218,219,220,221,222,224,225,226,227,228,228,229,230,231, // b
|
||||
232,233,234,235,236,237,237,238,239,240,240,241,242,243,243,244, // s
|
||||
245,245,246,246,247,248,248,249,249,250,250,251,251,251,252,252, // o
|
||||
252,253,253,253,254,254,254,254,254,255,255,255,255,255,255,255 }; // n
|
||||
|
||||
#ifdef AUTOBLINK
|
||||
uint32_t timeOfLastBlink = 0L, timeToNextBlink = 0L;
|
||||
#endif
|
||||
|
||||
void frame( // Process motion for a single frame of left or right eye
|
||||
uint32_t iScale) { // Iris scale (0-1023) passed in
|
||||
static uint32_t frames = 0; // Used in frame rate calculation
|
||||
static uint8_t eyeIndex = 0; // eye[] array counter
|
||||
int32_t eyeX, eyeY;
|
||||
uint32_t t = micros(); // Time at start of function
|
||||
|
||||
Serial.print((++frames * 1000) / (millis() - fstart)); Serial.println("fps");// Show frame rate
|
||||
|
||||
if(++eyeIndex >= NUM_EYES) eyeIndex = 0; // Cycle through eyes, 1 per call
|
||||
|
||||
// Autonomous X/Y eye motion
|
||||
// Periodically initiates motion to a new random point, random speed,
|
||||
// holds there for random period until next motion.
|
||||
|
||||
static bool eyeInMotion = false;
|
||||
static int32_t eyeOldX=512, eyeOldY=512, eyeNewX=512, eyeNewY=512;
|
||||
static uint32_t eyeMoveStartTime = 0L;
|
||||
static int32_t eyeMoveDuration = 0L;
|
||||
|
||||
int32_t dt = t - eyeMoveStartTime; // uS elapsed since last eye event
|
||||
if(eyeInMotion) { // Currently moving?
|
||||
if(dt >= eyeMoveDuration) { // Time up? Destination reached.
|
||||
eyeInMotion = false; // Stop moving
|
||||
eyeMoveDuration = random(3000000L); // 0-3 sec stop
|
||||
eyeMoveStartTime = t; // Save initial time of stop
|
||||
eyeX = eyeOldX = eyeNewX; // Save position
|
||||
eyeY = eyeOldY = eyeNewY;
|
||||
} else { // Move time's not yet fully elapsed -- interpolate position
|
||||
int16_t e = ease[255 * dt / eyeMoveDuration] + 1; // Ease curve
|
||||
eyeX = eyeOldX + (((eyeNewX - eyeOldX) * e) / 256); // Interp X
|
||||
eyeY = eyeOldY + (((eyeNewY - eyeOldY) * e) / 256); // and Y
|
||||
}
|
||||
} else { // Eye stopped
|
||||
eyeX = eyeOldX;
|
||||
eyeY = eyeOldY;
|
||||
if(dt > eyeMoveDuration) { // Time up? Begin new move.
|
||||
int16_t dx, dy;
|
||||
uint32_t d;
|
||||
do { // Pick new dest in circle
|
||||
eyeNewX = random(1024);
|
||||
eyeNewY = random(1024);
|
||||
dx = (eyeNewX * 2) - 1023;
|
||||
dy = (eyeNewY * 2) - 1023;
|
||||
} while((d = (dx * dx + dy * dy)) > (1023 * 1023)); // Keep trying
|
||||
eyeMoveDuration = random(50000, 150000);//random(72000, 144000); // ~1/14 - ~1/7 sec
|
||||
eyeMoveStartTime = t; // Save initial time of move
|
||||
eyeInMotion = true; // Start move on next frame
|
||||
}
|
||||
}
|
||||
|
||||
// Blinking
|
||||
/*
|
||||
#ifdef AUTOBLINK
|
||||
// Similar to the autonomous eye movement above -- blink start times
|
||||
// and durations are random (within ranges).
|
||||
if((t - timeOfLastBlink) >= timeToNextBlink) { // Start new blink?
|
||||
timeOfLastBlink = t;
|
||||
uint32_t blinkDuration = random(36000, 72000); // ~1/28 - ~1/14 sec
|
||||
// Set up durations for both eyes (if not already winking)
|
||||
for(uint8_t e=0; e<NUM_EYES; e++) {
|
||||
if(eye[e].blink.state == NOBLINK) {
|
||||
eye[e].blink.state = ENBLINK;
|
||||
eye[e].blink.startTime = t;
|
||||
eye[e].blink.duration = blinkDuration;
|
||||
}
|
||||
}
|
||||
timeToNextBlink = blinkDuration * 3 + random(4000000);
|
||||
}
|
||||
#endif
|
||||
*/
|
||||
/*
|
||||
if(eye[eyeIndex].blink.state) { // Eye currently blinking?
|
||||
// Check if current blink state time has elapsed
|
||||
if((t - eye[eyeIndex].blink.startTime) >= eye[eyeIndex].blink.duration) {
|
||||
// Yes -- increment blink state, unless...
|
||||
if((eye[eyeIndex].blink.state == ENBLINK) && // Enblinking and...
|
||||
((digitalRead(BLINK_PIN) == LOW) || // blink or wink held...
|
||||
digitalRead(eye[eyeIndex].blink.pin) == LOW)) {
|
||||
// Don't advance state yet -- eye is held closed instead
|
||||
} else { // No buttons, or other state...
|
||||
if(++eye[eyeIndex].blink.state > DEBLINK) { // Deblinking finished?
|
||||
eye[eyeIndex].blink.state = NOBLINK; // No longer blinking
|
||||
} else { // Advancing from ENBLINK to DEBLINK mode
|
||||
eye[eyeIndex].blink.duration *= 2; // DEBLINK is 1/2 ENBLINK speed
|
||||
eye[eyeIndex].blink.startTime = t;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else { // Not currently blinking...check buttons!
|
||||
if(digitalRead(BLINK_PIN) == LOW) {
|
||||
// Manually-initiated blinks have random durations like auto-blink
|
||||
uint32_t blinkDuration = random(36000, 72000);
|
||||
for(uint8_t e=0; e<NUM_EYES; e++) {
|
||||
if(eye[e].blink.state == NOBLINK) {
|
||||
eye[e].blink.state = ENBLINK;
|
||||
eye[e].blink.startTime = t;
|
||||
eye[e].blink.duration = blinkDuration;
|
||||
}
|
||||
}
|
||||
} else if(digitalRead(eye[eyeIndex].blink.pin) == LOW) { // Wink!
|
||||
eye[eyeIndex].blink.state = ENBLINK;
|
||||
eye[eyeIndex].blink.startTime = t;
|
||||
eye[eyeIndex].blink.duration = random(45000, 90000);
|
||||
}
|
||||
}
|
||||
*/
|
||||
// Process motion, blinking and iris scale into renderable values
|
||||
|
||||
// Iris scaling: remap from 0-1023 input to iris map height pixel units
|
||||
iScale = ((IRIS_MAP_HEIGHT + 1) * 1024) /
|
||||
(1024 - (iScale * (IRIS_MAP_HEIGHT - 1) / IRIS_MAP_HEIGHT));
|
||||
|
||||
// Scale eye X/Y positions (0-1023) to pixel units used by drawEye()
|
||||
eyeX = map(eyeX, 0, 1023, 0, SCLERA_WIDTH - 128);
|
||||
eyeY = map(eyeY, 0, 1023, 0, SCLERA_HEIGHT - 128);
|
||||
if(eyeIndex == 1) eyeX = (SCLERA_WIDTH - 128) - eyeX; // Mirrored display
|
||||
|
||||
// Horizontal position is offset so that eyes are very slightly crossed
|
||||
// to appear fixated (converged) at a conversational distance. Number
|
||||
// here was extracted from my posterior and not mathematically based.
|
||||
// I suppose one could get all clever with a range sensor, but for now...
|
||||
eyeX += 4;
|
||||
if(eyeX > (SCLERA_WIDTH - 128)) eyeX = (SCLERA_WIDTH - 128);
|
||||
|
||||
// Eyelids are rendered using a brightness threshold image. This same
|
||||
// map can be used to simplify another problem: making the upper eyelid
|
||||
// track the pupil (eyes tend to open only as much as needed -- e.g. look
|
||||
// down and the upper eyelid drops). Just sample a point in the upper
|
||||
// lid map slightly above the pupil to determine the rendering threshold.
|
||||
static uint8_t uThreshold = 128;
|
||||
uint8_t lThreshold, n;
|
||||
|
||||
#ifdef TRACKING
|
||||
int16_t sampleX = SCLERA_WIDTH / 2 - (eyeX / 2), // Reduce X influence
|
||||
sampleY = SCLERA_HEIGHT / 2 - (eyeY + IRIS_HEIGHT / 4);
|
||||
// Eyelid is slightly asymmetrical, so two readings are taken, averaged
|
||||
if(sampleY < 0) n = 0;
|
||||
else n = (pgm_read_byte(upper + sampleY * SCREEN_WIDTH + sampleX) +
|
||||
pgm_read_byte(upper + sampleY * SCREEN_WIDTH + (SCREEN_WIDTH - 1 - sampleX))) / 2;
|
||||
uThreshold = (uThreshold * 3 + n) / 4; // Filter/soften motion
|
||||
// Lower eyelid doesn't track the same way, but seems to be pulled upward
|
||||
// by tension from the upper lid.
|
||||
lThreshold = 254 - uThreshold;
|
||||
#else // No tracking -- eyelids full open unless blink modifies them
|
||||
uThreshold = lThreshold = 0;
|
||||
#endif
|
||||
|
||||
// The upper/lower thresholds are then scaled relative to the current
|
||||
// blink position so that blinks work together with pupil tracking.
|
||||
if(eye[eyeIndex].blink.state) { // Eye currently blinking?
|
||||
uint32_t s = (t - eye[eyeIndex].blink.startTime);
|
||||
if(s >= eye[eyeIndex].blink.duration) s = 255; // At or past blink end
|
||||
else s = 255 * s / eye[eyeIndex].blink.duration; // Mid-blink
|
||||
s = (eye[eyeIndex].blink.state == DEBLINK) ? 1 + s : 256 - s;
|
||||
n = (uThreshold * s + 254 * (257 - s)) / 256;
|
||||
lThreshold = (lThreshold * s + 254 * (257 - s)) / 256;
|
||||
} else {
|
||||
n = uThreshold;
|
||||
}
|
||||
|
||||
// Pass all the derived values to the eye-rendering function:
|
||||
drawEye(eyeIndex, iScale, eyeX, eyeY, n, lThreshold);
|
||||
|
||||
}
|
||||
|
||||
|
||||
// AUTONOMOUS IRIS SCALING (if no photocell or dial) -----------------------
|
||||
|
||||
#if !defined(IRIS_PIN) || (IRIS_PIN < 0)
|
||||
|
||||
// Autonomous iris motion uses a fractal behavior to similate both the major
|
||||
// reaction of the eye plus the continuous smaller adjustments that occur.
|
||||
|
||||
uint16_t oldIris = (IRIS_MIN + IRIS_MAX) / 2, newIris;
|
||||
|
||||
void split( // Subdivides motion path into two sub-paths w/randimization
|
||||
int16_t startValue, // Iris scale value (IRIS_MIN to IRIS_MAX) at start
|
||||
int16_t endValue, // Iris scale value at end
|
||||
uint32_t startTime, // micros() at start
|
||||
int32_t duration, // Start-to-end time, in microseconds
|
||||
int16_t range) { // Allowable scale value variance when subdividing
|
||||
|
||||
if(range >= 8) { // Limit subdvision count, because recursion
|
||||
range /= 2; // Split range & time in half for subdivision,
|
||||
duration /= 2; // then pick random center point within range:
|
||||
int16_t midValue = (startValue + endValue - range) / 2 + random(range);
|
||||
uint32_t midTime = startTime + duration;
|
||||
split(startValue, midValue, startTime, duration, range); // First half
|
||||
split(midValue , endValue, midTime , duration, range); // Second half
|
||||
} else { // No more subdivisons, do iris motion...
|
||||
int32_t dt; // Time (micros) since start of motion
|
||||
int16_t v; // Interim value
|
||||
while((dt = (micros() - startTime)) < duration) {
|
||||
v = startValue + (((endValue - startValue) * dt) / duration);
|
||||
if(v < IRIS_MIN) v = IRIS_MIN; // Clip just in case
|
||||
else if(v > IRIS_MAX) v = IRIS_MAX;
|
||||
frame(v); // Draw frame w/interim iris scale value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // !IRIS_PIN
|
||||
|
||||
|
||||
// MAIN LOOP -- runs continuously after setup() ----------------------------
|
||||
|
||||
void loop() {
|
||||
|
||||
#if defined(IRIS_PIN) && (IRIS_PIN >= 0) // Interactive iris
|
||||
|
||||
uint16_t v = 512; //analogRead(IRIS_PIN); // Raw dial/photocell reading
|
||||
#ifdef IRIS_PIN_FLIP
|
||||
v = 1023 - v;
|
||||
#endif
|
||||
v = map(v, 0, 1023, IRIS_MIN, IRIS_MAX); // Scale to iris range
|
||||
#ifdef IRIS_SMOOTH // Filter input (gradual motion)
|
||||
static uint16_t irisValue = (IRIS_MIN + IRIS_MAX) / 2;
|
||||
irisValue = ((irisValue * 15) + v) / 16;
|
||||
frame(irisValue);
|
||||
#else // Unfiltered (immediate motion)
|
||||
frame(v);
|
||||
#endif // IRIS_SMOOTH
|
||||
|
||||
#else // Autonomous iris scaling -- invoke recursive function
|
||||
|
||||
newIris = random(IRIS_MIN, IRIS_MAX);
|
||||
split(oldIris, newIris, micros(), 10000000L, IRIS_MAX - IRIS_MIN);
|
||||
oldIris = newIris;
|
||||
|
||||
#endif // IRIS_PIN
|
||||
|
||||
//screenshotToConsole();
|
||||
}
|
||||
|
||||
|
||||
|
||||
11966
libraries/TFT_eSPI/examples/Generic/ESP8266_uncannyEyes/defaultEye.h
Normal file
11966
libraries/TFT_eSPI/examples/Generic/ESP8266_uncannyEyes/defaultEye.h
Normal file
File diff suppressed because it is too large
Load diff
15638
libraries/TFT_eSPI/examples/Generic/ESP8266_uncannyEyes/dragonEye.h
Normal file
15638
libraries/TFT_eSPI/examples/Generic/ESP8266_uncannyEyes/dragonEye.h
Normal file
File diff suppressed because it is too large
Load diff
11430
libraries/TFT_eSPI/examples/Generic/ESP8266_uncannyEyes/goatEye.h
Normal file
11430
libraries/TFT_eSPI/examples/Generic/ESP8266_uncannyEyes/goatEye.h
Normal file
File diff suppressed because it is too large
Load diff
15638
libraries/TFT_eSPI/examples/Generic/ESP8266_uncannyEyes/noScleraEye.h
Normal file
15638
libraries/TFT_eSPI/examples/Generic/ESP8266_uncannyEyes/noScleraEye.h
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,220 @@
|
|||
// Include code in this tab and call screenshotToConsole() this dumps an
|
||||
// image off the screen and sends it to a PC via the serial port in a Run
|
||||
// Length Encoded format for viewing with a "ILIScreenshotViewer" utility.
|
||||
|
||||
// The PC "ILIScreenshotViewer" is part of the ILI9241_due library in the
|
||||
// Tools folder, that library can be found here:
|
||||
// https://github.com/marekburiak/ILI9341_Due
|
||||
|
||||
// Converted by Bodmer to operate with the TFT_ILI9341_ESP library:
|
||||
// https://github.com/Bodmer/TFT_ILI9341_ESP
|
||||
|
||||
/*
|
||||
The functions below have been adapted from the ILI9341_due library, the file
|
||||
header from the .cpp source file is included below:
|
||||
|
||||
ILI9341_due_.cpp - Arduino Due library for interfacing with ILI9341-based TFTs
|
||||
|
||||
Copyright (c) 2014 Marek Buriak
|
||||
|
||||
This library is based on ILI9341_t3 library from Paul Stoffregen
|
||||
(https://github.com/PaulStoffregen/ILI9341_t3), Adafruit_ILI9341
|
||||
and Adafruit_GFX libraries from Limor Fried/Ladyada
|
||||
(https://github.com/adafruit/Adafruit_ILI9341).
|
||||
|
||||
This file is part of the Arduino ILI9341_due library.
|
||||
Sources for this library can be found at https://github.com/marekburiak/ILI9341_Due.
|
||||
|
||||
ILI9341_due is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
ILI9341_due is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details:
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
//====================================================================================
|
||||
|
||||
void screenshotToConsole()
|
||||
{
|
||||
uint8_t e = 0;
|
||||
uint8_t lastColor[3];
|
||||
uint8_t color[3];
|
||||
uint32_t sameColorPixelCount = 0;
|
||||
uint16_t sameColorPixelCount16 = 0;
|
||||
uint32_t sameColorStartIndex = 0;
|
||||
uint32_t totalImageDataLength = 0;
|
||||
|
||||
// delay(1000);
|
||||
|
||||
// Header text
|
||||
Serial.println((eye[e].tft.width() - 1));
|
||||
Serial.println((eye[e].tft.height() - 1));
|
||||
Serial.println(F("==== PIXEL DATA START ===="));
|
||||
|
||||
// Get first pixel to prime the Run Length Encoded
|
||||
// Function format is: tft.readRectRGB( x, y, width, height, buffer);
|
||||
// color is a pointer to a buffer that the RGB 8-bit values are piped into
|
||||
// the buffer size must be >= (width * height * 3) bytes
|
||||
eye[e].tft.readRectRGB(0, 0, 1, 1, color); // 1 x 1 so reading 1 pixel at 0,0
|
||||
|
||||
lastColor[0] = color[0]; // Red
|
||||
lastColor[1] = color[1]; // Green
|
||||
lastColor[2] = color[2]; // Blue
|
||||
|
||||
printHex8(color, 3); //Send color of the first pixel to serial port
|
||||
totalImageDataLength += 6;
|
||||
sameColorStartIndex = 0;
|
||||
|
||||
for (uint32_t py = 0; py < (eye[e].tft.height() - 1); py++)
|
||||
{
|
||||
for (uint32_t px = 0; px < (eye[e].tft.width() - 1); px++)
|
||||
{
|
||||
uint32_t i = px + eye[e].tft.width() * py;
|
||||
yield();
|
||||
if (i)
|
||||
{
|
||||
eye[e].tft.readRectRGB(px, py, 1, 1, color);
|
||||
|
||||
if (color[0] != lastColor[0] ||
|
||||
color[1] != lastColor[1] ||
|
||||
color[2] != lastColor[2])
|
||||
{
|
||||
sameColorPixelCount = i - sameColorStartIndex;
|
||||
if (sameColorPixelCount > 65535)
|
||||
{
|
||||
sameColorPixelCount16 = 65535;
|
||||
printHex16(&sameColorPixelCount16, 1);
|
||||
printHex8(lastColor, 3);
|
||||
totalImageDataLength += 10;
|
||||
sameColorPixelCount16 = sameColorPixelCount - 65535;
|
||||
}
|
||||
else
|
||||
sameColorPixelCount16 = sameColorPixelCount;
|
||||
printHex16(&sameColorPixelCount16, 1);
|
||||
printHex8(color, 3);
|
||||
totalImageDataLength += 10;
|
||||
|
||||
sameColorStartIndex = i;
|
||||
lastColor[0] = color[0];
|
||||
lastColor[1] = color[1];
|
||||
lastColor[2] = color[2];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
sameColorPixelCount = (uint32_t)eye[e].tft.width() * (uint32_t)eye[e].tft.height() - sameColorStartIndex;
|
||||
if (sameColorPixelCount > 65535)
|
||||
{
|
||||
sameColorPixelCount16 = 65535;
|
||||
printHex16(&sameColorPixelCount16, 1);
|
||||
printHex8(lastColor, 3);
|
||||
totalImageDataLength += 10;
|
||||
sameColorPixelCount16 = sameColorPixelCount - 65535;
|
||||
}
|
||||
else
|
||||
sameColorPixelCount16 = sameColorPixelCount;
|
||||
printHex16(&sameColorPixelCount16, 1);
|
||||
totalImageDataLength += 4;
|
||||
printHex32(&totalImageDataLength, 1);
|
||||
|
||||
// Footer text
|
||||
Serial.println();
|
||||
Serial.println(F("==== PIXEL DATA END ===="));
|
||||
Serial.print(F("Total Image Data Length: "));
|
||||
Serial.println(totalImageDataLength);
|
||||
}
|
||||
|
||||
void printHex8(uint8_t *data, uint8_t length) // prints 8-bit data in hex
|
||||
{
|
||||
char tmp[length * 2 + 1];
|
||||
byte first;
|
||||
byte second;
|
||||
for (int i = 0; i < length; i++) {
|
||||
first = (data[i] >> 4) & 0x0f;
|
||||
second = data[i] & 0x0f;
|
||||
// base for converting single digit numbers to ASCII is 48
|
||||
// base for 10-16 to become upper-case characters A-F is 55
|
||||
// note: difference is 7
|
||||
tmp[i * 2] = first + 48;
|
||||
tmp[i * 2 + 1] = second + 48;
|
||||
if (first > 9) tmp[i * 2] += 7;
|
||||
if (second > 9) tmp[i * 2 + 1] += 7;
|
||||
}
|
||||
tmp[length * 2] = 0;
|
||||
Serial.print(tmp);
|
||||
}
|
||||
|
||||
void printHex16(uint16_t *data, uint8_t length) // prints 8-bit data in hex
|
||||
{
|
||||
char tmp[length * 4 + 1];
|
||||
byte first;
|
||||
byte second;
|
||||
byte third;
|
||||
byte fourth;
|
||||
for (int i = 0; i < length; i++) {
|
||||
first = (data[i] >> 12) & 0x0f;
|
||||
second = (data[i] >> 8) & 0x0f;
|
||||
third = (data[i] >> 4) & 0x0f;
|
||||
fourth = data[i] & 0x0f;
|
||||
//Serial << first << " " << second << " " << third << " " << fourth << endl;
|
||||
// base for converting single digit numbers to ASCII is 48
|
||||
// base for 10-16 to become upper-case characters A-F is 55
|
||||
// note: difference is 7
|
||||
tmp[i * 4] = first + 48;
|
||||
tmp[i * 4 + 1] = second + 48;
|
||||
tmp[i * 4 + 2] = third + 48;
|
||||
tmp[i * 4 + 3] = fourth + 48;
|
||||
//tmp[i*5+4] = 32; // add trailing space
|
||||
if (first > 9) tmp[i * 4] += 7;
|
||||
if (second > 9) tmp[i * 4 + 1] += 7;
|
||||
if (third > 9) tmp[i * 4 + 2] += 7;
|
||||
if (fourth > 9) tmp[i * 4 + 3] += 7;
|
||||
}
|
||||
tmp[length * 4] = 0;
|
||||
Serial.print(tmp);
|
||||
}
|
||||
|
||||
void printHex32(uint32_t *data, uint8_t length) // prints 8-bit data in hex
|
||||
{
|
||||
char tmp[length * 8 + 1];
|
||||
byte dataByte[8];
|
||||
for (int i = 0; i < length; i++) {
|
||||
dataByte[0] = (data[i] >> 28) & 0x0f;
|
||||
dataByte[1] = (data[i] >> 24) & 0x0f;
|
||||
dataByte[2] = (data[i] >> 20) & 0x0f;
|
||||
dataByte[3] = (data[i] >> 16) & 0x0f;
|
||||
dataByte[4] = (data[i] >> 12) & 0x0f;
|
||||
dataByte[5] = (data[i] >> 8) & 0x0f;
|
||||
dataByte[6] = (data[i] >> 4) & 0x0f;
|
||||
dataByte[7] = data[i] & 0x0f;
|
||||
//Serial << first << " " << second << " " << third << " " << fourth << endl;
|
||||
// base for converting single digit numbers to ASCII is 48
|
||||
// base for 10-16 to become upper-case characters A-F is 55
|
||||
// note: difference is 7
|
||||
tmp[i * 4] = dataByte[0] + 48;
|
||||
tmp[i * 4 + 1] = dataByte[1] + 48;
|
||||
tmp[i * 4 + 2] = dataByte[2] + 48;
|
||||
tmp[i * 4 + 3] = dataByte[3] + 48;
|
||||
tmp[i * 4 + 4] = dataByte[4] + 48;
|
||||
tmp[i * 4 + 5] = dataByte[5] + 48;
|
||||
tmp[i * 4 + 6] = dataByte[6] + 48;
|
||||
tmp[i * 4 + 7] = dataByte[7] + 48;
|
||||
//tmp[i*5+4] = 32; // add trailing space
|
||||
if (dataByte[0] > 9) tmp[i * 4] += 7;
|
||||
if (dataByte[1] > 9) tmp[i * 4 + 1] += 7;
|
||||
if (dataByte[2] > 9) tmp[i * 4 + 2] += 7;
|
||||
if (dataByte[3] > 9) tmp[i * 4 + 3] += 7;
|
||||
if (dataByte[4] > 9) tmp[i * 4 + 4] += 7;
|
||||
if (dataByte[5] > 9) tmp[i * 4 + 5] += 7;
|
||||
if (dataByte[6] > 9) tmp[i * 4 + 6] += 7;
|
||||
if (dataByte[7] > 9) tmp[i * 4 + 7] += 7;
|
||||
}
|
||||
tmp[length * 8] = 0;
|
||||
Serial.print(tmp);
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
This sketch demonstrates the use of the horizontal and vertical gradient
|
||||
rectangle fill functions.
|
||||
|
||||
Example for library:
|
||||
https://github.com/Bodmer/TFT_eSPI
|
||||
|
||||
Created by Bodmer 27/1/22
|
||||
*/
|
||||
|
||||
#include <TFT_eSPI.h> // Include the graphics library
|
||||
TFT_eSPI tft = TFT_eSPI(); // Create object "tft"
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Setup
|
||||
// -------------------------------------------------------------------------
|
||||
void setup(void) {
|
||||
tft.init();
|
||||
tft.setRotation(1);
|
||||
tft.fillScreen(TFT_DARKGREY);
|
||||
tft.setTextFont(2);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Main loop
|
||||
// -------------------------------------------------------------------------
|
||||
void loop()
|
||||
{
|
||||
tft.fillRectHGradient(0, 0, 160, 50, TFT_MAGENTA, TFT_BLUE);
|
||||
tft.setCursor(10,10);
|
||||
tft.print("Horizontal gradient");
|
||||
|
||||
tft.fillRectVGradient(0, 60, 160, 50, TFT_ORANGE, TFT_RED);
|
||||
tft.setCursor(10,70);
|
||||
tft.print("Vertical gradient");
|
||||
|
||||
while(1) delay(100); // Wait here
|
||||
}
|
||||
83
libraries/TFT_eSPI/examples/Generic/Julia_Set/Julia_Set.ino
Normal file
83
libraries/TFT_eSPI/examples/Generic/Julia_Set/Julia_Set.ino
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
// Based on sketch here:
|
||||
// https://github.com/OpenHDZ/Arduino-experimentation
|
||||
// Adapted for TFT_eSPI library
|
||||
|
||||
// Note: a high number of floating point calculations are needed
|
||||
// for each pixel so rendering will be quite slow.
|
||||
// For best performance use a Teensy 4.x (600MHz CPU clock).
|
||||
|
||||
#include <TFT_eSPI.h> // Hardware-specific library
|
||||
|
||||
TFT_eSPI tft = TFT_eSPI(); // Invoke custom library
|
||||
|
||||
const uint16_t MAX_ITERATION = 300; // Nombre de couleurs
|
||||
|
||||
#define SCREEN_WIDTH tft.width() //
|
||||
#define SCREEN_HEIGHT tft.height() // Taille de l'écran
|
||||
|
||||
static float zoom = 0.5;
|
||||
|
||||
/* Fonction setup */
|
||||
void setup() {
|
||||
/* Initialise l'écran LCD */
|
||||
tft.begin();
|
||||
tft.setRotation(1);
|
||||
tft.fillScreen(TFT_BLACK);
|
||||
tft.setFreeFont(&FreeMono9pt7b);
|
||||
}
|
||||
|
||||
/* Fonction loop() */
|
||||
void loop() {
|
||||
/* Dessine la fractale */
|
||||
draw_Julia(-0.8,+0.156,zoom);
|
||||
tft.fillRect(0, 0, 150, 20, TFT_BLACK);
|
||||
tft.setCursor(0,15);
|
||||
tft.setTextColor(TFT_WHITE);
|
||||
tft.print(" Zoom = ");
|
||||
tft.println(zoom);
|
||||
delay(2000);
|
||||
zoom *= 1.5;
|
||||
if (zoom > 100) zoom = 0.5;
|
||||
}
|
||||
|
||||
/*
|
||||
Dessine une fractale de Julia
|
||||
*/
|
||||
|
||||
void draw_Julia(float c_r, float c_i, float zoom) {
|
||||
|
||||
tft.setCursor(0,0);
|
||||
float new_r = 0.0, new_i = 0.0, old_r = 0.0, old_i = 0.0;
|
||||
|
||||
/* Pour chaque pixel en X */
|
||||
|
||||
for(int16_t x = SCREEN_WIDTH/2 - 1; x >= 0; x--) { // Rely on inverted symmetry
|
||||
/* Pour chaque pixel en Y */
|
||||
for(uint16_t y = 0; y < SCREEN_HEIGHT; y++) {
|
||||
old_r = 1.5 * (x - SCREEN_WIDTH / 2) / (0.5 * zoom * SCREEN_WIDTH);
|
||||
old_i = (y - SCREEN_HEIGHT / 2) / (0.5 * zoom * SCREEN_HEIGHT);
|
||||
uint16_t i = 0;
|
||||
|
||||
while ((old_r * old_r + old_i * old_i) < 4.0 && i < MAX_ITERATION) {
|
||||
new_r = old_r * old_r - old_i * old_i ;
|
||||
new_i = 2.0 * old_r * old_i;
|
||||
|
||||
old_r = new_r+c_r;
|
||||
old_i = new_i+c_i;
|
||||
|
||||
i++;
|
||||
}
|
||||
/* Affiche le pixel */
|
||||
if (i < 100){
|
||||
tft.drawPixel(x,y,tft.color565(255,255,map(i,0,100,255,0)));
|
||||
tft.drawPixel(SCREEN_WIDTH - x - 1,SCREEN_HEIGHT - y - 1,tft.color565(255,255,map(i,0,100,255,0)));
|
||||
}if(i<200){
|
||||
tft.drawPixel(x,y,tft.color565(255,map(i,100,200,255,0),0));
|
||||
tft.drawPixel(SCREEN_WIDTH - x - 1,SCREEN_HEIGHT - y - 1,tft.color565(255,map(i,100,200,255,0),0));
|
||||
}else{
|
||||
tft.drawPixel(x,y,tft.color565(map(i,200,300,255,0),0,0));
|
||||
tft.drawPixel(SCREEN_WIDTH - x - 1,SCREEN_HEIGHT - y - 1,tft.color565(map(i,200,300,255,0),0,0));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
Example for TFT_eSPI library
|
||||
|
||||
This example shows the use of a Adafruit_GFX custom font with a
|
||||
character code range of 32 - 255, this means accented characters
|
||||
(amongst others) are available.
|
||||
|
||||
The custom font file is attached to this sketch as a header file. The
|
||||
font data has been created following the instructions here:
|
||||
https://www.youtube.com/watch?v=L8MmTISmwZ8
|
||||
|
||||
Note that online converters for Adafruit_GFX compatible fonts are
|
||||
available but these typically only use characters in the range 32-127,
|
||||
and thus do not include the accented characters. These online converters
|
||||
can however still be used with this sketch but the example characters
|
||||
used must be changed.
|
||||
|
||||
The Arduino IDE uses UTF8 encoding for these characters. The TFT_eSPI
|
||||
library also expects characters in the range 128 to 255 to be UTF-8
|
||||
encoded. See link here for details:
|
||||
|
||||
https://playground.arduino.cc/Code/UTF-8
|
||||
|
||||
To summarise, UTF-8 characters are encoded as more than 1 byte so care must
|
||||
be taken:
|
||||
|
||||
char c = 'µ'; // Wrong
|
||||
char bad[4] = "5µA"; // Wrong
|
||||
char good[] = "5µA"; // Good
|
||||
String okay = "5µA"; // Good
|
||||
|
||||
Created by Bodmer 08/02/19
|
||||
|
||||
Make sure LOAD_GFXFF is defined in the used User_Setup file
|
||||
within the library folder.
|
||||
|
||||
#########################################################################
|
||||
###### DON'T FORGET TO UPDATE THE User_Setup.h FILE IN THE LIBRARY ######
|
||||
###### TO SELECT YOUR DISPLAY TYPE, PINS USED AND ENABLE FONTS ######
|
||||
#########################################################################
|
||||
*/
|
||||
|
||||
#define TEST_TEXT "ßäöü ñâàå" // Text that will be printed on screen in the font
|
||||
//#define TEST_TEXT "Hello" // Text that will be printed on screen in the font
|
||||
|
||||
#include "SPI.h"
|
||||
#include "TFT_eSPI.h"
|
||||
|
||||
// The custom font file attached to this sketch must be included
|
||||
#include "MyFont.h"
|
||||
|
||||
// Stock font and GFXFF reference handle
|
||||
#define GFXFF 1
|
||||
|
||||
// Easily remembered name for the font
|
||||
#define MYFONT32 &myFont32pt8b
|
||||
|
||||
// Use hardware SPI
|
||||
TFT_eSPI tft = TFT_eSPI();
|
||||
|
||||
void setup(void) {
|
||||
|
||||
Serial.begin(250000);
|
||||
|
||||
tft.begin();
|
||||
|
||||
tft.setRotation(1);
|
||||
|
||||
}
|
||||
|
||||
void loop() {
|
||||
|
||||
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
// Show custom fonts
|
||||
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
|
||||
// Where font sizes increase the screen is not cleared as the larger fonts overwrite
|
||||
// the smaller one with the background colour.
|
||||
|
||||
// We can set the text datum to be Top, Middle, Bottom vertically and Left, Centre
|
||||
// and Right horizontally. These are the text datums that can be used:
|
||||
// TL_DATUM = Top left (default)
|
||||
// TC_DATUM = Top centre
|
||||
// TR_DATUM = Top right
|
||||
// ML_DATUM = Middle left
|
||||
// MC_DATUM = Middle centre <<< This is used below
|
||||
// MR_DATUM = Middle right
|
||||
// BL_DATUM = Bottom left
|
||||
// BC_DATUM = Bottom centre
|
||||
// BR_DATUM = Bottom right
|
||||
// L_BASELINE = Left character baseline (Line the 'A' character would sit on)
|
||||
// C_BASELINE = Centre character baseline
|
||||
// R_BASELINE = Right character baseline
|
||||
|
||||
//Serial.println();
|
||||
|
||||
// Set text datum to middle centre (MC_DATUM)
|
||||
tft.setTextDatum(MC_DATUM);
|
||||
|
||||
// Set text colour to white with black background
|
||||
// Unlike the stock Adafruit_GFX library, the TFT_eSPI library DOES optionally draw
|
||||
// the background colour for the custom and Free Fonts when using drawString()
|
||||
tft.setTextColor(TFT_WHITE, TFT_BLACK); // White characters on black background
|
||||
//tft.setTextColor(TFT_WHITE); // or white characters, no background
|
||||
|
||||
tft.fillScreen(TFT_BLUE); // Clear screen
|
||||
tft.setFreeFont(MYFONT32); // Select the font
|
||||
tft.drawString("MyFont 32", 160, 60, GFXFF); // Print the name of the font
|
||||
tft.setFreeFont(MYFONT32); // Select the font
|
||||
tft.drawString(TEST_TEXT, 160, 140, GFXFF); // Print the test text in the custom font
|
||||
delay(2000);
|
||||
|
||||
// Setting textDatum does nothing when using tft.print
|
||||
tft.fillScreen(TFT_BLUE); // Clear screen
|
||||
tft.setCursor(0,60); // To be compatible with Adafruit_GFX the cursor datum is always bottom left
|
||||
tft.print("âäàå"); // Using tft.print means text background is NEVER rendered
|
||||
delay(2000);
|
||||
|
||||
// Reset text padding to zero (default)
|
||||
tft.setTextPadding(0);
|
||||
}
|
||||
3366
libraries/TFT_eSPI/examples/Generic/Local_Custom_Fonts/MyFont.h
Normal file
3366
libraries/TFT_eSPI/examples/Generic/Local_Custom_Fonts/MyFont.h
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,206 @@
|
|||
// Example of drawing a graphical "switch" and using
|
||||
// the touch screen to change it's state.
|
||||
|
||||
// This sketch does not use the libraries button drawing
|
||||
// and handling functions.
|
||||
|
||||
// Based on Adafruit_GFX library onoffbutton example.
|
||||
|
||||
// Touch handling for XPT2046 based screens is handled by
|
||||
// the TFT_eSPI library.
|
||||
|
||||
// Calibration data is stored in SPIFFS so we need to include it
|
||||
#include "FS.h"
|
||||
|
||||
#include <SPI.h>
|
||||
|
||||
#include <TFT_eSPI.h> // Hardware-specific library
|
||||
|
||||
TFT_eSPI tft = TFT_eSPI(); // Invoke custom library
|
||||
|
||||
// This is the file name used to store the touch coordinate
|
||||
// calibration data. Change the name to start a new calibration.
|
||||
#define CALIBRATION_FILE "/TouchCalData3"
|
||||
|
||||
// Set REPEAT_CAL to true instead of false to run calibration
|
||||
// again, otherwise it will only be done once.
|
||||
// Repeat calibration if you change the screen rotation.
|
||||
#define REPEAT_CAL false
|
||||
|
||||
bool SwitchOn = false;
|
||||
|
||||
// Comment out to stop drawing black spots
|
||||
#define BLACK_SPOT
|
||||
|
||||
// Switch position and size
|
||||
#define FRAME_X 100
|
||||
#define FRAME_Y 64
|
||||
#define FRAME_W 120
|
||||
#define FRAME_H 50
|
||||
|
||||
// Red zone size
|
||||
#define REDBUTTON_X FRAME_X
|
||||
#define REDBUTTON_Y FRAME_Y
|
||||
#define REDBUTTON_W (FRAME_W/2)
|
||||
#define REDBUTTON_H FRAME_H
|
||||
|
||||
// Green zone size
|
||||
#define GREENBUTTON_X (REDBUTTON_X + REDBUTTON_W)
|
||||
#define GREENBUTTON_Y FRAME_Y
|
||||
#define GREENBUTTON_W (FRAME_W/2)
|
||||
#define GREENBUTTON_H FRAME_H
|
||||
|
||||
//------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------------------
|
||||
void setup(void)
|
||||
{
|
||||
Serial.begin(9600);
|
||||
tft.init();
|
||||
|
||||
// Set the rotation before we calibrate
|
||||
tft.setRotation(1);
|
||||
|
||||
// call screen calibration
|
||||
touch_calibrate();
|
||||
|
||||
// clear screen
|
||||
tft.fillScreen(TFT_BLUE);
|
||||
|
||||
// Draw button (this example does not use library Button class)
|
||||
redBtn();
|
||||
}
|
||||
//------------------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------------------
|
||||
void loop()
|
||||
{
|
||||
uint16_t x, y;
|
||||
|
||||
// See if there's any touch data for us
|
||||
if (tft.getTouch(&x, &y))
|
||||
{
|
||||
// Draw a block spot to show where touch was calculated to be
|
||||
#ifdef BLACK_SPOT
|
||||
tft.fillCircle(x, y, 2, TFT_BLACK);
|
||||
#endif
|
||||
|
||||
if (SwitchOn)
|
||||
{
|
||||
if ((x > REDBUTTON_X) && (x < (REDBUTTON_X + REDBUTTON_W))) {
|
||||
if ((y > REDBUTTON_Y) && (y <= (REDBUTTON_Y + REDBUTTON_H))) {
|
||||
Serial.println("Red btn hit");
|
||||
redBtn();
|
||||
}
|
||||
}
|
||||
}
|
||||
else //Record is off (SwitchOn == false)
|
||||
{
|
||||
if ((x > GREENBUTTON_X) && (x < (GREENBUTTON_X + GREENBUTTON_W))) {
|
||||
if ((y > GREENBUTTON_Y) && (y <= (GREENBUTTON_Y + GREENBUTTON_H))) {
|
||||
Serial.println("Green btn hit");
|
||||
greenBtn();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Serial.println(SwitchOn);
|
||||
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------------------
|
||||
|
||||
void touch_calibrate()
|
||||
{
|
||||
uint16_t calData[5];
|
||||
uint8_t calDataOK = 0;
|
||||
|
||||
// check file system exists
|
||||
if (!SPIFFS.begin()) {
|
||||
Serial.println("Formatting file system");
|
||||
SPIFFS.format();
|
||||
SPIFFS.begin();
|
||||
}
|
||||
|
||||
// check if calibration file exists and size is correct
|
||||
if (SPIFFS.exists(CALIBRATION_FILE)) {
|
||||
if (REPEAT_CAL)
|
||||
{
|
||||
// Delete if we want to re-calibrate
|
||||
SPIFFS.remove(CALIBRATION_FILE);
|
||||
}
|
||||
else
|
||||
{
|
||||
File f = SPIFFS.open(CALIBRATION_FILE, "r");
|
||||
if (f) {
|
||||
if (f.readBytes((char *)calData, 14) == 14)
|
||||
calDataOK = 1;
|
||||
f.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (calDataOK && !REPEAT_CAL) {
|
||||
// calibration data valid
|
||||
tft.setTouch(calData);
|
||||
} else {
|
||||
// data not valid so recalibrate
|
||||
tft.fillScreen(TFT_BLACK);
|
||||
tft.setCursor(20, 0);
|
||||
tft.setTextFont(2);
|
||||
tft.setTextSize(1);
|
||||
tft.setTextColor(TFT_WHITE, TFT_BLACK);
|
||||
|
||||
tft.println("Touch corners as indicated");
|
||||
|
||||
tft.setTextFont(1);
|
||||
tft.println();
|
||||
|
||||
if (REPEAT_CAL) {
|
||||
tft.setTextColor(TFT_RED, TFT_BLACK);
|
||||
tft.println("Set REPEAT_CAL to false to stop this running again!");
|
||||
}
|
||||
|
||||
tft.calibrateTouch(calData, TFT_MAGENTA, TFT_BLACK, 15);
|
||||
|
||||
tft.setTextColor(TFT_GREEN, TFT_BLACK);
|
||||
tft.println("Calibration complete!");
|
||||
|
||||
// store data
|
||||
File f = SPIFFS.open(CALIBRATION_FILE, "w");
|
||||
if (f) {
|
||||
f.write((const unsigned char *)calData, 14);
|
||||
f.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void drawFrame()
|
||||
{
|
||||
tft.drawRect(FRAME_X, FRAME_Y, FRAME_W, FRAME_H, TFT_BLACK);
|
||||
}
|
||||
|
||||
// Draw a red button
|
||||
void redBtn()
|
||||
{
|
||||
tft.fillRect(REDBUTTON_X, REDBUTTON_Y, REDBUTTON_W, REDBUTTON_H, TFT_RED);
|
||||
tft.fillRect(GREENBUTTON_X, GREENBUTTON_Y, GREENBUTTON_W, GREENBUTTON_H, TFT_DARKGREY);
|
||||
drawFrame();
|
||||
tft.setTextColor(TFT_WHITE);
|
||||
tft.setTextSize(2);
|
||||
tft.setTextDatum(MC_DATUM);
|
||||
tft.drawString("ON", GREENBUTTON_X + (GREENBUTTON_W / 2), GREENBUTTON_Y + (GREENBUTTON_H / 2));
|
||||
SwitchOn = false;
|
||||
}
|
||||
|
||||
// Draw a green button
|
||||
void greenBtn()
|
||||
{
|
||||
tft.fillRect(GREENBUTTON_X, GREENBUTTON_Y, GREENBUTTON_W, GREENBUTTON_H, TFT_GREEN);
|
||||
tft.fillRect(REDBUTTON_X, REDBUTTON_Y, REDBUTTON_W, REDBUTTON_H, TFT_DARKGREY);
|
||||
drawFrame();
|
||||
tft.setTextColor(TFT_WHITE);
|
||||
tft.setTextSize(2);
|
||||
tft.setTextDatum(MC_DATUM);
|
||||
tft.drawString("OFF", REDBUTTON_X + (REDBUTTON_W / 2) + 1, REDBUTTON_Y + (REDBUTTON_H / 2));
|
||||
SwitchOn = true;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,192 @@
|
|||
/*
|
||||
The TFT_eSPI library incorporates an Adafruit_GFX compatible
|
||||
button handling class.
|
||||
|
||||
This example displays a column of buttons with varying label
|
||||
alignments.
|
||||
|
||||
The sketch has been tested on the ESP32 (which supports SPIFFS)
|
||||
|
||||
Adjust the definitions below according to your screen size
|
||||
*/
|
||||
|
||||
#include "FS.h"
|
||||
|
||||
#include <SPI.h>
|
||||
|
||||
#include <TFT_eSPI.h>
|
||||
|
||||
TFT_eSPI tft = TFT_eSPI();
|
||||
|
||||
// This is the file name used to store the calibration data
|
||||
// You can change this to create new calibration files.
|
||||
// The SPIFFS file name must start with "/".
|
||||
#define CALIBRATION_FILE "/TouchCalData1"
|
||||
|
||||
// Set REPEAT_CAL to true instead of false to run calibration
|
||||
// again, otherwise it will only be done once.
|
||||
// Repeat calibration if you change the screen rotation.
|
||||
#define REPEAT_CAL false
|
||||
|
||||
// Keypad start position, key sizes and spacing
|
||||
#define KEY_X 160 // Centre of key
|
||||
#define KEY_Y 50
|
||||
#define KEY_W 320 // Width and height
|
||||
#define KEY_H 22
|
||||
#define KEY_SPACING_X 0 // X and Y gap
|
||||
#define KEY_SPACING_Y 1
|
||||
#define KEY_TEXTSIZE 1 // Font size multiplier
|
||||
#define BUTTON_X_DELTA 22
|
||||
#define NUM_KEYS 6
|
||||
|
||||
TFT_eSPI_Button key[NUM_KEYS];
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
|
||||
tft.init();
|
||||
|
||||
// Set the rotation before we calibrate
|
||||
tft.setRotation(1);
|
||||
|
||||
// Check for backlight pin if not connected to VCC
|
||||
#ifndef TFT_BL
|
||||
Serial.println("No TFT backlight pin defined");
|
||||
#else
|
||||
pinMode(TFT_BL, OUTPUT);
|
||||
digitalWrite(TFT_BL, HIGH);
|
||||
#endif
|
||||
|
||||
// call screen calibration
|
||||
touch_calibrate();
|
||||
|
||||
// Clear screen
|
||||
tft.fillScreen(TFT_BLACK);
|
||||
|
||||
tft.setFreeFont(&FreeMono9pt7b);
|
||||
|
||||
drawButtons();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
uint16_t t_x = 0, t_y = 0; // To store the touch coordinates
|
||||
|
||||
// Get current touch state and coordinates
|
||||
bool pressed = tft.getTouch(&t_x, &t_y);
|
||||
|
||||
// Adjust press state of each key appropriately
|
||||
for (uint8_t b = 0; b < NUM_KEYS; b++) {
|
||||
if (pressed && key[b].contains(t_x, t_y))
|
||||
key[b].press(true); // tell the button it is pressed
|
||||
else
|
||||
key[b].press(false); // tell the button it is NOT pressed
|
||||
}
|
||||
|
||||
// Check if any key has changed state
|
||||
for (uint8_t b = 0; b < NUM_KEYS; b++) {
|
||||
// If button was just pressed, redraw inverted button
|
||||
if (key[b].justPressed()) {
|
||||
Serial.println("Button " + (String)b + " pressed");
|
||||
key[b].drawButton(true, "ML_DATUM + " + (String)(b * 10) + "px");
|
||||
}
|
||||
|
||||
// If button was just released, redraw normal color button
|
||||
if (key[b].justReleased()) {
|
||||
Serial.println("Button " + (String)b + " released");
|
||||
key[b].drawButton(false, "ML_DATUM + " + (String)(b * 10) + "px");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void drawButtons()
|
||||
{
|
||||
// Generate buttons with different size X deltas
|
||||
for (int i = 0; i < NUM_KEYS; i++)
|
||||
{
|
||||
key[i].initButton(&tft,
|
||||
KEY_X + 0 * (KEY_W + KEY_SPACING_X),
|
||||
KEY_Y + i * (KEY_H + KEY_SPACING_Y), // x, y, w, h, outline, fill, text
|
||||
KEY_W,
|
||||
KEY_H,
|
||||
TFT_BLACK, // Outline
|
||||
TFT_CYAN, // Fill
|
||||
TFT_BLACK, // Text
|
||||
"", // 10 Byte Label
|
||||
KEY_TEXTSIZE);
|
||||
|
||||
// Adjust button label X delta according to array position
|
||||
// setLabelDatum(uint16_t x_delta, uint16_t y_delta, uint8_t datum)
|
||||
key[i].setLabelDatum(i * 10 - (KEY_W/2), 0, ML_DATUM);
|
||||
|
||||
// Draw button and specify label string
|
||||
// Specifying label string here will allow more than the default 10 byte label
|
||||
key[i].drawButton(false, "ML_DATUM + " + (String)(i * 10) + "px");
|
||||
}
|
||||
}
|
||||
|
||||
void touch_calibrate()
|
||||
{
|
||||
uint16_t calData[5];
|
||||
uint8_t calDataOK = 0;
|
||||
|
||||
// check file system exists
|
||||
if (!SPIFFS.begin()) {
|
||||
Serial.println("Formatting file system");
|
||||
SPIFFS.format();
|
||||
SPIFFS.begin();
|
||||
}
|
||||
|
||||
// check if calibration file exists and size is correct
|
||||
if (SPIFFS.exists(CALIBRATION_FILE)) {
|
||||
if (REPEAT_CAL)
|
||||
{
|
||||
// Delete if we want to re-calibrate
|
||||
SPIFFS.remove(CALIBRATION_FILE);
|
||||
}
|
||||
else
|
||||
{
|
||||
File f = SPIFFS.open(CALIBRATION_FILE, "r");
|
||||
if (f) {
|
||||
if (f.readBytes((char *)calData, 14) == 14)
|
||||
calDataOK = 1;
|
||||
f.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (calDataOK && !REPEAT_CAL) {
|
||||
// calibration data valid
|
||||
tft.setTouch(calData);
|
||||
} else {
|
||||
// data not valid so recalibrate
|
||||
tft.fillScreen(TFT_BLACK);
|
||||
tft.setCursor(20, 0);
|
||||
tft.setTextFont(2);
|
||||
tft.setTextSize(1);
|
||||
tft.setTextColor(TFT_WHITE, TFT_BLACK);
|
||||
|
||||
tft.println("Touch corners as indicated");
|
||||
|
||||
tft.setTextFont(1);
|
||||
tft.println();
|
||||
|
||||
if (REPEAT_CAL) {
|
||||
tft.setTextColor(TFT_RED, TFT_BLACK);
|
||||
tft.println("Set REPEAT_CAL to false to stop this running again!");
|
||||
}
|
||||
|
||||
tft.calibrateTouch(calData, TFT_MAGENTA, TFT_BLACK, 15);
|
||||
|
||||
tft.setTextColor(TFT_GREEN, TFT_BLACK);
|
||||
tft.println("Calibration complete!");
|
||||
|
||||
// store data
|
||||
File f = SPIFFS.open(CALIBRATION_FILE, "w");
|
||||
if (f) {
|
||||
f.write((const unsigned char *)calData, 14);
|
||||
f.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
39
libraries/TFT_eSPI/examples/Generic/TFT_Flash_Bitmap/Alert.h
Normal file
39
libraries/TFT_eSPI/examples/Generic/TFT_Flash_Bitmap/Alert.h
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
// We need this header file to use FLASH as storage with PROGMEM directive:
|
||||
|
||||
// Icon width and height
|
||||
const uint16_t alertWidth = 32;
|
||||
const uint16_t alertHeight = 32;
|
||||
|
||||
const unsigned short alert[1024] PROGMEM={
|
||||
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0840,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 0, 32 pixels
|
||||
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x1080,0xAC66,0xEDE8,0xFE69,0xC4C6,0x2901,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 1, 64 pixels
|
||||
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0xBCC6,0xFE68,0xFE68,0xFE6A,0xFE68,0xEDE8,0x18A1,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 2, 96 pixels
|
||||
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x8344,0xFE48,0xFE8C,0xFFDD,0xFFFF,0xFEF0,0xFE48,0xB466,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 3, 128 pixels
|
||||
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x1880,0xEDC7,0xFE48,0xFF99,0xFFBC,0xFF9B,0xFFBD,0xFE6A,0xFE48,0x5A23,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 4, 160 pixels
|
||||
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x9BE5,0xFE28,0xFED0,0xFFBC,0xFF7A,0xFF9A,0xFF9B,0xFF35,0xFE28,0xBCA6,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 5, 192 pixels
|
||||
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x3962,0xFE28,0xFE28,0xFF9A,0xFF79,0xFF9A,0xFF9B,0xFF9A,0xFFBD,0xFE6B,0xFE28,0x72E3,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 6, 224 pixels
|
||||
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0xB465,0xFE28,0xFEF2,0xFF7A,0xFF79,0xFF7A,0xFF9A,0xFF7A,0xFF7A,0xFF78,0xFE28,0xDD67,0x0860,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 7, 256 pixels
|
||||
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x5A22,0xFE07,0xFE29,0xFF9B,0xFF37,0xFF58,0xFF79,0xFF79,0xFF79,0xFF58,0xFF9B,0xFEAE,0xFE07,0x93A4,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 8, 288 pixels
|
||||
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0xC4A5,0xFE07,0xFF15,0xFF37,0xFF36,0xAD11,0x2965,0x2965,0xCDF4,0xFF37,0xFF37,0xFF79,0xFE07,0xFE07,0x2901,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 9, 320 pixels
|
||||
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x7B03,0xFDE7,0xFE4B,0xFF79,0xFEF4,0xFF15,0xB552,0x2945,0x2945,0xDE55,0xFF16,0xFF15,0xFF58,0xFED1,0xFDE7,0xAC25,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 10, 352 pixels
|
||||
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0840,0xDD26,0xFDE7,0xFF57,0xFED3,0xFED2,0xFEF4,0xBD93,0x2124,0x2124,0xDE75,0xFF14,0xFED3,0xFED3,0xFF7A,0xFE08,0xFDE7,0x49A2,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 11, 384 pixels
|
||||
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x9BA4,0xFDC6,0xFE6E,0xFF36,0xFE90,0xFEB1,0xFED3,0xC592,0x2124,0x2124,0xE675,0xFED3,0xFEB2,0xFEB1,0xFEF3,0xFEF3,0xFDC6,0xBC45,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 12, 416 pixels
|
||||
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x3141,0xF5C6,0xF5C7,0xFF58,0xFE90,0xFE6F,0xFE8F,0xFEB1,0xCDB2,0x2104,0x2104,0xF6B4,0xFEB1,0xFE90,0xFE8F,0xFE90,0xFF58,0xFE0A,0xF5C6,0x72A3,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 13, 448 pixels
|
||||
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0xABE4,0xF5A6,0xFEB1,0xFED3,0xFE4E,0xFE6E,0xFE6F,0xFE90,0xD5F2,0x18E3,0x18E3,0xFED4,0xFE90,0xFE6F,0xFE6F,0xFE6E,0xFE91,0xFF36,0xF5A6,0xCCA5,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 14, 480 pixels
|
||||
0x0000,0x0000,0x0000,0x0000,0x0000,0x5202,0xF5A6,0xF5C7,0xFF58,0xFE4D,0xFE4D,0xFE4D,0xFE4E,0xFE6F,0xDE11,0x18C3,0x18C3,0xFED3,0xFE6F,0xFE6E,0xFE4E,0xFE4D,0xFE4D,0xFF16,0xFE2C,0xF5A6,0x9363,0x0000,0x0000,0x0000,0x0000,0x0000, // row 15, 512 pixels
|
||||
0x0000,0x0000,0x0000,0x0000,0x0000,0xBC44,0xF585,0xFED3,0xFE6F,0xFE2C,0xFE2C,0xFE2D,0xFE4D,0xFE4E,0xE630,0x10A2,0x2104,0xFED1,0xFE4E,0xFE4D,0xFE4D,0xFE2D,0xFE2C,0xFE4D,0xFF37,0xF586,0xF585,0x28E1,0x0000,0x0000,0x0000,0x0000, // row 16, 544 pixels
|
||||
0x0000,0x0000,0x0000,0x0000,0x7282,0xF565,0xF5EA,0xFF16,0xFE0B,0xFE0B,0xFE0B,0xFE2C,0xFE2C,0xFE4D,0xF670,0x1082,0x2924,0xFEB0,0xFE2D,0xFE2C,0xFE2C,0xFE2C,0xFE0B,0xFE0B,0xFEB2,0xFE6F,0xF565,0xA383,0x0000,0x0000,0x0000,0x0000, // row 17, 576 pixels
|
||||
0x0000,0x0000,0x0000,0x0840,0xD4C4,0xF565,0xFEF5,0xFE0C,0xFDE9,0xFDEA,0xFE0A,0xFE0B,0xFE0B,0xFE2C,0xFE8F,0x0861,0x2964,0xFE8F,0xFE2C,0xFE0B,0xFE0B,0xFE0B,0xFE0A,0xFDEA,0xFE0B,0xFF37,0xF586,0xF565,0x4181,0x0000,0x0000,0x0000, // row 18, 608 pixels
|
||||
0x0000,0x0000,0x0000,0x9343,0xF545,0xF60C,0xFED3,0xFDC8,0xFDC8,0xFDC9,0xFDE9,0xFDEA,0xFDEA,0xFE0B,0xFE8E,0x0861,0x3184,0xFE6D,0xFE0B,0xFE0A,0xFDEA,0xFDEA,0xFDE9,0xFDC9,0xFDC9,0xFE4E,0xFEB2,0xF545,0xB3E3,0x0000,0x0000,0x0000, // row 19, 640 pixels
|
||||
0x0000,0x0000,0x28E0,0xF544,0xF545,0xFF17,0xFDC8,0xFDA7,0xFDA7,0xFDC8,0xFDC8,0xFDC9,0xFDC9,0xFDE9,0xFE6C,0x10A2,0x39C4,0xFE4C,0xFDEA,0xFDE9,0xFDC9,0xFDC9,0xFDC8,0xFDC8,0xFDA7,0xFDA8,0xFF16,0xF588,0xF544,0x6222,0x0000,0x0000, // row 20, 672 pixels
|
||||
0x0000,0x0000,0xA383,0xF524,0xF64E,0xFE4E,0xFD86,0xFD86,0xFD87,0xFDA7,0xFDA7,0xFDA8,0xFDC8,0xFDC8,0xFE2A,0xA469,0xB4EA,0xFE2A,0xFDC9,0xFDC8,0xFDC8,0xFDA8,0xFDA7,0xFDA7,0xFD87,0xFD86,0xFDEA,0xFED3,0xF524,0xC443,0x0000,0x0000, // row 21, 704 pixels
|
||||
0x0000,0x51C1,0xF504,0xF546,0xFF16,0xF565,0xFD65,0xFD65,0xFD86,0xFD86,0xFD86,0xFDA7,0xFDA7,0xFDA7,0xFDE8,0xFE6A,0xFE4A,0xFDE8,0xFDA7,0xFDA7,0xFDA7,0xFDA7,0xFD86,0xFD86,0xFD86,0xFD65,0xFD65,0xFEB2,0xF5CA,0xF504,0x8AE2,0x0000, // row 22, 736 pixels
|
||||
0x0000,0xB3A2,0xED03,0xFE92,0xFDC9,0xF543,0xF544,0xFD44,0xFD65,0xFD65,0xFD65,0xFD86,0xFD86,0xFD86,0xFDA7,0xFDC7,0xFDC7,0xFDA7,0xFD86,0xFD86,0xFD86,0xFD86,0xFD65,0xFD65,0xFD65,0xFD44,0xF544,0xFD86,0xFEF5,0xED03,0xE4C3,0x1880, // row 23, 768 pixels
|
||||
0x7241,0xECE3,0xF567,0xFED3,0xF523,0xF523,0xF523,0xF543,0xF544,0xF544,0xFD65,0xFD65,0xFD65,0xFD65,0xD4E6,0x39C5,0x39A5,0xD4E6,0xFD86,0xFD65,0xFD65,0xFD65,0xFD65,0xF544,0xF544,0xF543,0xF523,0xF523,0xFE2E,0xF5EC,0xECE3,0x9B42, // row 24, 800 pixels
|
||||
0xD443,0xECE3,0xFED4,0xF565,0xF502,0xF502,0xF522,0xF523,0xF523,0xF543,0xF544,0xF544,0xF544,0xFD65,0x8B64,0x18C3,0x18C3,0x8344,0xFD85,0xFD44,0xF544,0xF544,0xF544,0xF543,0xF523,0xF523,0xF522,0xF502,0xF523,0xFEF5,0xED04,0xECE3, // row 25, 832 pixels
|
||||
0xECC3,0xF5AB,0xFE6F,0xF501,0xF4E1,0xF501,0xF502,0xF502,0xF522,0xF522,0xF523,0xF523,0xF523,0xFD84,0xC504,0x20E1,0x18E1,0xC4E4,0xFD84,0xF543,0xF523,0xF523,0xF523,0xF522,0xF522,0xF502,0xF502,0xF501,0xF501,0xFDC9,0xF62F,0xECC3, // row 26, 864 pixels
|
||||
0xECC2,0xFE92,0xF523,0xF4E0,0xF4E0,0xF4E1,0xF4E1,0xF501,0xF501,0xF502,0xF502,0xF522,0xF522,0xF543,0xFDE3,0xFEA5,0xF6A4,0xFE04,0xF543,0xF522,0xF522,0xF522,0xF502,0xF502,0xF501,0xF501,0xF4E1,0xF4E1,0xF4E0,0xF4E1,0xFED4,0xECC2, // row 27, 896 pixels
|
||||
0xECA2,0xF5EC,0xF4E0,0xF4C0,0xF4E0,0xF4E0,0xF4E0,0xF4E1,0xF4E1,0xF501,0xF501,0xF501,0xF502,0xF502,0xF542,0xFDA2,0xFDA2,0xF542,0xF502,0xF502,0xF502,0xF501,0xF501,0xF501,0xF4E1,0xF4E1,0xF4E0,0xF4E0,0xF4E0,0xF4C0,0xF5A9,0xECA2, // row 28, 928 pixels
|
||||
0xECA2,0xECA2,0xECC2,0xF4C1,0xF4C1,0xF4C1,0xF4C1,0xF4C1,0xF4C1,0xF4C1,0xF4C1,0xF4E1,0xF4E2,0xF4E2,0xF4E2,0xF4E2,0xF4E2,0xF4E2,0xF4E2,0xF4E2,0xF4E2,0xF4E1,0xF4C1,0xF4C1,0xF4C1,0xF4C1,0xF4C1,0xF4C1,0xF4C1,0xECC2,0xECC3,0xECA2, // row 29, 960 pixels
|
||||
0x8AC1,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0xEC82,0x9B01, // row 30, 992 pixels
|
||||
0x0000,0x1880,0x51A0,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x8AA1,0x61E0,0x28E0,0x0000}; // row 31, 1024 pixels
|
||||
39
libraries/TFT_eSPI/examples/Generic/TFT_Flash_Bitmap/Close.h
Normal file
39
libraries/TFT_eSPI/examples/Generic/TFT_Flash_Bitmap/Close.h
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
// We need this header file to use FLASH as storage with PROGMEM directive:
|
||||
|
||||
// Icon width and height
|
||||
const uint16_t closeWidth = 32;
|
||||
const uint16_t closeHeight = 32;
|
||||
|
||||
const unsigned short closeX[1024] PROGMEM={
|
||||
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x30C3,0x4124,0x61C7,0x61C7,0x4124,0x30E3,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 0, 32 pixels
|
||||
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x48E3,0xA249,0xEB8E,0xFCB2,0xFD14,0xFD75,0xFD96,0xFD34,0xFCF3,0xEBEF,0xA28A,0x4904,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 1, 64 pixels
|
||||
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x58E3,0xC228,0xFC10,0xFD34,0xFE18,0xFE59,0xFE79,0xFE9A,0xFE9A,0xFE9A,0xFE9A,0xFE59,0xFD75,0xFC51,0xC28A,0x5904,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 2, 96 pixels
|
||||
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x2041,0x8945,0xF34D,0xFD34,0xFDB6,0xFD75,0xFD55,0xFD55,0xFD96,0xFDD7,0xFDF7,0xFDF7,0xFDB6,0xFDB6,0xFDD7,0xFDF7,0xFD75,0xF38E,0x8965,0x2041,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 3, 128 pixels
|
||||
0x0000,0x0000,0x0000,0x0000,0x0000,0x4082,0xE208,0xF410,0xFD34,0xFC92,0xFBEF,0xFBAE,0xFBEF,0xFC71,0xFD14,0xFD75,0xFDB6,0xFD75,0xFD14,0xFC92,0xFC51,0xFC71,0xFCF3,0xFD75,0xFC30,0xEA28,0x40A2,0x0000,0x0000,0x0000,0x0000,0x0000, // row 4, 160 pixels
|
||||
0x0000,0x0000,0x0000,0x0000,0x3861,0xE1E7,0xF451,0xFC92,0xFB4D,0xFA49,0xFA49,0xFAEB,0xFBAE,0xFC71,0xFD34,0xFDB6,0xFE18,0xFDB6,0xFD34,0xFC71,0xFBAE,0xFB0C,0xFAEB,0xFBAE,0xFCD3,0xFC71,0xE208,0x4082,0x0000,0x0000,0x0000,0x0000, // row 5, 192 pixels
|
||||
0x0000,0x0000,0x0000,0x1020,0xD986,0xF430,0xFC30,0xFA28,0xF924,0xF965,0xFA8A,0xFB0C,0xFBAE,0xFC51,0xFD14,0xFD75,0xFDB6,0xFD75,0xFD14,0xFC51,0xFC71,0xFBEF,0xFA28,0xF9C7,0xFA8A,0xFC51,0xF430,0xD9A6,0x1020,0x0000,0x0000,0x0000, // row 6, 224 pixels
|
||||
0x0000,0x0000,0x0000,0x78A2,0xEB6D,0xFC30,0xF9C7,0xF861,0xF8A2,0xFA08,0xFEDB,0xFD55,0xFB4D,0xFC10,0xFC92,0xFD14,0xFD34,0xFD14,0xFC92,0xFCB2,0xFF7D,0xFF7D,0xFB2C,0xF945,0xF8E3,0xF9E7,0xFC30,0xEB8E,0x78C3,0x0000,0x0000,0x0000, // row 7, 256 pixels
|
||||
0x0000,0x0000,0x3841,0xD9E7,0xF492,0xF208,0xF041,0xF800,0xF945,0xFE9A,0xFFFF,0xFFFF,0xFD75,0xFB8E,0xFC10,0xFC51,0xFC71,0xFC51,0xFCB2,0xFF7D,0xFFFF,0xFFFF,0xFF3C,0xFA8A,0xF882,0xF841,0xFA08,0xFC92,0xDA08,0x3841,0x0000,0x0000, // row 8, 288 pixels
|
||||
0x0000,0x0000,0x88A2,0xEBCF,0xF2EB,0xF061,0xF000,0xF8E3,0xFE79,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFD75,0xFB4D,0xFBAE,0xFBAE,0xFC71,0xFF7D,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFEFB,0xFA28,0xF800,0xF061,0xF2EB,0xEBEF,0x90C3,0x0000,0x0000, // row 9, 320 pixels
|
||||
0x0000,0x2820,0xD1C7,0xF410,0xE945,0xE800,0xF000,0xFE9A,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFD34,0xFAEB,0xFBCF,0xFF5D,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFF1C,0xF986,0xF000,0xF145,0xF410,0xD1E7,0x2820,0x0000, // row 10, 352 pixels
|
||||
0x0000,0x6841,0xDB2C,0xEACB,0xE041,0xE800,0xF000,0xFEFB,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFD14,0xFF1C,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFBCF,0xF082,0xF000,0xE841,0xEACB,0xE34D,0x7061,0x0000, // row 11, 384 pixels
|
||||
0x0000,0x9861,0xE3CF,0xE186,0xE000,0xE800,0xE800,0xF145,0xFEDB,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFB8E,0xF000,0xF000,0xE800,0xE800,0xE986,0xEBCF,0xA082,0x0000, // row 12, 416 pixels
|
||||
0x0800,0xB8A2,0xE3AE,0xD8A2,0xD800,0xE000,0xE800,0xE800,0xF145,0xFEFB,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFB8E,0xF000,0xF000,0xE800,0xE800,0xE000,0xE0A2,0xEBAE,0xC0C3,0x0800, // row 13, 448 pixels
|
||||
0x1800,0xC124,0xE30C,0xD020,0xD800,0xE000,0xE000,0xE800,0xE800,0xF145,0xFEDB,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFB8E,0xF000,0xF000,0xE800,0xE800,0xE000,0xE000,0xD820,0xE30C,0xC124,0x1800, // row 14, 480 pixels
|
||||
0x2800,0xC165,0xDAAA,0xC800,0xD000,0xD800,0xE000,0xE000,0xE800,0xE800,0xF124,0xFE79,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFB6D,0xF000,0xF000,0xE800,0xE800,0xE000,0xE000,0xD800,0xD000,0xDAAA,0xC165,0x2800, // row 15, 512 pixels
|
||||
0x2000,0xB924,0xD269,0xC800,0xD000,0xD000,0xD800,0xE000,0xE000,0xE800,0xE924,0xFEFB,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xF36D,0xE800,0xE800,0xE800,0xE000,0xE000,0xD800,0xD000,0xD000,0xDA69,0xC145,0x2800, // row 16, 544 pixels
|
||||
0x1000,0xB0A2,0xD28A,0xC000,0xC800,0xD000,0xD000,0xD800,0xD800,0xE165,0xFEFB,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xF3AE,0xE000,0xE000,0xD800,0xD800,0xD000,0xD000,0xC800,0xD28A,0xB8C3,0x1000, // row 17, 576 pixels
|
||||
0x0000,0xA800,0xD2AA,0xB800,0xC000,0xC800,0xC800,0xD000,0xD965,0xFEFB,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xEBAE,0xD800,0xD800,0xD000,0xC800,0xC800,0xC000,0xD2AA,0xB020,0x0000, // row 18, 608 pixels
|
||||
0x0000,0x8000,0xCA69,0xB841,0xB800,0xC000,0xC800,0xD186,0xFEFB,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xEBCF,0xD000,0xC800,0xC800,0xC000,0xC041,0xCA69,0x8000,0x0000, // row 19, 640 pixels
|
||||
0x0000,0x4800,0xC1C7,0xB8E3,0xB800,0xB800,0xC000,0xF69A,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xEBEF,0xFE79,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xE410,0xC841,0xC000,0xB800,0xC0E3,0xC1C7,0x4800,0x0000, // row 20, 672 pixels
|
||||
0x0000,0x1000,0xB061,0xC1E7,0xB000,0xB000,0xB800,0xD269,0xFFBE,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xE38E,0xD000,0xD965,0xF69A,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xDB0C,0xC020,0xB800,0xB000,0xC1E7,0xB061,0x1000,0x0000, // row 21, 704 pixels
|
||||
0x0000,0x0000,0x6000,0xB9C7,0xB061,0xB000,0xB000,0xB800,0xCA49,0xFF9E,0xFFFF,0xFFFF,0xFFFF,0xE38E,0xC800,0xC800,0xC800,0xD186,0xF69A,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xDB0C,0xB800,0xB800,0xB000,0xB061,0xC1C7,0x6000,0x0000,0x0000, // row 22, 736 pixels
|
||||
0x0000,0x0000,0x1800,0xB041,0xB986,0xA800,0xA800,0xB000,0xB000,0xCA49,0xFF7D,0xFFFF,0xDB8E,0xC000,0xC000,0xC000,0xC000,0xC000,0xC986,0xF6DB,0xFFFF,0xFFFF,0xD30C,0xB800,0xB000,0xB000,0xA800,0xB986,0xB041,0x1800,0x0000,0x0000, // row 23, 768 pixels
|
||||
0x0000,0x0000,0x0000,0x5800,0xB0E3,0xA8C3,0xA800,0xA800,0xA800,0xB000,0xCACB,0xD38E,0xB000,0xB800,0xB800,0xB800,0xB800,0xB800,0xB800,0xC145,0xF6DB,0xD34D,0xB000,0xB000,0xA800,0xA800,0xB0C3,0xB0E3,0x5800,0x0000,0x0000,0x0000, // row 24, 800 pixels
|
||||
0x0000,0x0000,0x0000,0x0000,0x6000,0xB124,0xA882,0xA000,0xA800,0xA800,0xA800,0xA800,0xB000,0xB000,0xB000,0xB000,0xB000,0xB000,0xB000,0xB000,0xB000,0xA800,0xA800,0xA800,0xA800,0xA882,0xB124,0x6000,0x0000,0x0000,0x0000,0x0000, // row 25, 832 pixels
|
||||
0x0000,0x0000,0x0000,0x0000,0x0000,0x6000,0xB104,0xA882,0xA000,0xA000,0xA000,0xA800,0xA800,0xA800,0xA800,0xA800,0xA800,0xA800,0xA800,0xA800,0xA800,0xA800,0xA000,0xA000,0xA882,0xB104,0x6000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 26, 864 pixels
|
||||
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x6000,0xB0A2,0xA8C3,0xA020,0xA000,0xA000,0xA000,0xA000,0xA000,0xA000,0xA000,0xA000,0xA000,0xA000,0xA000,0xA000,0xA020,0xA8C3,0xB0A2,0x6000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 27, 896 pixels
|
||||
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x4800,0xA800,0xB0C3,0xA0A2,0x9800,0x9800,0x9800,0x9800,0xA000,0xA000,0xA000,0x9800,0x9800,0x9800,0xA082,0xB0E3,0xA800,0x4800,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 28, 928 pixels
|
||||
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x5800,0xA800,0xB0A2,0xA8E3,0xA0A2,0xA041,0x9800,0x9800,0xA041,0xA0A2,0xA8E3,0xB0A2,0xA800,0x5800,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 29, 960 pixels
|
||||
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x3000,0x6000,0x8800,0xA000,0xA800,0xA800,0xA000,0x8800,0x6000,0x3000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 30, 992 pixels
|
||||
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000}; // row 31, 1024 pixels
|
||||
39
libraries/TFT_eSPI/examples/Generic/TFT_Flash_Bitmap/Info.h
Normal file
39
libraries/TFT_eSPI/examples/Generic/TFT_Flash_Bitmap/Info.h
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
// We need this header file to use FLASH as storage with PROGMEM directive:
|
||||
|
||||
// Icon width and height
|
||||
const uint16_t infoWidth = 32;
|
||||
const uint16_t infoHeight = 32;
|
||||
|
||||
const unsigned short info[1024] PROGMEM={
|
||||
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 0, 32 pixels
|
||||
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0861,0x4A69,0x8C71,0xA514,0xBDF7,0xBDF7,0xA514,0x8C71,0x4A69,0x0861,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 1, 64 pixels
|
||||
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x39E7,0x9CF3,0xEF7D,0xF79E,0xFFDF,0xFFDF,0xFFDF,0xFFDF,0xFFDF,0xFFDF,0xF79E,0xEF7D,0x9CF3,0x39E7,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 2, 96 pixels
|
||||
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x2965,0x9492,0xF79E,0xFFDF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFDF,0xF79E,0x9492,0x2965,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 3, 128 pixels
|
||||
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x630C,0xEF7D,0xFFDF,0xFFFF,0xFFFF,0xFFFF,0xD75F,0xB6BF,0x9E5F,0x963F,0x963F,0x9E5F,0xB6BF,0xD75F,0xFFFF,0xFFFF,0xFFFF,0xFFDF,0xEF7D,0x630C,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 4, 160 pixels
|
||||
0x0000,0x0000,0x0000,0x0000,0x0000,0x73AE,0xEF7D,0xFFDF,0xFFFF,0xFFDF,0xBEDF,0x7DBF,0x7DBF,0x7DDF,0x7DDF,0x7DDF,0x7DDF,0x7DDF,0x7DBF,0x759F,0x7DBE,0xBEBF,0xFFDF,0xFFFF,0xFFDF,0xEF7D,0x73AE,0x0000,0x0000,0x0000,0x0000,0x0000, // row 5, 192 pixels
|
||||
0x0000,0x0000,0x0000,0x0000,0x630C,0xEF7D,0xFFFF,0xFFFF,0xE77F,0x7DBE,0x759E,0x759F,0x7DBF,0x7DDF,0x7DDF,0x85FF,0x7DDF,0x7DDF,0x7DBF,0x759F,0x759E,0x6D7E,0x7DBE,0xDF7F,0xFFFF,0xFFFF,0xEF7D,0x630C,0x0000,0x0000,0x0000,0x0000, // row 6, 224 pixels
|
||||
0x0000,0x0000,0x0000,0x31A6,0xEF5D,0xFFDF,0xFFFF,0xCF1E,0x6D7E,0x6D7E,0x759E,0x759F,0x7DBF,0x7DDF,0x8E1F,0xBEDF,0xC6FF,0x8DFF,0x75BF,0x759F,0x759E,0x6D7E,0x655E,0x655D,0xCF1E,0xFFFF,0xFFDF,0xEF5D,0x31A6,0x0000,0x0000,0x0000, // row 7, 256 pixels
|
||||
0x0000,0x0000,0x0000,0x94B2,0xF7BE,0xFFFF,0xDF5E,0x655D,0x655D,0x6D7E,0x6D7E,0x759E,0x75BF,0x759F,0xEFBF,0xFFFF,0xFFFF,0xEFBF,0x759F,0x759E,0x6D7E,0x6D7E,0x655D,0x653D,0x653D,0xDF5E,0xFFFF,0xF7BE,0x94B2,0x0000,0x0000,0x0000, // row 8, 288 pixels
|
||||
0x0000,0x0000,0x4228,0xEF7D,0xFFFF,0xF7BF,0x6D5D,0x653D,0x655D,0x6D5E,0x6D7E,0x759E,0x759E,0x85DF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0x8DFE,0x6D7E,0x6D7E,0x6D5E,0x655D,0x653D,0x5D1D,0x6D5D,0xF7BF,0xFFFF,0xEF7D,0x4228,0x0000,0x0000, // row 9, 320 pixels
|
||||
0x0000,0x0000,0xA534,0xFFDF,0xFFDF,0xA65D,0x5D1D,0x5D1D,0x653D,0x655E,0x6D7E,0x6D7E,0x6D7E,0x651E,0xE77F,0xFFFF,0xFFFF,0xF7BF,0x5CFE,0x6D7E,0x6D7E,0x655E,0x653D,0x5D1D,0x5D1D,0x54FC,0xA65D,0xFFDF,0xFFDF,0xA534,0x0000,0x0000, // row 10, 352 pixels
|
||||
0x0000,0x18E3,0xEF5D,0xFFFF,0xEF9E,0x5CFC,0x54FC,0x5D1D,0x5D3D,0x653D,0x655E,0x6D7E,0x6D7E,0x653E,0x6D3E,0xB67E,0xBEBE,0x755E,0x5D1E,0x6D5E,0x655E,0x653D,0x5D3D,0x5D1D,0x54FC,0x54DC,0x54FC,0xEF9E,0xFFFF,0xEF5D,0x18E3,0x0000, // row 11, 384 pixels
|
||||
0x0000,0x630C,0xEF7D,0xFFDF,0xB69D,0x54DC,0x54FC,0x5CFC,0x5D1D,0x653D,0x653D,0x655E,0x6D5E,0x655E,0x5CFE,0x4C9D,0x4C7D,0x54DD,0x653E,0x655E,0x653D,0x653D,0x5D1D,0x5CFC,0x54FC,0x54DC,0x4CBC,0xB69D,0xFFDF,0xEF7D,0x630C,0x0000, // row 12, 416 pixels
|
||||
0x0000,0x94B2,0xF7BE,0xFFDF,0x85BC,0x4CBC,0x54DC,0x54FC,0x5CFD,0x5D1D,0x5D3D,0x653D,0x655D,0x653D,0x85DE,0xC6FE,0xC6FE,0x85BE,0x653D,0x653D,0x5D3D,0x5D1D,0x5CFD,0x54FC,0x54DC,0x4CBC,0x4CBB,0x85BC,0xFFDF,0xF7BE,0x94B2,0x0000, // row 13, 448 pixels
|
||||
0x0000,0xB5B6,0xFFDF,0xF7BE,0x651C,0x4CBB,0x4CBC,0x54DC,0x54FC,0x5CFC,0x5D1D,0x5D1D,0x653D,0x5D1D,0xE77E,0xFFDF,0xFFDF,0xEF9E,0x5CFD,0x5D1D,0x5D1D,0x5CFC,0x54FC,0x54DC,0x4CBC,0x4CBB,0x449B,0x651B,0xF7BE,0xFFDF,0xB5B6,0x0000, // row 14, 480 pixels
|
||||
0x0000,0xC638,0xFFDF,0xF7BE,0x54DB,0x449B,0x4CBB,0x4CBC,0x54DC,0x54FC,0x54FC,0x5D1D,0x5D1D,0x7D7D,0xF7BE,0xF7BE,0xF7BE,0xF7BE,0x7D7D,0x5CFD,0x54FC,0x54FC,0x54DC,0x4CBC,0x4CBB,0x449B,0x447B,0x54BB,0xF7BE,0xFFDF,0xC638,0x0000, // row 15, 512 pixels
|
||||
0x0000,0xC638,0xFFDF,0xF79E,0x4CBB,0x449B,0x449B,0x4CBB,0x4CBC,0x54DC,0x54DC,0x54FC,0x54DC,0x753C,0xF7BE,0xF7BE,0xF7BE,0xF7BE,0x753C,0x54DC,0x54DC,0x54DC,0x4CBC,0x4CBB,0x449B,0x449B,0x3C7B,0x4C9B,0xF79E,0xFFDF,0xC638,0x0000, // row 16, 544 pixels
|
||||
0x0000,0xB5B6,0xFFDF,0xF7BE,0x5CFB,0x3C7B,0x447B,0x449B,0x4CBB,0x4CBC,0x4CBC,0x4CDC,0x4CBC,0x6D1C,0xF7BE,0xF7BE,0xF7BE,0xF7BE,0x6CFC,0x4CBC,0x4CBC,0x4CBC,0x4CBB,0x449B,0x447B,0x3C7B,0x3C5A,0x54DB,0xF7BE,0xFFDF,0xB5B6,0x0000, // row 17, 576 pixels
|
||||
0x0000,0x94B2,0xF7BE,0xF7BE,0x755B,0x3C5A,0x3C7B,0x447B,0x449B,0x449B,0x4CBB,0x4CBB,0x4C9B,0x6CFB,0xF79E,0xF79E,0xF79E,0xF79E,0x64FB,0x449B,0x4CBB,0x449B,0x449B,0x447B,0x3C7B,0x3C5A,0x3C5A,0x753B,0xF7BE,0xF7BE,0x9CD3,0x0000, // row 18, 608 pixels
|
||||
0x0000,0x6B4D,0xEF7D,0xF7BE,0xA61C,0x3C5A,0x3C5A,0x3C7B,0x447B,0x447B,0x449B,0x449B,0x447B,0x64DB,0xF79E,0xF79E,0xF79E,0xF79E,0x64DB,0x447B,0x449B,0x447B,0x447B,0x3C7B,0x3C5A,0x3C5A,0x343A,0xA61C,0xF7BE,0xEF7D,0x6B4D,0x0000, // row 19, 640 pixels
|
||||
0x0000,0x2124,0xE71C,0xFFDF,0xDF3D,0x3C5A,0x343A,0x3C5A,0x3C5A,0x3C7B,0x3C7B,0x447B,0x3C5B,0x64BA,0xF79E,0xF79E,0xF79E,0xF79E,0x64BA,0x3C5B,0x3C7B,0x3C7B,0x3C5A,0x3C5A,0x343A,0x343A,0x343A,0xDF3D,0xFFDF,0xE71C,0x2124,0x0000, // row 20, 672 pixels
|
||||
0x0000,0x0000,0xAD75,0xF7BE,0xF79E,0x859B,0x343A,0x343A,0x345A,0x3C5A,0x3C5A,0x3C5A,0x3C5A,0x5C9A,0xEF7D,0xEF7D,0xEF7D,0xEF7D,0x5C9A,0x3C3A,0x3C5A,0x3C5A,0x345A,0x343A,0x343A,0x341A,0x859B,0xF79E,0xF7BE,0xAD75,0x0000,0x0000, // row 21, 704 pixels
|
||||
0x0000,0x0000,0x528A,0xE71C,0xFFDF,0xDF3D,0x3C5A,0x343A,0x343A,0x343A,0x343A,0x3C5A,0x343A,0x4C5A,0xEF7D,0xEF7D,0xEF7D,0xEF7D,0x4C59,0x343A,0x343A,0x343A,0x343A,0x343A,0x341A,0x3C5A,0xDF3D,0xFFDF,0xE71C,0x528A,0x0000,0x0000, // row 22, 736 pixels
|
||||
0x0000,0x0000,0x0000,0x9CD3,0xF79E,0xF7BE,0xBE7C,0x3419,0x341A,0x341A,0x343A,0x343A,0x341A,0x2B99,0xC69C,0xEF7D,0xEF7D,0xD6DC,0x2398,0x341A,0x343A,0x341A,0x341A,0x2C19,0x2C19,0xBE7C,0xF7BE,0xF79E,0x9CD3,0x0000,0x0000,0x0000, // row 23, 768 pixels
|
||||
0x0000,0x0000,0x0000,0x39E7,0xDEDB,0xFFDF,0xF79E,0x9DFB,0x2C19,0x2C19,0x2C1A,0x341A,0x341A,0x2BB9,0x2B57,0x6459,0x74B9,0x2337,0x2BB9,0x341A,0x2C1A,0x2C19,0x2C19,0x2C19,0x9DFB,0xF79E,0xFFDF,0xDEDB,0x39E7,0x0000,0x0000,0x0000, // row 24, 800 pixels
|
||||
0x0000,0x0000,0x0000,0x0000,0x632C,0xDEFB,0xFFDF,0xEF7D,0xB65C,0x3C39,0x2BF9,0x2C19,0x2C19,0x2BF9,0x2398,0x1B58,0x1B37,0x2398,0x2BF9,0x2C19,0x2BF9,0x2BF9,0x3439,0xB65C,0xEF7D,0xFFDF,0xDEFB,0x632C,0x0000,0x0000,0x0000,0x0000, // row 25, 832 pixels
|
||||
0x0000,0x0000,0x0000,0x0000,0x0000,0x73AE,0xDEFB,0xF7BE,0xF79E,0xDF1C,0x7D5A,0x2BF9,0x2BF9,0x2BF9,0x2BF9,0x23D9,0x23D9,0x2BF9,0x2BF9,0x2BF9,0x2BF9,0x7D5A,0xDF1C,0xF79E,0xF7BE,0xDEFB,0x73AE,0x0000,0x0000,0x0000,0x0000,0x0000, // row 26, 864 pixels
|
||||
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x632C,0xDEDB,0xF79E,0xFFDF,0xEF7D,0xD6FC,0x9DFB,0x5CDA,0x4C9A,0x3419,0x3419,0x4C9A,0x5CDA,0x9DFB,0xD6FC,0xEF7D,0xFFDF,0xF79E,0xDEDB,0x632C,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 27, 896 pixels
|
||||
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x4208,0x94B2,0xDEFB,0xF7BE,0xFFDF,0xF7BE,0xF79E,0xEF7D,0xEF5D,0xEF5D,0xEF7D,0xF79E,0xF7BE,0xFFDF,0xF7BE,0xDEFB,0x94B2,0x4208,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 28, 928 pixels
|
||||
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x528A,0xA534,0xDEDB,0xE73C,0xF79E,0xF7BE,0xF7BE,0xF7BE,0xF7BE,0xF79E,0xE73C,0xDEDB,0xA534,0x528A,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 29, 960 pixels
|
||||
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x18C3,0x5AEB,0x8C71,0xAD55,0xBDD7,0xBDD7,0xAD55,0x8C71,0x5AEB,0x18C3,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // row 30, 992 pixels
|
||||
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000}; // row 31, 1024 pixels
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
// Icon images are stored in tabs ^ e.g. Alert.h etc.above this line
|
||||
// more than one icon can be in a header file
|
||||
|
||||
// Arrays containing FLASH images can be created with UTFT library tool:
|
||||
// (libraries\UTFT\Tools\ImageConverter565.exe)
|
||||
// Convert to .c format then copy into a new tab
|
||||
|
||||
/*
|
||||
This sketch demonstrates loading images from arrays stored in program (FLASH) memory.
|
||||
|
||||
Works with TFT_eSPI library here:
|
||||
https://github.com/Bodmer/TFT_eSPI
|
||||
|
||||
This sketch does not use/need any fonts at all...
|
||||
|
||||
Code derived from ILI9341_due library example
|
||||
|
||||
Make sure all the display driver and pin connections are correct by
|
||||
editing the User_Setup.h file in the TFT_eSPI library folder.
|
||||
|
||||
#########################################################################
|
||||
###### DON'T FORGET TO UPDATE THE User_Setup.h FILE IN THE LIBRARY ######
|
||||
#########################################################################
|
||||
*/
|
||||
|
||||
#include <TFT_eSPI.h> // Hardware-specific library
|
||||
|
||||
TFT_eSPI tft = TFT_eSPI(); // Invoke custom library
|
||||
|
||||
// Include the header files that contain the icons
|
||||
#include "Alert.h"
|
||||
#include "Close.h"
|
||||
#include "Info.h"
|
||||
|
||||
long count = 0; // Loop count
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
tft.begin();
|
||||
tft.setRotation(1); // landscape
|
||||
|
||||
tft.fillScreen(TFT_BLACK);
|
||||
|
||||
// Swap the colour byte order when rendering
|
||||
tft.setSwapBytes(true);
|
||||
|
||||
// Draw the icons
|
||||
tft.pushImage(100, 100, infoWidth, infoHeight, info);
|
||||
tft.pushImage(140, 100, alertWidth, alertHeight, alert);
|
||||
tft.pushImage(180, 100, closeWidth, closeHeight, closeX);
|
||||
|
||||
// Pause here to admire the icons!
|
||||
delay(2000);
|
||||
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
// Loop filling and clearing screen
|
||||
tft.pushImage(random(tft.width() - infoWidth), random(tft.height() - infoHeight), infoWidth, infoHeight, info);
|
||||
tft.pushImage(random(tft.width() - alertWidth), random(tft.height() - alertHeight), alertWidth, alertHeight, alert);
|
||||
tft.pushImage(random(tft.width() - closeWidth), random(tft.height() - closeHeight), alertWidth, closeHeight, closeX);
|
||||
|
||||
// Clear screen after 100 x 3 = 300 icons drawn
|
||||
if (1000 == count++) {
|
||||
count = 1;
|
||||
tft.setRotation(2 * random(2)); // Rotate randomly to clear display left>right or right>left to reduce monotony!
|
||||
tft.fillScreen(TFT_BLACK);
|
||||
tft.setRotation(1);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
// Bodmer's BMP image rendering function
|
||||
|
||||
void drawBmp(const char *filename, int16_t x, int16_t y) {
|
||||
|
||||
if ((x >= tft.width()) || (y >= tft.height())) return;
|
||||
|
||||
fs::File bmpFS;
|
||||
|
||||
// Open requested file on SD card
|
||||
bmpFS = SPIFFS.open(filename, "r");
|
||||
|
||||
if (!bmpFS)
|
||||
{
|
||||
Serial.print("File not found");
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t seekOffset;
|
||||
uint16_t w, h, row, col;
|
||||
uint8_t r, g, b;
|
||||
|
||||
uint32_t startTime = millis();
|
||||
|
||||
if (read16(bmpFS) == 0x4D42)
|
||||
{
|
||||
read32(bmpFS);
|
||||
read32(bmpFS);
|
||||
seekOffset = read32(bmpFS);
|
||||
read32(bmpFS);
|
||||
w = read32(bmpFS);
|
||||
h = read32(bmpFS);
|
||||
|
||||
if ((read16(bmpFS) == 1) && (read16(bmpFS) == 24) && (read32(bmpFS) == 0))
|
||||
{
|
||||
y += h - 1;
|
||||
|
||||
bool oldSwapBytes = tft.getSwapBytes();
|
||||
tft.setSwapBytes(true);
|
||||
bmpFS.seek(seekOffset);
|
||||
|
||||
uint16_t padding = (4 - ((w * 3) & 3)) & 3;
|
||||
uint8_t lineBuffer[w * 3 + padding];
|
||||
|
||||
for (row = 0; row < h; row++) {
|
||||
|
||||
bmpFS.read(lineBuffer, sizeof(lineBuffer));
|
||||
uint8_t* bptr = lineBuffer;
|
||||
uint16_t* tptr = (uint16_t*)lineBuffer;
|
||||
// Convert 24 to 16-bit colours
|
||||
for (uint16_t col = 0; col < w; col++)
|
||||
{
|
||||
b = *bptr++;
|
||||
g = *bptr++;
|
||||
r = *bptr++;
|
||||
*tptr++ = ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3);
|
||||
}
|
||||
|
||||
// Push the pixel row to screen, pushImage will crop the line if needed
|
||||
// y is decremented as the BMP image is drawn bottom up
|
||||
tft.pushImage(x, y--, w, 1, (uint16_t*)lineBuffer);
|
||||
}
|
||||
tft.setSwapBytes(oldSwapBytes);
|
||||
Serial.print("Loaded in "); Serial.print(millis() - startTime);
|
||||
Serial.println(" ms");
|
||||
}
|
||||
else Serial.println("BMP format not recognized.");
|
||||
}
|
||||
bmpFS.close();
|
||||
}
|
||||
|
||||
// These read 16- and 32-bit types from the SD card file.
|
||||
// BMP data is stored little-endian, Arduino is little-endian too.
|
||||
// May need to reverse subscript order if porting elsewhere.
|
||||
|
||||
uint16_t read16(fs::File &f) {
|
||||
uint16_t result;
|
||||
((uint8_t *)&result)[0] = f.read(); // LSB
|
||||
((uint8_t *)&result)[1] = f.read(); // MSB
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t read32(fs::File &f) {
|
||||
uint32_t result;
|
||||
((uint8_t *)&result)[0] = f.read(); // LSB
|
||||
((uint8_t *)&result)[1] = f.read();
|
||||
((uint8_t *)&result)[2] = f.read();
|
||||
((uint8_t *)&result)[3] = f.read(); // MSB
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
// This sketch draws BMP images pulled from SPIFFS onto the TFT. It is an
|
||||
// an example from this library: https://github.com/Bodmer/TFT_eSPI
|
||||
|
||||
// Images in SPIFFS must be put in the root folder (top level) to be found
|
||||
// Use the SPIFFS library example to verify SPIFFS works!
|
||||
|
||||
// The example image used to test this sketch can be found in the sketch
|
||||
// Data folder, press Ctrl+K to see this folder. Use the IDE "Tools" menu
|
||||
// option to upload the sketches data folder to the SPIFFS
|
||||
|
||||
// This sketch has been tested on the ESP32 and ESP8266
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
|
||||
//====================================================================================
|
||||
// Libraries
|
||||
//====================================================================================
|
||||
// Call up the SPIFFS FLASH filing system this is part of the ESP Core
|
||||
#define FS_NO_GLOBALS
|
||||
#include <FS.h>
|
||||
|
||||
#ifdef ESP32
|
||||
#include "SPIFFS.h" // For ESP32 only
|
||||
#endif
|
||||
|
||||
// Call up the TFT library
|
||||
#include <TFT_eSPI.h> // Hardware-specific library for ESP8266
|
||||
|
||||
// Invoke TFT library
|
||||
TFT_eSPI tft = TFT_eSPI();
|
||||
|
||||
//====================================================================================
|
||||
// Setup
|
||||
//====================================================================================
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
|
||||
if (!SPIFFS.begin()) {
|
||||
Serial.println("SPIFFS initialisation failed!");
|
||||
while (1) yield(); // Stay here twiddling thumbs waiting
|
||||
}
|
||||
Serial.println("\r\nSPIFFS initialised.");
|
||||
|
||||
// Now initialise the TFT
|
||||
tft.begin();
|
||||
tft.setRotation(0); // 0 & 2 Portrait. 1 & 3 landscape
|
||||
tft.fillScreen(TFT_BLACK);
|
||||
}
|
||||
|
||||
//====================================================================================
|
||||
// Loop
|
||||
//====================================================================================
|
||||
void loop()
|
||||
{
|
||||
int x = random(tft.width() - 128);
|
||||
int y = random(tft.height() - 160);
|
||||
|
||||
drawBmp("/parrot.bmp", x, y);
|
||||
|
||||
delay(1000);
|
||||
}
|
||||
//====================================================================================
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 60 KiB |
|
|
@ -0,0 +1,207 @@
|
|||
/*
|
||||
This sketch has been written to test the Processing screenshot client.
|
||||
|
||||
It has been created to work with the TFT_eSPI library here:
|
||||
https://github.com/Bodmer/TFT_eSPI
|
||||
|
||||
It sends screenshots to a PC running a Processing client sketch.
|
||||
|
||||
The Processing IDE that will run the client sketch can be downloaded
|
||||
here: https://processing.org/
|
||||
|
||||
The Processing sketch needed is contained within a tab attached to this
|
||||
Arduino sketch. Cut and paste that tab into the Processing IDE and run.
|
||||
Read the Processing sketch header for instructions.
|
||||
|
||||
This sketch uses the GLCD, 2, 4, 6 fonts only.
|
||||
|
||||
Make sure all the display driver and pin connections are correct by
|
||||
editing the User_Setup.h file in the TFT_eSPI library folder.
|
||||
|
||||
Maximum recommended SPI clock rate is 27MHz when reading pixels, 40MHz
|
||||
seems to be OK with ILI9341 displays but this is above the manufacturers
|
||||
specified maximum clock rate.
|
||||
|
||||
In the setup file you can define different write and read SPI clock rates
|
||||
|
||||
In the setup file you can define TFT_SDA_READ for a TFT with bi-directional
|
||||
SDA pin (otherwise the normal MISO pin will be used to read from the TFT)
|
||||
|
||||
>>>> NOTE: NOT ALL TFTs SUPPORT READING THE CGRAM (pixel) MEMORY <<<<
|
||||
|
||||
#########################################################################
|
||||
###### DON'T FORGET TO UPDATE THE User_Setup.h FILE IN THE LIBRARY ######
|
||||
#########################################################################
|
||||
*/
|
||||
|
||||
// Created by: Bodmer 5/3/17
|
||||
// Updated by: Bodmer 10/3/17
|
||||
// Updated by: Bodmer 23/11/18 to support SDA reads and the ESP32
|
||||
// Version: 0.07
|
||||
|
||||
// MIT licence applies, all text above must be included in derivative works
|
||||
|
||||
#include <TFT_eSPI.h> // Hardware-specific library
|
||||
#include <SPI.h>
|
||||
|
||||
TFT_eSPI tft = TFT_eSPI(); // Invoke custom library
|
||||
|
||||
unsigned long targetTime = 0;
|
||||
byte red = 0x1F;
|
||||
byte green = 0;
|
||||
byte blue = 0;
|
||||
byte state = 0;
|
||||
unsigned int colour = red << 11; // Colour order is RGB 5+6+5 bits each
|
||||
|
||||
void setup(void) {
|
||||
Serial.begin(921600); // Set to a high rate for fast image transfer to a PC
|
||||
|
||||
tft.init();
|
||||
tft.setRotation(0);
|
||||
tft.fillScreen(TFT_BLACK);
|
||||
|
||||
randomSeed(analogRead(A0));
|
||||
|
||||
targetTime = millis() + 1000;
|
||||
}
|
||||
|
||||
#define RGB_TEST false // true produces a simple RGB color test screen
|
||||
|
||||
void loop() {
|
||||
|
||||
if (targetTime < millis()) {
|
||||
if (!RGB_TEST)
|
||||
{
|
||||
targetTime = millis() + 1500; // Wait a minimum of 1.5s
|
||||
|
||||
tft.setRotation(random(4));
|
||||
rainbow_fill(); // Fill the screen with rainbow colours
|
||||
|
||||
tft.setTextColor(TFT_BLACK); // Text background is not defined so it is transparent
|
||||
tft.setTextDatum(TC_DATUM); // Top Centre datum
|
||||
int xpos = tft.width() / 2; // Centre of screen
|
||||
|
||||
tft.setTextFont(0); // Select font 0 which is the Adafruit font
|
||||
tft.drawString("Original Adafruit font!", xpos, 5);
|
||||
|
||||
// The new larger fonts do not need to use the .setCursor call, coords are embedded
|
||||
tft.setTextColor(TFT_BLACK); // Do not plot the background colour
|
||||
|
||||
// Overlay the black text on top of the rainbow plot (the advantage of not drawing the background colour!)
|
||||
tft.drawString("Font size 2", xpos, 14, 2); // Draw text centre at position xpos, 14 using font 2
|
||||
tft.drawString("Font size 4", xpos, 30, 4); // Draw text centre at position xpos, 30 using font 4
|
||||
tft.drawString("12.34", xpos, 54, 6); // Draw text centre at position xpos, 54 using font 6
|
||||
|
||||
tft.drawString("12.34 is in font size 6", xpos, 92, 2); // Draw text centre at position xpos, 92 using font 2
|
||||
// Note the x position is the top of the font!
|
||||
|
||||
// draw a floating point number
|
||||
float pi = 3.1415926; // Value to print
|
||||
int precision = 3; // Number of digits after decimal point
|
||||
|
||||
int ypos = 110; // y position
|
||||
|
||||
tft.setTextDatum(TR_DATUM); // Top Right datum so text butts neatly to xpos (right justified)
|
||||
|
||||
tft.drawFloat(pi, precision, xpos, ypos, 2); // Draw rounded number and return new xpos delta for next print position
|
||||
|
||||
tft.setTextDatum(TL_DATUM); // Top Left datum so text butts neatly to xpos (left justified)
|
||||
|
||||
tft.drawString(" is pi", xpos, ypos, 2);
|
||||
|
||||
tft.setTextSize(1); // We are using a font size multiplier of 1
|
||||
tft.setTextDatum(TC_DATUM); // Top Centre datum
|
||||
tft.setTextColor(TFT_BLACK); // Set text colour to black, no background (so transparent)
|
||||
|
||||
tft.drawString("Transparent...", xpos, 125, 4); // Font 4
|
||||
|
||||
tft.setTextColor(TFT_WHITE, TFT_BLACK); // Set text colour to white and background to black
|
||||
tft.drawString("White on black", xpos, 150, 4); // Font 4
|
||||
|
||||
tft.setTextColor(TFT_GREEN, TFT_BLACK); // This time we will use green text on a black background
|
||||
|
||||
tft.setTextFont(2); // Select font 2, now we do not need to specify the font in drawString()
|
||||
|
||||
// An easier way to position text and blank old text is to set the datum and use width padding
|
||||
tft.setTextDatum(BC_DATUM); // Bottom centre for text datum
|
||||
tft.setTextPadding(tft.width() + 1); // Pad text to full screen width + 1 spare for +/-1 position rounding
|
||||
|
||||
tft.drawString("Ode to a Small Lump of Green Putty", xpos, 230 - 32);
|
||||
tft.drawString("I Found in My Armpit One Midsummer", xpos, 230 - 16);
|
||||
tft.drawString("Morning", xpos, 230);
|
||||
|
||||
tft.setTextDatum(TL_DATUM); // Reset to top left for text datum
|
||||
tft.setTextPadding(0); // Reset text padding to 0 pixels
|
||||
|
||||
// Now call the screen server to send a copy of the TFT screen to the PC running the Processing client sketch
|
||||
screenServer();
|
||||
}
|
||||
else
|
||||
{
|
||||
tft.fillScreen(TFT_BLACK);
|
||||
tft.fillRect( 0, 0, 16, 16, TFT_RED);
|
||||
tft.fillRect(16, 0, 16, 16, TFT_GREEN);
|
||||
tft.fillRect(32, 0, 16, 16, TFT_BLUE);
|
||||
screenServer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fill screen with a rainbow pattern
|
||||
void rainbow_fill()
|
||||
{
|
||||
// The colours and state are not initialised so the start colour changes each time the function is called
|
||||
int rotation = tft.getRotation();
|
||||
tft.setRotation(random(4));
|
||||
for (int i = tft.height() - 1; i >= 0; i--) {
|
||||
// This is a "state machine" that ramps up/down the colour brightnesses in sequence
|
||||
switch (state) {
|
||||
case 0:
|
||||
green ++;
|
||||
if (green == 64) {
|
||||
green = 63;
|
||||
state = 1;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
red--;
|
||||
if (red == 255) {
|
||||
red = 0;
|
||||
state = 2;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
blue ++;
|
||||
if (blue == 32) {
|
||||
blue = 31;
|
||||
state = 3;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
green --;
|
||||
if (green == 255) {
|
||||
green = 0;
|
||||
state = 4;
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
red ++;
|
||||
if (red == 32) {
|
||||
red = 31;
|
||||
state = 5;
|
||||
}
|
||||
break;
|
||||
case 5:
|
||||
blue --;
|
||||
if (blue == 255) {
|
||||
blue = 0;
|
||||
state = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
colour = red << 11 | green << 5 | blue;
|
||||
// Draw a line 1 pixel wide in the selected colour
|
||||
tft.drawFastHLine(0, i, tft.width(), colour); // tft.width() returns the pixel width of the display
|
||||
}
|
||||
tft.setRotation(rotation);
|
||||
}
|
||||
|
|
@ -0,0 +1,535 @@
|
|||
// This is a copy of the processing sketch that can be used to capture the images
|
||||
// Copy the sketch below and remove the /* and */ at the beginning and end.
|
||||
|
||||
// The sketch runs in Processing version 3.3 on a PC, it can be downloaded here:
|
||||
// https://processing.org/download/
|
||||
|
||||
/*
|
||||
|
||||
// This is a Processing sketch, see https://processing.org/ to download the IDE
|
||||
|
||||
// The sketch is a client that requests TFT screenshots from an Arduino board.
|
||||
// The Arduino must call a screenshot server function to respond with pixels.
|
||||
|
||||
// It has been created to work with the TFT_eSPI library here:
|
||||
// https://github.com/Bodmer/TFT_eSPI
|
||||
|
||||
// The sketch must only be run when the designated serial port is available and enumerated
|
||||
// otherwise the screenshot window may freeze and that process will need to be terminated
|
||||
// This is a limitation of the Processing environment and not the sketch.
|
||||
// If anyone knows how to determine if a serial port is available at start up the PM me
|
||||
// on (Bodmer) the Arduino forum.
|
||||
|
||||
// The block below contains variables that the user may need to change for a particular setup
|
||||
// As a minimum set the serial port and baud rate must be defined. The capture window is
|
||||
// automatically resized for landscape, portrait and different TFT resolutions.
|
||||
|
||||
// Captured images are stored in the sketch folder, use the Processing IDE "Sketch" menu
|
||||
// option "Show Sketch Folder" or press Ctrl+K
|
||||
|
||||
// Created by: Bodmer 5/3/17
|
||||
// Updated by: Bodmer 12/3/17
|
||||
// Version: 0.07
|
||||
|
||||
// MIT licence applies, all text above must be included in derivative works
|
||||
|
||||
|
||||
// ###########################################################################################
|
||||
// # These are the values to change for a particular setup #
|
||||
// #
|
||||
int serial_port = 0; // Use enumerated value from list provided when sketch is run #
|
||||
// #
|
||||
// On an Arduino Due Programming Port use a baud rate of:115200) #
|
||||
// On an Arduino Due Native USB Port use a baud rate of any value #
|
||||
int serial_baud_rate = 921600; // #
|
||||
// #
|
||||
// Change the image file type saved here, comment out all but one #
|
||||
//String image_type = ".jpg"; // #
|
||||
String image_type = ".png"; // Lossless compression #
|
||||
//String image_type = ".bmp"; // #
|
||||
//String image_type = ".tif"; // #
|
||||
// #
|
||||
boolean save_border = true; // Save the image with a border #
|
||||
int border = 5; // Border pixel width #
|
||||
boolean fade = false; // Fade out image after saving #
|
||||
// #
|
||||
int max_images = 100; // Maximum of numbered file images before over-writing files #
|
||||
// #
|
||||
int max_allowed = 1000; // Maximum number of save images allowed before a restart #
|
||||
// #
|
||||
// # End of the values to change for a particular setup #
|
||||
// ###########################################################################################
|
||||
|
||||
// These are default values, this sketch obtains the actual values from the Arduino board
|
||||
int tft_width = 480; // default TFT width (automatic - sent by Arduino)
|
||||
int tft_height = 480; // default TFT height (automatic - sent by Arduino)
|
||||
int color_bytes = 2; // 2 for 16-bit, 3 for three RGB bytes (automatic - sent by Arduino)
|
||||
|
||||
import processing.serial.*;
|
||||
|
||||
Serial serial; // Create an instance called serial
|
||||
|
||||
int serialCount = 0; // Count of colour bytes arriving
|
||||
|
||||
// Stage window graded background colours
|
||||
color bgcolor1 = color(0, 100, 104); // Arduino IDE style background color 1
|
||||
color bgcolor2 = color(77, 183, 187); // Arduino IDE style background color 2
|
||||
//color bgcolor2 = color(255, 255, 255); // White
|
||||
|
||||
// TFT image frame greyscale value (dark grey)
|
||||
color frameColor = 42;
|
||||
|
||||
color buttonStopped = color(255, 0, 0);
|
||||
color buttonRunning = color(128, 204, 206);
|
||||
color buttonDimmed = color(180, 0, 0);
|
||||
boolean dimmed = false;
|
||||
boolean running = true;
|
||||
boolean mouseClick = false;
|
||||
|
||||
int[] rgb = new int[3]; // Buffer for the colour bytes
|
||||
int indexRed = 0; // Colour byte index in the array
|
||||
int indexGreen = 1;
|
||||
int indexBlue = 2;
|
||||
|
||||
int n = 0;
|
||||
|
||||
int x_offset = (500 - tft_width) /2; // Image offsets in the window
|
||||
int y_offset = 20;
|
||||
|
||||
int xpos = 0, ypos = 0; // Current pixel position
|
||||
|
||||
int beginTime = 0;
|
||||
int pixelWaitTime = 1000; // Maximum 1000ms wait for image pixels to arrive
|
||||
int lastPixelTime = 0; // Time that "image send" command was sent
|
||||
|
||||
int requestTime = 0;
|
||||
int requestCount = 0;
|
||||
|
||||
int state = 0; // State machine current state
|
||||
|
||||
int progress_bar = 0; // Console progress bar dot count
|
||||
int pixel_count = 0; // Number of pixels read for 1 screen
|
||||
float percentage = 0; // Percentage of pixels received
|
||||
|
||||
int saved_image_count = 0; // Stats - number of images processed
|
||||
int bad_image_count = 0; // Stats - number of images that had lost pixels
|
||||
String filename = "";
|
||||
|
||||
int drawLoopCount = 0; // Used for the fade out
|
||||
|
||||
void setup() {
|
||||
|
||||
size(500, 540); // Stage size, can handle 480 pixels wide screen
|
||||
noStroke(); // No border on the next thing drawn
|
||||
noSmooth(); // No anti-aliasing to avoid adjacent pixel colour merging
|
||||
|
||||
// Graded background and title
|
||||
drawWindow();
|
||||
|
||||
frameRate(2000); // High frame rate so draw() loops fast
|
||||
|
||||
// Print a list of the available serial ports
|
||||
println("-----------------------");
|
||||
println("Available Serial Ports:");
|
||||
println("-----------------------");
|
||||
printArray(Serial.list());
|
||||
println("-----------------------");
|
||||
|
||||
print("Port currently used: [");
|
||||
print(serial_port);
|
||||
println("]");
|
||||
|
||||
String portName = Serial.list()[serial_port];
|
||||
|
||||
serial = new Serial(this, portName, serial_baud_rate);
|
||||
|
||||
state = 99;
|
||||
}
|
||||
|
||||
void draw() {
|
||||
|
||||
if (mouseClick) buttonClicked();
|
||||
|
||||
switch(state) {
|
||||
|
||||
case 0: // Init varaibles, send start request
|
||||
if (running) {
|
||||
tint(0, 0, 0, 255);
|
||||
flushBuffer();
|
||||
println("");
|
||||
print("Ready: ");
|
||||
|
||||
xpos = 0;
|
||||
ypos = 0;
|
||||
serialCount = 0;
|
||||
progress_bar = 0;
|
||||
pixel_count = 0;
|
||||
percentage = 0;
|
||||
drawLoopCount = frameCount;
|
||||
lastPixelTime = millis() + 1000;
|
||||
|
||||
state = 1;
|
||||
} else {
|
||||
if (millis() > beginTime) {
|
||||
beginTime = millis() + 500;
|
||||
dimmed = !dimmed;
|
||||
if (dimmed) drawButton(buttonDimmed);
|
||||
else drawButton(buttonStopped);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 1: // Console message, give server some time
|
||||
print("requesting image ");
|
||||
serial.write("S");
|
||||
delay(10);
|
||||
beginTime = millis();
|
||||
requestTime = millis() + 1000;
|
||||
requestCount = 1;
|
||||
state = 2;
|
||||
break;
|
||||
|
||||
case 2: // Get size and set start time for rendering duration report
|
||||
if (millis() > requestTime) {
|
||||
requestCount++;
|
||||
print("*");
|
||||
serial.clear();
|
||||
serial.write("S");
|
||||
if (requestCount > 32) {
|
||||
requestCount = 0;
|
||||
System.err.println(" - no response!");
|
||||
state = 0;
|
||||
}
|
||||
requestTime = millis() + 1000;
|
||||
}
|
||||
if ( getSize() == true ) { // Go to next state when we have the size and bits per pixel
|
||||
getFilename();
|
||||
flushBuffer(); // Precaution in case image header size increases in later versions
|
||||
lastPixelTime = millis() + 1000;
|
||||
beginTime = millis();
|
||||
state = 3;
|
||||
}
|
||||
break;
|
||||
|
||||
case 3: // Request pixels and render returned RGB values
|
||||
state = renderPixels(); // State will change when all pixels are rendered
|
||||
|
||||
// Request more pixels, changing the number requested allows the average transfer rate to be controlled
|
||||
// The pixel transfer rate is dependant on four things:
|
||||
// 1. The frame rate defined in this Processing sketch in setup()
|
||||
// 2. The baud rate of the serial link (~10 bit periods per byte)
|
||||
// 3. The number of request bytes 'R' sent in the lines below
|
||||
// 4. The number of pixels sent in a burst by the server sketch (defined via NPIXELS)
|
||||
|
||||
//serial.write("RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR"); // 32 x NPIXELS more
|
||||
serial.write("RRRRRRRRRRRRRRRR"); // 16 x NPIXELS more
|
||||
//serial.write("RRRRRRRR"); // 8 x NPIXELS more
|
||||
//serial.write("RRRR"); // 4 x NPIXELS more
|
||||
//serial.write("RR"); // 2 x NPIXELS more
|
||||
//serial.write("R"); // 1 x NPIXELS more
|
||||
if (!running) state = 4;
|
||||
break;
|
||||
|
||||
case 4: // Pixel receive time-out, flush serial buffer
|
||||
flushBuffer();
|
||||
state = 6;
|
||||
break;
|
||||
|
||||
case 5: // Save the image to the sketch folder (Ctrl+K to access)
|
||||
saveScreenshot();
|
||||
saved_image_count++;
|
||||
println("Saved image count = " + saved_image_count);
|
||||
if (bad_image_count > 0) System.err.println(" Bad image count = " + bad_image_count);
|
||||
drawLoopCount = frameCount; // Reset value ready for counting in step 6
|
||||
state = 6;
|
||||
break;
|
||||
|
||||
case 6: // Fade the old image if enabled
|
||||
if ( fadedImage() == true ) state = 0; // Go to next state when image has faded
|
||||
break;
|
||||
|
||||
case 99: // Draw image viewer window
|
||||
drawWindow();
|
||||
delay(50); // Delay here seems to be required for the IDE console to get ready
|
||||
state = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
println("");
|
||||
System.err.println("Error state reached - check sketch!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void drawWindow()
|
||||
{
|
||||
// Graded background in Arduino colours
|
||||
for (int i = 0; i < height - 25; i++) {
|
||||
float inter = map(i, 0, height - 25, 0, 1);
|
||||
color c = lerpColor(bgcolor1, bgcolor2, inter);
|
||||
stroke(c);
|
||||
line(0, i, 500, i);
|
||||
}
|
||||
fill(bgcolor2);
|
||||
rect( 0, height-25, width-1, 24);
|
||||
textAlign(CENTER);
|
||||
textSize(20);
|
||||
fill(0);
|
||||
text("Bodmer's TFT image viewer", width/2, height-6);
|
||||
|
||||
if (running) drawButton(buttonRunning);
|
||||
else drawButton(buttonStopped);
|
||||
}
|
||||
|
||||
void flushBuffer()
|
||||
{
|
||||
//println("Clearing serial pipe after a time-out");
|
||||
int clearTime = millis() + 50;
|
||||
while ( millis() < clearTime ) serial.clear();
|
||||
}
|
||||
|
||||
boolean getSize()
|
||||
{
|
||||
if ( serial.available() > 6 ) {
|
||||
println();
|
||||
char code = (char)serial.read();
|
||||
if (code == 'W') {
|
||||
tft_width = serial.read()<<8 | serial.read();
|
||||
}
|
||||
code = (char)serial.read();
|
||||
if (code == 'H') {
|
||||
tft_height = serial.read()<<8 | serial.read();
|
||||
}
|
||||
code = (char)serial.read();
|
||||
if (code == 'Y') {
|
||||
int bits_per_pixel = (char)serial.read();
|
||||
if (bits_per_pixel == 24) color_bytes = 3;
|
||||
else color_bytes = 2;
|
||||
}
|
||||
code = (char)serial.read();
|
||||
if (code == '?') {
|
||||
drawWindow();
|
||||
|
||||
x_offset = (500 - tft_width) /2;
|
||||
tint(0, 0, 0, 255);
|
||||
noStroke();
|
||||
fill(frameColor);
|
||||
rect((width - tft_width)/2 - border, y_offset - border, tft_width + 2 * border, tft_height + 2 * border);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void saveScreenshot()
|
||||
{
|
||||
println();
|
||||
if (saved_image_count < max_allowed)
|
||||
{
|
||||
if (filename == "") filename = "tft_screen_" + (n++);
|
||||
filename = filename + image_type;
|
||||
println("Saving image as \"" + filename + "\"");
|
||||
if (save_border)
|
||||
{
|
||||
PImage partialSave = get(x_offset - border, y_offset - border, tft_width + 2*border, tft_height + 2*border);
|
||||
partialSave.save(filename);
|
||||
} else {
|
||||
PImage partialSave = get(x_offset, y_offset, tft_width, tft_height);
|
||||
partialSave.save(filename);
|
||||
}
|
||||
|
||||
if (n>=max_images) n = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
System.err.println(max_allowed + " saved image count exceeded, restart the sketch");
|
||||
}
|
||||
}
|
||||
|
||||
void getFilename()
|
||||
{
|
||||
int readTime = millis() + 20;
|
||||
int inByte = 0;
|
||||
filename = "";
|
||||
while ( serial.available() > 0 && millis() < readTime && inByte != '.')
|
||||
{
|
||||
inByte = serial.read();
|
||||
if (inByte == ' ') inByte = '_';
|
||||
if ( unicodeCheck(inByte) ) filename += (char)inByte;
|
||||
}
|
||||
|
||||
inByte = serial.read();
|
||||
if (inByte == '@') filename += "_" + timeCode();
|
||||
else if (inByte == '#') filename += "_" + saved_image_count%100;
|
||||
else if (inByte == '%') filename += "_" + millis();
|
||||
else if (inByte != '*') filename = "";
|
||||
|
||||
inByte = serial.read();
|
||||
if (inByte == 'j') image_type =".jpg";
|
||||
else if (inByte == 'b') image_type =".bmp";
|
||||
else if (inByte == 'p') image_type =".png";
|
||||
else if (inByte == 't') image_type =".tif";
|
||||
}
|
||||
|
||||
boolean unicodeCheck(int unicode)
|
||||
{
|
||||
if ( unicode >= '0' && unicode <= '9' ) return true;
|
||||
if ( (unicode >= 'A' && unicode <= 'Z' ) || (unicode >= 'a' && unicode <= 'z')) return true;
|
||||
if ( unicode == '_' || unicode == '/' ) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
String timeCode()
|
||||
{
|
||||
String timeCode = (int)year() + "_" + (int)month() + "_" + (int)day() + "_";
|
||||
timeCode += (int)hour() + "_" + (int)minute() + "_" + (int)second();
|
||||
return timeCode;
|
||||
}
|
||||
|
||||
int renderPixels()
|
||||
{
|
||||
if ( serial.available() > 0 ) {
|
||||
|
||||
// Add the latest byte from the serial port to array:
|
||||
while (serial.available()>0)
|
||||
{
|
||||
rgb[serialCount++] = serial.read();
|
||||
|
||||
// If we have 3 colour bytes:
|
||||
if ( serialCount >= color_bytes ) {
|
||||
serialCount = 0;
|
||||
pixel_count++;
|
||||
if (color_bytes == 3)
|
||||
{
|
||||
stroke(rgb[indexRed], rgb[indexGreen], rgb[indexBlue], 1000);
|
||||
} else
|
||||
{ // Can cater for various byte orders
|
||||
//stroke( (rgb[0] & 0x1F)<<3, (rgb[0] & 0xE0)>>3 | (rgb[1] & 0x07)<<5, (rgb[1] & 0xF8));
|
||||
//stroke( (rgb[1] & 0x1F)<<3, (rgb[1] & 0xE0)>>3 | (rgb[0] & 0x07)<<5, (rgb[0] & 0xF8));
|
||||
stroke( (rgb[0] & 0xF8), (rgb[1] & 0xE0)>>3 | (rgb[0] & 0x07)<<5, (rgb[1] & 0x1F)<<3);
|
||||
//stroke( (rgb[1] & 0xF8), (rgb[0] & 0xE0)>>3 | (rgb[1] & 0x07)<<5, (rgb[0] & 0x1F)<<3);
|
||||
}
|
||||
// We get some pixel merge aliasing if smooth() is defined, so draw pixel twice
|
||||
point(xpos + x_offset, ypos + y_offset);
|
||||
//point(xpos + x_offset, ypos + y_offset);
|
||||
|
||||
lastPixelTime = millis();
|
||||
xpos++;
|
||||
if (xpos >= tft_width) {
|
||||
xpos = 0;
|
||||
progressBar();
|
||||
ypos++;
|
||||
if (ypos>=tft_height) {
|
||||
ypos = 0;
|
||||
if ((int)percentage <100) {
|
||||
while (progress_bar++ < 64) print(" ");
|
||||
percent(100);
|
||||
}
|
||||
println("Image fetch time = " + (millis()-beginTime)/1000.0 + " s");
|
||||
return 5;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else
|
||||
{
|
||||
if (millis() > (lastPixelTime + pixelWaitTime))
|
||||
{
|
||||
println("");
|
||||
System.err.println(pixelWaitTime + "ms time-out for pixels exceeded...");
|
||||
if (pixel_count > 0) {
|
||||
bad_image_count++;
|
||||
System.err.print("Pixels missing = " + (tft_width * tft_height - pixel_count));
|
||||
System.err.println(", corrupted image not saved");
|
||||
System.err.println("Good image count = " + saved_image_count);
|
||||
System.err.println(" Bad image count = " + bad_image_count);
|
||||
}
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
return 3;
|
||||
}
|
||||
|
||||
void progressBar()
|
||||
{
|
||||
progress_bar++;
|
||||
print(".");
|
||||
if (progress_bar >63)
|
||||
{
|
||||
progress_bar = 0;
|
||||
percentage = 0.5 + 100 * pixel_count/(0.001 + tft_width * tft_height);
|
||||
percent(percentage);
|
||||
}
|
||||
}
|
||||
|
||||
void percent(float percentage)
|
||||
{
|
||||
if (percentage > 100) percentage = 100;
|
||||
println(" [ " + (int)percentage + "% ]");
|
||||
textAlign(LEFT);
|
||||
textSize(16);
|
||||
noStroke();
|
||||
fill(bgcolor2);
|
||||
rect(10, height - 25, 70, 20);
|
||||
fill(0);
|
||||
text(" [ " + (int)percentage + "% ]", 10, height-8);
|
||||
}
|
||||
|
||||
boolean fadedImage()
|
||||
{
|
||||
int opacity = frameCount - drawLoopCount; // So we get increasing fade
|
||||
if (fade)
|
||||
{
|
||||
tint(255, opacity);
|
||||
//image(tft_img, x_offset, y_offset);
|
||||
noStroke();
|
||||
fill(50, 50, 50, opacity);
|
||||
rect( (width - tft_width)/2, y_offset, tft_width, tft_height);
|
||||
delay(10);
|
||||
}
|
||||
if (opacity > 50) // End fade after 50 cycles
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void drawButton(color buttonColor)
|
||||
{
|
||||
stroke(0);
|
||||
fill(buttonColor);
|
||||
rect(500 - 100, 540 - 26, 80, 24);
|
||||
textAlign(CENTER);
|
||||
textSize(20);
|
||||
fill(0);
|
||||
if (running) text(" Pause ", 500 - 60, height-7);
|
||||
else text(" Run ", 500 - 60, height-7);
|
||||
}
|
||||
|
||||
void buttonClicked()
|
||||
{
|
||||
mouseClick = false;
|
||||
if (running) {
|
||||
running = false;
|
||||
drawButton(buttonStopped);
|
||||
System.err.println("");
|
||||
System.err.println("Stopped - click 'Run' button: ");
|
||||
//noStroke();
|
||||
//fill(50);
|
||||
//rect( (width - tft_width)/2, y_offset, tft_width, tft_height);
|
||||
beginTime = millis() + 500;
|
||||
dimmed = false;
|
||||
state = 4;
|
||||
} else {
|
||||
running = true;
|
||||
drawButton(buttonRunning);
|
||||
}
|
||||
}
|
||||
|
||||
void mousePressed() {
|
||||
if (mouseX > (500 - 100) && mouseX < (500 - 20) && mouseY > (540 - 26) && mouseY < (540 - 2)) {
|
||||
mouseClick = true;
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
|
|
@ -0,0 +1,196 @@
|
|||
// Reads a screen image off the TFT and send it to a processing client sketch
|
||||
// over the serial port. Use a high baud rate, e.g. for an ESP8266:
|
||||
// Serial.begin(921600);
|
||||
|
||||
// At 921600 baud a 320 x 240 image with 16-bit colour transfers can be sent to the
|
||||
// PC client in ~1.67s and 24-bit colour in ~2.5s which is close to the theoretical
|
||||
// minimum transfer time.
|
||||
|
||||
// This sketch has been created to work with the TFT_eSPI library here:
|
||||
// https://github.com/Bodmer/TFT_eSPI
|
||||
|
||||
// Created by: Bodmer 27/1/17
|
||||
// Updated by: Bodmer 10/3/17
|
||||
// Updated by: Bodmer 23/11/18 to support SDA reads and the ESP32
|
||||
// Version: 0.08
|
||||
|
||||
// MIT licence applies, all text above must be included in derivative works
|
||||
|
||||
//====================================================================================
|
||||
// Definitions
|
||||
//====================================================================================
|
||||
#define PIXEL_TIMEOUT 100 // 100ms Time-out between pixel requests
|
||||
#define START_TIMEOUT 10000 // 10s Maximum time to wait at start transfer
|
||||
|
||||
#define BITS_PER_PIXEL 16 // 24 for RGB colour format, 16 for 565 colour format
|
||||
|
||||
// File names must be alpha-numeric characters (0-9, a-z, A-Z) or "/" underscore "_"
|
||||
// other ascii characters are stripped out by client, including / generates
|
||||
// sub-directories
|
||||
#define DEFAULT_FILENAME "tft_screenshots/screenshot" // In case none is specified
|
||||
#define FILE_TYPE "png" // jpg, bmp, png, tif are valid
|
||||
|
||||
// Filename extension
|
||||
// '#' = add incrementing number, '@' = add timestamp, '%' add millis() timestamp,
|
||||
// '*' = add nothing
|
||||
// '@' and '%' will generate new unique filenames, so beware of cluttering up your
|
||||
// hard drive with lots of images! The PC client sketch is set to limit the number of
|
||||
// saved images to 1000 and will then prompt for a restart.
|
||||
#define FILE_EXT '@'
|
||||
|
||||
// Number of pixels to send in a burst (minimum of 1), no benefit above 8
|
||||
// NPIXELS values and render times:
|
||||
// NPIXELS 1 = use readPixel() = >5s and 16-bit pixels only
|
||||
// NPIXELS >1 using rectRead() 2 = 1.75s, 4 = 1.68s, 8 = 1.67s
|
||||
#define NPIXELS 8 // Must be integer division of both TFT width and TFT height
|
||||
|
||||
//====================================================================================
|
||||
// Screen server call with no filename
|
||||
//====================================================================================
|
||||
// Start a screen dump server (serial or network) - no filename specified
|
||||
bool screenServer(void)
|
||||
{
|
||||
// With no filename the screenshot will be saved with a default name e.g. tft_screen_#.xxx
|
||||
// where # is a number 0-9 and xxx is a file type specified below
|
||||
return screenServer(DEFAULT_FILENAME);
|
||||
}
|
||||
|
||||
//====================================================================================
|
||||
// Screen server call with filename
|
||||
//====================================================================================
|
||||
// Start a screen dump server (serial or network) - filename specified
|
||||
bool screenServer(String filename)
|
||||
{
|
||||
delay(0); // Equivalent to yield() for ESP8266;
|
||||
|
||||
bool result = serialScreenServer(filename); // Screenshot serial port server
|
||||
//bool result = wifiScreenServer(filename); // Screenshot WiFi UDP port server (WIP)
|
||||
|
||||
delay(0); // Equivalent to yield()
|
||||
|
||||
//Serial.println();
|
||||
//if (result) Serial.println(F("Screen dump passed :-)"));
|
||||
//else Serial.println(F("Screen dump failed :-("));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//====================================================================================
|
||||
// Serial server function that sends the data to the client
|
||||
//====================================================================================
|
||||
bool serialScreenServer(String filename)
|
||||
{
|
||||
// Precautionary receive buffer garbage flush for 50ms
|
||||
uint32_t clearTime = millis() + 50;
|
||||
while ( millis() < clearTime && Serial.read() >= 0) delay(0); // Equivalent to yield() for ESP8266;
|
||||
|
||||
bool wait = true;
|
||||
uint32_t lastCmdTime = millis(); // Initialise start of command time-out
|
||||
|
||||
// Wait for the starting flag with a start time-out
|
||||
while (wait)
|
||||
{
|
||||
delay(0); // Equivalent to yield() for ESP8266;
|
||||
// Check serial buffer
|
||||
if (Serial.available() > 0) {
|
||||
// Read the command byte
|
||||
uint8_t cmd = Serial.read();
|
||||
// If it is 'S' (start command) then clear the serial buffer for 100ms and stop waiting
|
||||
if ( cmd == 'S' ) {
|
||||
// Precautionary receive buffer garbage flush for 50ms
|
||||
clearTime = millis() + 50;
|
||||
while ( millis() < clearTime && Serial.read() >= 0) delay(0); // Equivalent to yield() for ESP8266;
|
||||
|
||||
wait = false; // No need to wait anymore
|
||||
lastCmdTime = millis(); // Set last received command time
|
||||
|
||||
// Send screen size etc.using a simple header with delimiters for client checks
|
||||
sendParameters(filename);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Check for time-out
|
||||
if ( millis() > lastCmdTime + START_TIMEOUT) return false;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t color[3 * NPIXELS]; // RGB and 565 format color buffer for N pixels
|
||||
|
||||
// Send all the pixels on the whole screen
|
||||
for ( uint32_t y = 0; y < tft.height(); y++)
|
||||
{
|
||||
// Increment x by NPIXELS as we send NPIXELS for every byte received
|
||||
for ( uint32_t x = 0; x < tft.width(); x += NPIXELS)
|
||||
{
|
||||
delay(0); // Equivalent to yield() for ESP8266;
|
||||
|
||||
// Wait here for serial data to arrive or a time-out elapses
|
||||
while ( Serial.available() == 0 )
|
||||
{
|
||||
if ( millis() > lastCmdTime + PIXEL_TIMEOUT) return false;
|
||||
delay(0); // Equivalent to yield() for ESP8266;
|
||||
}
|
||||
|
||||
// Serial data must be available to get here, read 1 byte and
|
||||
// respond with N pixels, i.e. N x 3 RGB bytes or N x 2 565 format bytes
|
||||
if ( Serial.read() == 'X' ) {
|
||||
// X command byte means abort, so clear the buffer and return
|
||||
clearTime = millis() + 50;
|
||||
while ( millis() < clearTime && Serial.read() >= 0) delay(0); // Equivalent to yield() for ESP8266;
|
||||
return false;
|
||||
}
|
||||
// Save arrival time of the read command (for later time-out check)
|
||||
lastCmdTime = millis();
|
||||
|
||||
#if defined BITS_PER_PIXEL && BITS_PER_PIXEL >= 24 && NPIXELS > 1
|
||||
// Fetch N RGB pixels from x,y and put in buffer
|
||||
tft.readRectRGB(x, y, NPIXELS, 1, color);
|
||||
// Send buffer to client
|
||||
Serial.write(color, 3 * NPIXELS); // Write all pixels in the buffer
|
||||
#else
|
||||
// Fetch N 565 format pixels from x,y and put in buffer
|
||||
if (NPIXELS > 1) tft.readRect(x, y, NPIXELS, 1, (uint16_t *)color);
|
||||
else
|
||||
{
|
||||
uint16_t c = tft.readPixel(x, y);
|
||||
color[0] = c>>8;
|
||||
color[1] = c & 0xFF; // Swap bytes
|
||||
}
|
||||
// Send buffer to client
|
||||
Serial.write(color, 2 * NPIXELS); // Write all pixels in the buffer
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
Serial.flush(); // Make sure all pixel bytes have been despatched
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//====================================================================================
|
||||
// Send screen size etc.using a simple header with delimiters for client checks
|
||||
//====================================================================================
|
||||
void sendParameters(String filename)
|
||||
{
|
||||
Serial.write('W'); // Width
|
||||
Serial.write(tft.width() >> 8);
|
||||
Serial.write(tft.width() & 0xFF);
|
||||
|
||||
Serial.write('H'); // Height
|
||||
Serial.write(tft.height() >> 8);
|
||||
Serial.write(tft.height() & 0xFF);
|
||||
|
||||
Serial.write('Y'); // Bits per pixel (16 or 24)
|
||||
if (NPIXELS > 1) Serial.write(BITS_PER_PIXEL);
|
||||
else Serial.write(16); // readPixel() only provides 16-bit values
|
||||
|
||||
Serial.write('?'); // Filename next
|
||||
Serial.print(filename);
|
||||
|
||||
Serial.write('.'); // End of filename marker
|
||||
|
||||
Serial.write(FILE_EXT); // Filename extension identifier
|
||||
|
||||
Serial.write(*FILE_TYPE); // First character defines file type j,b,p,t
|
||||
}
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
Sketch to generate the setup() calibration values, these are reported
|
||||
to the Serial Monitor.
|
||||
|
||||
The sketch has been tested on the ESP8266 and screen with XPT2046 driver.
|
||||
*/
|
||||
|
||||
#include <SPI.h>
|
||||
#include <TFT_eSPI.h> // Hardware-specific library
|
||||
|
||||
TFT_eSPI tft = TFT_eSPI(); // Invoke custom library
|
||||
|
||||
//------------------------------------------------------------------------------------------
|
||||
|
||||
void setup() {
|
||||
// Use serial port
|
||||
Serial.begin(115200);
|
||||
|
||||
// Initialise the TFT screen
|
||||
tft.init();
|
||||
|
||||
// Set the rotation to the orientation you wish to use in your project before calibration
|
||||
// (the touch coordinates returned then correspond to that rotation only)
|
||||
tft.setRotation(1);
|
||||
|
||||
// Calibrate the touch screen and retrieve the scaling factors
|
||||
touch_calibrate();
|
||||
|
||||
/*
|
||||
// Replace above line with the code sent to Serial Monitor
|
||||
// once calibration is complete, e.g.:
|
||||
uint16_t calData[5] = { 286, 3534, 283, 3600, 6 };
|
||||
tft.setTouch(calData);
|
||||
*/
|
||||
|
||||
// Clear the screen
|
||||
tft.fillScreen(TFT_BLACK);
|
||||
tft.drawCentreString("Touch screen to test!",tft.width()/2, tft.height()/2, 2);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------------
|
||||
|
||||
void loop(void) {
|
||||
uint16_t x = 0, y = 0; // To store the touch coordinates
|
||||
|
||||
// Pressed will be set true is there is a valid touch on the screen
|
||||
bool pressed = tft.getTouch(&x, &y);
|
||||
|
||||
// Draw a white spot at the detected coordinates
|
||||
if (pressed) {
|
||||
tft.fillCircle(x, y, 2, TFT_WHITE);
|
||||
//Serial.print("x,y = ");
|
||||
//Serial.print(x);
|
||||
//Serial.print(",");
|
||||
//Serial.println(y);
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------------
|
||||
|
||||
// Code to run a screen calibration, not needed when calibration values set in setup()
|
||||
void touch_calibrate()
|
||||
{
|
||||
uint16_t calData[5];
|
||||
uint8_t calDataOK = 0;
|
||||
|
||||
// Calibrate
|
||||
tft.fillScreen(TFT_BLACK);
|
||||
tft.setCursor(20, 0);
|
||||
tft.setTextFont(2);
|
||||
tft.setTextSize(1);
|
||||
tft.setTextColor(TFT_WHITE, TFT_BLACK);
|
||||
|
||||
tft.println("Touch corners as indicated");
|
||||
|
||||
tft.setTextFont(1);
|
||||
tft.println();
|
||||
|
||||
tft.calibrateTouch(calData, TFT_MAGENTA, TFT_BLACK, 15);
|
||||
|
||||
Serial.println(); Serial.println();
|
||||
Serial.println("// Use this calibration code in setup():");
|
||||
Serial.print(" uint16_t calData[5] = ");
|
||||
Serial.print("{ ");
|
||||
|
||||
for (uint8_t i = 0; i < 5; i++)
|
||||
{
|
||||
Serial.print(calData[i]);
|
||||
if (i < 4) Serial.print(", ");
|
||||
}
|
||||
|
||||
Serial.println(" };");
|
||||
Serial.print(" tft.setTouch(calData);");
|
||||
Serial.println(); Serial.println();
|
||||
|
||||
tft.fillScreen(TFT_BLACK);
|
||||
|
||||
tft.setTextColor(TFT_GREEN, TFT_BLACK);
|
||||
tft.println("Calibration complete!");
|
||||
tft.println("Calibration code sent to Serial port.");
|
||||
|
||||
delay(4000);
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
// Viewport Demo
|
||||
|
||||
// See viewport_commands tab for details of functions available
|
||||
|
||||
// This example uses the viewport commands to create a "virtual TFT" within the
|
||||
// normal TFT display area. This allows a sketch written for a smaller screen to
|
||||
// be run in a viewport window. By default, the graphics 0,0 datum is set to the
|
||||
// top left corner of the viewport, but optionally the datum can be kept at the
|
||||
// corner of the TFT.
|
||||
|
||||
// Viewports have a number of potential uses:
|
||||
// - create a "virtual" TFT screen smaller than the actual TFT screen
|
||||
// - render GUI items (menus etc.) in a viewport, erase GUI item by redrawing whole screen,
|
||||
// this will be fast because only the viewport will be refreshed (e.g. clearing menu)
|
||||
// - limit screen refresh to a particular area, e.g. changing numbers, icons or graph plotting
|
||||
// - showing a small portion of a larger image or sprite, this allows panning and scrolling
|
||||
|
||||
// A viewport can have the coordinate datum (position 0,0) either at the top left corner of
|
||||
// the viewport or at the normal top left corner of the TFT.
|
||||
// Putting the coordinate datum at the viewport corner means that functions that draw graphics
|
||||
// in a fixed position can be relocated anywhere on the screen. (see plotBox() below). This
|
||||
// makes it easier to reposition groups of graphical objects (for example GUI buttons) that have
|
||||
// fixed relative positions.
|
||||
|
||||
#include <SPI.h>
|
||||
#include <TFT_eSPI.h>
|
||||
|
||||
TFT_eSPI tft = TFT_eSPI();
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
tft.init();
|
||||
tft.setRotation(1);
|
||||
tft.fillScreen(TFT_BLACK);
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
// Normal Screen
|
||||
drawX();
|
||||
|
||||
delay(2000);
|
||||
|
||||
// Viewport screen
|
||||
tft.setViewport(10, 10, 140, 100);
|
||||
tft.frameViewport(TFT_NAVY, -2);
|
||||
tft.fillScreen(TFT_BLACK);
|
||||
drawX();
|
||||
tft.resetViewport();
|
||||
|
||||
delay(2000);
|
||||
|
||||
//Normal screen
|
||||
tft.fillScreen(TFT_BLACK);
|
||||
drawX();
|
||||
|
||||
delay(2000);
|
||||
|
||||
tft.fillScreen(TFT_BLACK);
|
||||
|
||||
// Viewport as a clipping window (false parameter means coordinate datum stays at TFT top left)
|
||||
tft.setViewport(10, 10, tft.width()/2 - 10, tft.height() - 20, false);
|
||||
//tft.frameViewport(TFT_NAVY, 2); // Add 2 pixel border inside viewport
|
||||
//tft.frameViewport(TFT_NAVY, -2); // Add 2 pixel border outside viewport
|
||||
drawX();
|
||||
|
||||
delay(2000);
|
||||
|
||||
while(1)
|
||||
{
|
||||
tft.resetViewport(); // Reset viewport so width() and height() return TFT size
|
||||
|
||||
uint16_t w = 40;
|
||||
uint16_t h = 40;
|
||||
uint16_t x = random(tft.width() - w);
|
||||
uint16_t y = random(tft.height() - h);
|
||||
|
||||
tft.setViewport(x, y, w, h);
|
||||
|
||||
plotBox();
|
||||
|
||||
delay(0);
|
||||
}
|
||||
}
|
||||
|
||||
void drawX(void)
|
||||
{
|
||||
tft.fillScreen(tft.color565(25,25,25)); // Grey
|
||||
|
||||
// Draw circle
|
||||
tft.drawCircle(tft.width()/2, tft.height()/2, tft.width()/4, TFT_RED);
|
||||
|
||||
// Draw diagonal lines
|
||||
tft.drawLine(0 , 0, tft.width()-1, tft.height()-1, TFT_GREEN);
|
||||
tft.drawLine(0 , tft.height()-1, tft.width()-1, 0, TFT_BLUE);
|
||||
|
||||
tft.setTextDatum(MC_DATUM);
|
||||
tft.setTextColor(TFT_WHITE, tft.color565(25,25,25));
|
||||
tft.drawString("Hello World!", tft.width()/2, tft.height()/2, 4); // Font 4
|
||||
}
|
||||
|
||||
void plotBox(void)
|
||||
{
|
||||
// These are always plotted at a fixed position but they can
|
||||
// be plotted into a viewport anywhere on the screen because
|
||||
// a viewport can move the screen datum
|
||||
tft.fillScreen(TFT_BLACK); // When a viewport is set, this just fills the viewport
|
||||
tft.drawRect(0,0, 40,40, TFT_BLUE);
|
||||
tft.setTextDatum(MC_DATUM);
|
||||
tft.setTextColor(TFT_WHITE);
|
||||
tft.drawNumber( random(100), 20, 23, 4); // Number in font 4
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
|
||||
// Create a viewport at TFT screen coordinated X,Y of width W and height H
|
||||
tft.setViewport(X, Y, W, H); // By default the 0,0 coordinate datum is moved to top left
|
||||
// corner of viewport
|
||||
// Note: tft.width() and tft.height() now return viewport size!
|
||||
// The above command is identical to:
|
||||
tft.setViewport(VP_X, VP_Y, VP_W, VP_H, true); // true parameter is optional
|
||||
|
||||
// To create a viewport that keeps the coordinate datum at top left of TFT, use false parameter
|
||||
tft.setViewport(VP_X, VP_Y, VP_W, VP_H, false); // Note: tft.width() and tft.height() return TFT size!
|
||||
|
||||
// To get viewport x, y coordinates, width, height and datum position flag
|
||||
int32_t x = tft.getViewportX(); // Always returns viewport x coordinate relative to screen left edge
|
||||
int32_t y = tft.getViewportY(void); // Always returns viewport y coordinate relative to screen top edge
|
||||
int32_t w = tft.getViewportWidth(); // Always returns width of viewport
|
||||
int32_t h = tft.getViewportHeight(); // Always returns height of viewport
|
||||
bool f = tft.getViewportDatum(); // Datum of the viewport (false = TFT corner, true = viewport corner)
|
||||
// To check if all or part of an area is in the viewport
|
||||
checkViewport(x, y, w, h); // Returns "true" if all or part of area is in viewport
|
||||
|
||||
// To draw a rectangular frame outside viewport of width W (when W is negative)
|
||||
tft.frameViewport(TFT_RED, -W); // Note setting the width to a large negative value will clear the screen
|
||||
// outside the viewport
|
||||
|
||||
// To draw a rectangular frame inside viewport of width W (when W is positive)
|
||||
tft.frameViewport(TFT_RED, W); // Note setting the width to a large positive value will clear the screen
|
||||
// inside the viewport
|
||||
|
||||
// To reset the viewport to the normal TFT full screen
|
||||
tft.resetViewport(); // Note: Graphics will NOT be drawn to the TFT outside a viewport until
|
||||
// this command is used! ( The exception is using the frameViewport command
|
||||
// detailed above with a negative width.)
|
||||
|
||||
// Note:
|
||||
// Using setRotation rotates the whole TFT screen it does not just
|
||||
// rotate the viewport (this is a possible future enhancement).
|
||||
// Redraw all graphics after a rotation since some TFT's do not
|
||||
// re-map the TFT graphics RAM to the screen pixels as expected.
|
||||
*/
|
||||
|
|
@ -0,0 +1,382 @@
|
|||
/*
|
||||
This sketch demonstrates the Adafruit graphicstest sketch running in a
|
||||
viewport (aka window) within the TFT screen area. To do this line 37 has
|
||||
been added. Line 39 draws a frame outside the viewport.
|
||||
|
||||
This sketch uses the GLCD font (font 1) only.
|
||||
|
||||
Make sure all the display driver and pin connections are correct by
|
||||
editing the User_Setup.h file in the TFT_eSPI library folder.
|
||||
|
||||
#########################################################################
|
||||
###### DON'T FORGET TO UPDATE THE User_Setup.h FILE IN THE LIBRARY ######
|
||||
#########################################################################
|
||||
*/
|
||||
|
||||
|
||||
#include "SPI.h"
|
||||
#include "TFT_eSPI.h"
|
||||
|
||||
TFT_eSPI tft = TFT_eSPI();
|
||||
|
||||
unsigned long total = 0;
|
||||
unsigned long tn = 0;
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
while (!Serial);
|
||||
|
||||
Serial.println(""); Serial.println("");
|
||||
Serial.println("TFT_eSPI library test!");
|
||||
|
||||
tft.init();
|
||||
|
||||
tn = micros();
|
||||
tft.fillScreen(TFT_BLACK);
|
||||
|
||||
// Create a viewport 220 x 300 pixels
|
||||
tft.setViewport(10,10,220,300);
|
||||
|
||||
tft.frameViewport(TFT_RED, -1); // 1 pixel wide frame around viewport
|
||||
|
||||
yield(); Serial.println(F("Benchmark Time (microseconds)"));
|
||||
|
||||
yield(); Serial.print(F("Screen fill "));
|
||||
yield(); Serial.println(testFillScreen());
|
||||
//total+=testFillScreen();
|
||||
//delay(500);
|
||||
|
||||
yield(); Serial.print(F("Text "));
|
||||
yield(); Serial.println(testText());
|
||||
//total+=testText();
|
||||
//delay(3000);
|
||||
|
||||
yield(); Serial.print(F("Lines "));
|
||||
yield(); Serial.println(testLines(TFT_CYAN));
|
||||
//total+=testLines(TFT_CYAN);
|
||||
//delay(500);
|
||||
|
||||
yield(); Serial.print(F("Horiz/Vert Lines "));
|
||||
yield(); Serial.println(testFastLines(TFT_RED, TFT_BLUE));
|
||||
//total+=testFastLines(TFT_RED, TFT_BLUE);
|
||||
//delay(500);
|
||||
|
||||
yield(); Serial.print(F("Rectangles (outline) "));
|
||||
yield(); Serial.println(testRects(TFT_GREEN));
|
||||
//total+=testRects(TFT_GREEN);
|
||||
//delay(500);
|
||||
|
||||
yield(); Serial.print(F("Rectangles (filled) "));
|
||||
yield(); Serial.println(testFilledRects(TFT_YELLOW, TFT_MAGENTA));
|
||||
//total+=testFilledRects(TFT_YELLOW, TFT_MAGENTA);
|
||||
//delay(500);
|
||||
|
||||
yield(); Serial.print(F("Circles (filled) "));
|
||||
yield(); Serial.println(testFilledCircles(10, TFT_MAGENTA));
|
||||
//total+= testFilledCircles(10, TFT_MAGENTA);
|
||||
|
||||
yield(); Serial.print(F("Circles (outline) "));
|
||||
yield(); Serial.println(testCircles(10, TFT_WHITE));
|
||||
//total+=testCircles(10, TFT_WHITE);
|
||||
//delay(500);
|
||||
|
||||
yield(); Serial.print(F("Triangles (outline) "));
|
||||
yield(); Serial.println(testTriangles());
|
||||
//total+=testTriangles();
|
||||
//delay(500);
|
||||
|
||||
yield(); Serial.print(F("Triangles (filled) "));
|
||||
yield(); Serial.println(testFilledTriangles());
|
||||
//total += testFilledTriangles();
|
||||
//delay(500);
|
||||
|
||||
yield(); Serial.print(F("Rounded rects (outline) "));
|
||||
yield(); Serial.println(testRoundRects());
|
||||
//total+=testRoundRects();
|
||||
//delay(500);
|
||||
|
||||
yield(); Serial.print(F("Rounded rects (filled) "));
|
||||
yield(); Serial.println(testFilledRoundRects());
|
||||
//total+=testFilledRoundRects();
|
||||
//delay(500);
|
||||
|
||||
yield(); Serial.println(F("Done!")); yield();
|
||||
//Serial.print(F("Total = ")); Serial.println(total);
|
||||
|
||||
//yield();Serial.println(millis()-tn);
|
||||
}
|
||||
|
||||
void loop(void) {
|
||||
for (uint8_t rotation = 0; rotation < 4; rotation++) {
|
||||
tft.setRotation(rotation);
|
||||
tft.resetViewport(); // reset viewport to whole screen
|
||||
tft.fillScreen(TFT_BLACK); // so it can be cleared
|
||||
|
||||
// Create a viewport 220 x 300 pixels
|
||||
tft.setViewport(10,10,220,300);
|
||||
tft.frameViewport(TFT_RED, -1); // 1 pixel wide frame around viewport
|
||||
|
||||
testText();
|
||||
delay(2000);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
unsigned long testFillScreen() {
|
||||
unsigned long start = micros();
|
||||
tft.fillScreen(TFT_BLACK);
|
||||
tft.fillScreen(TFT_RED);
|
||||
tft.fillScreen(TFT_GREEN);
|
||||
tft.fillScreen(TFT_BLUE);
|
||||
tft.fillScreen(TFT_BLACK);
|
||||
return micros() - start;
|
||||
}
|
||||
|
||||
unsigned long testText() {
|
||||
tft.fillScreen(TFT_BLACK);
|
||||
unsigned long start = micros();
|
||||
tft.setCursor(0, 0);
|
||||
tft.setTextColor(TFT_WHITE); tft.setTextSize(1);
|
||||
tft.println("Hello World!");
|
||||
tft.setTextColor(TFT_YELLOW); tft.setTextSize(2);
|
||||
tft.println(1234.56);
|
||||
tft.setTextColor(TFT_RED); tft.setTextSize(3);
|
||||
tft.println(0xDEADBEEF, HEX);
|
||||
tft.println();
|
||||
tft.setTextColor(TFT_GREEN);
|
||||
tft.setTextSize(5);
|
||||
tft.println("Groop");
|
||||
tft.setTextSize(2);
|
||||
tft.println("I implore thee,");
|
||||
//tft.setTextColor(TFT_GREEN,TFT_BLACK);
|
||||
tft.setTextSize(1);
|
||||
tft.println("my foonting turlingdromes.");
|
||||
tft.println("And hooptiously drangle me");
|
||||
tft.println("with crinkly bindlewurdles,");
|
||||
tft.println("Or I will rend thee");
|
||||
tft.println("in the gobberwarts");
|
||||
tft.println("with my blurglecruncheon,");
|
||||
tft.println("see if I don't!");
|
||||
return micros() - start;
|
||||
}
|
||||
|
||||
unsigned long testLines(uint16_t color) {
|
||||
unsigned long start, t;
|
||||
int x1, y1, x2, y2,
|
||||
w = tft.width(),
|
||||
h = tft.height();
|
||||
|
||||
tft.fillScreen(TFT_BLACK);
|
||||
|
||||
x1 = y1 = 0;
|
||||
y2 = h - 1;
|
||||
start = micros();
|
||||
for (x2 = 0; x2 < w; x2 += 6) tft.drawLine(x1, y1, x2, y2, color);
|
||||
x2 = w - 1;
|
||||
for (y2 = 0; y2 < h; y2 += 6) tft.drawLine(x1, y1, x2, y2, color);
|
||||
t = micros() - start; // fillScreen doesn't count against timing
|
||||
|
||||
tft.fillScreen(TFT_BLACK);
|
||||
|
||||
x1 = w - 1;
|
||||
y1 = 0;
|
||||
y2 = h - 1;
|
||||
start = micros();
|
||||
for (x2 = 0; x2 < w; x2 += 6) tft.drawLine(x1, y1, x2, y2, color);
|
||||
x2 = 0;
|
||||
for (y2 = 0; y2 < h; y2 += 6) tft.drawLine(x1, y1, x2, y2, color);
|
||||
t += micros() - start;
|
||||
|
||||
tft.fillScreen(TFT_BLACK);
|
||||
|
||||
x1 = 0;
|
||||
y1 = h - 1;
|
||||
y2 = 0;
|
||||
start = micros();
|
||||
for (x2 = 0; x2 < w; x2 += 6) tft.drawLine(x1, y1, x2, y2, color);
|
||||
x2 = w - 1;
|
||||
for (y2 = 0; y2 < h; y2 += 6) tft.drawLine(x1, y1, x2, y2, color);
|
||||
t += micros() - start;
|
||||
|
||||
tft.fillScreen(TFT_BLACK);
|
||||
|
||||
x1 = w - 1;
|
||||
y1 = h - 1;
|
||||
y2 = 0;
|
||||
start = micros();
|
||||
for (x2 = 0; x2 < w; x2 += 6) tft.drawLine(x1, y1, x2, y2, color);
|
||||
x2 = 0;
|
||||
for (y2 = 0; y2 < h; y2 += 6) tft.drawLine(x1, y1, x2, y2, color);
|
||||
|
||||
return micros() - start;
|
||||
}
|
||||
|
||||
unsigned long testFastLines(uint16_t color1, uint16_t color2) {
|
||||
unsigned long start;
|
||||
int x, y, w = tft.width(), h = tft.height();
|
||||
|
||||
tft.fillScreen(TFT_BLACK);
|
||||
start = micros();
|
||||
for (y = 0; y < h; y += 5) tft.drawFastHLine(0, y, w, color1);
|
||||
for (x = 0; x < w; x += 5) tft.drawFastVLine(x, 0, h, color2);
|
||||
|
||||
return micros() - start;
|
||||
}
|
||||
|
||||
unsigned long testRects(uint16_t color) {
|
||||
unsigned long start;
|
||||
int n, i, i2,
|
||||
cx = tft.width() / 2,
|
||||
cy = tft.height() / 2;
|
||||
|
||||
tft.fillScreen(TFT_BLACK);
|
||||
n = min(tft.width(), tft.height());
|
||||
start = micros();
|
||||
for (i = 2; i < n; i += 6) {
|
||||
i2 = i / 2;
|
||||
tft.drawRect(cx - i2, cy - i2, i, i, color);
|
||||
}
|
||||
|
||||
return micros() - start;
|
||||
}
|
||||
|
||||
unsigned long testFilledRects(uint16_t color1, uint16_t color2) {
|
||||
unsigned long start, t = 0;
|
||||
int n, i, i2,
|
||||
cx = tft.width() / 2 - 1,
|
||||
cy = tft.height() / 2 - 1;
|
||||
|
||||
tft.fillScreen(TFT_BLACK);
|
||||
n = min(tft.width(), tft.height());
|
||||
for (i = n - 1; i > 0; i -= 6) {
|
||||
i2 = i / 2;
|
||||
start = micros();
|
||||
tft.fillRect(cx - i2, cy - i2, i, i, color1);
|
||||
t += micros() - start;
|
||||
// Outlines are not included in timing results
|
||||
tft.drawRect(cx - i2, cy - i2, i, i, color2);
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
unsigned long testFilledCircles(uint8_t radius, uint16_t color) {
|
||||
unsigned long start;
|
||||
int x, y, w = tft.width(), h = tft.height(), r2 = radius * 2;
|
||||
|
||||
tft.fillScreen(TFT_BLACK);
|
||||
start = micros();
|
||||
for (x = radius; x < w; x += r2) {
|
||||
for (y = radius; y < h; y += r2) {
|
||||
tft.fillCircle(x, y, radius, color);
|
||||
}
|
||||
}
|
||||
|
||||
return micros() - start;
|
||||
}
|
||||
|
||||
unsigned long testCircles(uint8_t radius, uint16_t color) {
|
||||
unsigned long start;
|
||||
int x, y, r2 = radius * 2,
|
||||
w = tft.width() + radius,
|
||||
h = tft.height() + radius;
|
||||
|
||||
// Screen is not cleared for this one -- this is
|
||||
// intentional and does not affect the reported time.
|
||||
start = micros();
|
||||
for (x = 0; x < w; x += r2) {
|
||||
for (y = 0; y < h; y += r2) {
|
||||
tft.drawCircle(x, y, radius, color);
|
||||
}
|
||||
}
|
||||
|
||||
return micros() - start;
|
||||
}
|
||||
|
||||
unsigned long testTriangles() {
|
||||
unsigned long start;
|
||||
int n, i, cx = tft.width() / 2 - 1,
|
||||
cy = tft.height() / 2 - 1;
|
||||
|
||||
tft.fillScreen(TFT_BLACK);
|
||||
n = min(cx, cy);
|
||||
start = micros();
|
||||
for (i = 0; i < n; i += 5) {
|
||||
tft.drawTriangle(
|
||||
cx , cy - i, // peak
|
||||
cx - i, cy + i, // bottom left
|
||||
cx + i, cy + i, // bottom right
|
||||
tft.color565(0, 0, i));
|
||||
}
|
||||
|
||||
return micros() - start;
|
||||
}
|
||||
|
||||
unsigned long testFilledTriangles() {
|
||||
unsigned long start, t = 0;
|
||||
int i, cx = tft.width() / 2 - 1,
|
||||
cy = tft.height() / 2 - 1;
|
||||
|
||||
tft.fillScreen(TFT_BLACK);
|
||||
start = micros();
|
||||
for (i = min(cx, cy); i > 10; i -= 5) {
|
||||
start = micros();
|
||||
tft.fillTriangle(cx, cy - i, cx - i, cy + i, cx + i, cy + i,
|
||||
tft.color565(0, i, i));
|
||||
t += micros() - start;
|
||||
tft.drawTriangle(cx, cy - i, cx - i, cy + i, cx + i, cy + i,
|
||||
tft.color565(i, i, 0));
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
unsigned long testRoundRects() {
|
||||
unsigned long start;
|
||||
int w, i, i2,
|
||||
cx = tft.width() / 2 - 1,
|
||||
cy = tft.height() / 2 - 1;
|
||||
|
||||
tft.fillScreen(TFT_BLACK);
|
||||
w = min(tft.width(), tft.height());
|
||||
start = micros();
|
||||
for (i = 0; i < w; i += 6) {
|
||||
i2 = i / 2;
|
||||
tft.drawRoundRect(cx - i2, cy - i2, i, i, i / 8, tft.color565(i, 0, 0));
|
||||
}
|
||||
|
||||
return micros() - start;
|
||||
}
|
||||
|
||||
unsigned long testFilledRoundRects() {
|
||||
unsigned long start;
|
||||
int i, i2,
|
||||
cx = tft.width() / 2 - 1,
|
||||
cy = tft.height() / 2 - 1;
|
||||
|
||||
tft.fillScreen(TFT_BLACK);
|
||||
start = micros();
|
||||
for (i = min(tft.width(), tft.height()); i > 20; i -= 6) {
|
||||
i2 = i / 2;
|
||||
tft.fillRoundRect(cx - i2, cy - i2, i, i, i / 8, tft.color565(0, i, 0));
|
||||
}
|
||||
|
||||
return micros() - start;
|
||||
}
|
||||
|
||||
/***************************************************
|
||||
Original Adafruit text:
|
||||
|
||||
This is an example sketch for the Adafruit 2.2" SPI display.
|
||||
This library works with the Adafruit 2.2" TFT Breakout w/SD card
|
||||
----> http://www.adafruit.com/products/1480
|
||||
|
||||
Check out the links above for our tutorials and wiring diagrams
|
||||
These displays use SPI to communicate, 4 or 5 pins are required to
|
||||
interface (RST is optional)
|
||||
Adafruit invests time and resources providing this open source code,
|
||||
please support Adafruit and open-source hardware by purchasing
|
||||
products from Adafruit!
|
||||
|
||||
Written by Limor Fried/Ladyada for Adafruit Industries.
|
||||
MIT license, all text above must be included in any redistribution
|
||||
****************************************************/
|
||||
|
|
@ -0,0 +1,194 @@
|
|||
/*
|
||||
This tests the alpha blending function that is used with the anti-aliased
|
||||
fonts:
|
||||
|
||||
Alpha = 0 = 100% background, alpha = 255 = 100% foreground colour
|
||||
|
||||
blendedColor = tft.alphaBlend(alpha, fg_color, bg_color);
|
||||
|
||||
The alphaBlend() function operates on 16-bit colours only
|
||||
A test is included where the colours are mapped to 8 bits after blending
|
||||
|
||||
Information on alpha blending is here
|
||||
https://en.wikipedia.org/wiki/Alpha_compositing
|
||||
|
||||
Example for library:
|
||||
https://github.com/Bodmer/TFT_eSPI
|
||||
|
||||
The sketch has been tested on a 320x240 ILI9341 based TFT, it
|
||||
could be adapted for other screen sizes.
|
||||
|
||||
Created by Bodmer 10/2/18
|
||||
|
||||
#########################################################################
|
||||
###### DON'T FORGET TO UPDATE THE User_Setup.h FILE IN THE LIBRARY ######
|
||||
#########################################################################
|
||||
*/
|
||||
|
||||
#include <TFT_eSPI.h> // Include the graphics library
|
||||
|
||||
TFT_eSPI tft = TFT_eSPI(); // Create object "tft"
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Setup
|
||||
// -------------------------------------------------------------------------
|
||||
void setup(void) {
|
||||
tft.init();
|
||||
tft.setRotation(0);
|
||||
tft.fillScreen(TFT_DARKGREY);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Main loop
|
||||
// -------------------------------------------------------------------------
|
||||
void loop()
|
||||
{
|
||||
// 16-bit colours (5 bits red, 6 bits green, 5 bits blue)
|
||||
// Blend from white to full spectrum
|
||||
for (int a = 0; a < 256; a+=2) // Alpha 0 = 100% background, alpha 255 = 100% foreground
|
||||
{
|
||||
for (int c = 0; c < 192; c++) tft.drawPixel(c, a/2, tft.alphaBlend(a, rainbow(c), TFT_WHITE));
|
||||
}
|
||||
|
||||
// Blend from full spectrum to black
|
||||
for (int a = 255; a > 2; a-=2)
|
||||
{
|
||||
for (int c = 0; c < 192; c++) tft.drawPixel(c, 128 + (255-a)/2, tft.alphaBlend(a, rainbow(c), TFT_BLACK));
|
||||
}
|
||||
|
||||
// Blend from white to black (32 grey levels)
|
||||
for (uint16_t a = 0; a < 255; a++) // Alpha 0 = 100% background, alpha 255 = 100% foreground
|
||||
{
|
||||
tft.drawFastHLine(192, a, 12, tft.alphaBlend(a, TFT_BLACK, TFT_WHITE));
|
||||
tft.drawFastHLine(204, a, 12, tft.alphaBlend(a, TFT_BLACK, TFT_RED));
|
||||
tft.drawFastHLine(216, a, 12, tft.alphaBlend(a, TFT_BLACK, TFT_GREEN));
|
||||
tft.drawFastHLine(228, a, 12, tft.alphaBlend(a, TFT_BLACK, TFT_BLUE));
|
||||
}
|
||||
|
||||
delay(4000);
|
||||
|
||||
// Blend from white to colour (32 grey levels)
|
||||
for (uint16_t a = 0; a < 255; a++) // Alpha 0 = 100% background, alpha 255 = 100% foreground
|
||||
{
|
||||
//tft.drawFastHLine(192, a, 12, tft.alphaBlend(a, TFT_BLACK, TFT_WHITE));
|
||||
tft.drawFastHLine(204, a, 12, tft.alphaBlend(a, TFT_RED, TFT_WHITE));
|
||||
tft.drawFastHLine(216, a, 12, tft.alphaBlend(a, TFT_GREEN, TFT_WHITE));
|
||||
tft.drawFastHLine(228, a, 12, tft.alphaBlend(a, TFT_BLUE, TFT_WHITE));
|
||||
}
|
||||
|
||||
delay(4000);
|
||||
|
||||
//*
|
||||
// Decrease to 8-bit colour (3 bits red, 3 bits green, 2 bits blue)
|
||||
// Blend from white to full spectrum
|
||||
for (int a = 0; a < 256; a+=2) // Alpha 0 = 100% background, alpha 255 = 100% foreground
|
||||
{
|
||||
// Convert blended 16-bit colour to 8 bits to reduce colour resolution, then map back to 16 bits for displaying
|
||||
for (int c = 0; c < 192; c++) tft.drawPixel(c, a/2, tft.color8to16(tft.color16to8(tft.alphaBlend(a, rainbow(c), 0xFFFF))));
|
||||
}
|
||||
|
||||
// Blend from full spectrum to black
|
||||
for (int a = 255; a > 2; a-=2)
|
||||
{
|
||||
// Convert blended 16-bit colour to 8 bits to reduce colour resolution, then map back to 16 bits for displaying
|
||||
for (int c = 0; c < 192; c++) tft.drawPixel(c, 128 + (255-a)/2, tft.color8to16(tft.color16to8(tft.alphaBlend(a, rainbow(c), 0))));
|
||||
}
|
||||
|
||||
// Blend from white to black (4 grey levels - it will draw 4 more with a blue tinge due to lower blue bit count)
|
||||
// Blend from black to a primary colour
|
||||
for (uint16_t a = 0; a < 255; a++) // Alpha 0 = 100% background, alpha 255 = 100% foreground
|
||||
{
|
||||
tft.drawFastHLine(192, a, 12, tft.color8to16(tft.color16to8(tft.alphaBlend(a, TFT_BLACK, TFT_WHITE))));
|
||||
tft.drawFastHLine(204, a, 12, tft.color8to16(tft.color16to8(tft.alphaBlend(a, TFT_BLACK, TFT_RED))));
|
||||
tft.drawFastHLine(216, a, 12, tft.color8to16(tft.color16to8(tft.alphaBlend(a, TFT_BLACK, TFT_GREEN))));
|
||||
tft.drawFastHLine(228, a, 12, tft.color8to16(tft.color16to8(tft.alphaBlend(a, TFT_BLACK, TFT_BLUE))));
|
||||
}
|
||||
|
||||
delay(4000);
|
||||
//*/
|
||||
|
||||
/*
|
||||
// 16-bit colours (5 bits red, 6 bits green, 5 bits blue)
|
||||
for (int a = 0; a < 256; a+=2) // Alpha 0 = 100% background, alpha 255 = 100% foreground
|
||||
{
|
||||
for (int c = 0; c < 192; c++) tft.drawPixel(c, a/2, tft.alphaBlend(a, rainbow(c), TFT_CYAN));
|
||||
}
|
||||
|
||||
// Blend from full spectrum to cyan
|
||||
for (int a = 255; a > 2; a-=2)
|
||||
{
|
||||
for (int c = 0; c < 192; c++) tft.drawPixel(c, 128 + (255-a)/2, tft.alphaBlend(a, rainbow(c), TFT_YELLOW));
|
||||
}
|
||||
//*/
|
||||
|
||||
/*
|
||||
// Blend other colour transitions for test purposes
|
||||
for (uint16_t a = 0; a < 255; a++) // Alpha 0 = 100% background, alpha 255 = 100% foreground
|
||||
{
|
||||
tft.drawFastHLine(192, a, 12, tft.alphaBlend(a, TFT_WHITE, TFT_WHITE)); // Should show as solid white
|
||||
tft.drawFastHLine(204, a, 12, tft.alphaBlend(a, TFT_BLACK, TFT_BLACK)); // Should show as solid black
|
||||
tft.drawFastHLine(216, a, 12, tft.alphaBlend(a, TFT_YELLOW, TFT_CYAN)); // Brightness should be fairly even
|
||||
tft.drawFastHLine(228, a, 12, tft.alphaBlend(a, TFT_CYAN, TFT_MAGENTA));// Brightness should be fairly even
|
||||
}
|
||||
|
||||
delay(4000);
|
||||
//*/
|
||||
}
|
||||
|
||||
|
||||
// #########################################################################
|
||||
// Return a 16-bit rainbow colour
|
||||
// #########################################################################
|
||||
unsigned int rainbow(byte value)
|
||||
{
|
||||
// If 'value' is in the range 0-159 it is converted to a spectrum colour
|
||||
// from 0 = red through to 127 = blue to 159 = violet
|
||||
// Extending the range to 0-191 adds a further violet to red band
|
||||
|
||||
value = value%192;
|
||||
|
||||
byte red = 0; // Red is the top 5 bits of a 16-bit colour value
|
||||
byte green = 0; // Green is the middle 6 bits, but only top 5 bits used here
|
||||
byte blue = 0; // Blue is the bottom 5 bits
|
||||
|
||||
byte sector = value >> 5;
|
||||
byte amplit = value & 0x1F;
|
||||
|
||||
switch (sector)
|
||||
{
|
||||
case 0:
|
||||
red = 0x1F;
|
||||
green = amplit; // Green ramps up
|
||||
blue = 0;
|
||||
break;
|
||||
case 1:
|
||||
red = 0x1F - amplit; // Red ramps down
|
||||
green = 0x1F;
|
||||
blue = 0;
|
||||
break;
|
||||
case 2:
|
||||
red = 0;
|
||||
green = 0x1F;
|
||||
blue = amplit; // Blue ramps up
|
||||
break;
|
||||
case 3:
|
||||
red = 0;
|
||||
green = 0x1F - amplit; // Green ramps down
|
||||
blue = 0x1F;
|
||||
break;
|
||||
case 4:
|
||||
red = amplit; // Red ramps up
|
||||
green = 0;
|
||||
blue = 0x1F;
|
||||
break;
|
||||
case 5:
|
||||
red = 0x1F;
|
||||
green = 0;
|
||||
blue = 0x1F - amplit; // Blue ramps down
|
||||
break;
|
||||
}
|
||||
|
||||
return red << 11 | green << 6 | blue;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
// Example sketch to demonstrate the drawing of X BitMap (XBM)
|
||||
// format image onto the display.
|
||||
|
||||
// Information on the X BitMap (XBM) format can be found here:
|
||||
// https://en.wikipedia.org/wiki/X_BitMap
|
||||
|
||||
// This example is part of the TFT_eSPI library:
|
||||
// https://github.com/Bodmer/TFT_eSPI
|
||||
|
||||
// Created by Bodmer 23/04/18
|
||||
|
||||
#include "xbm.h" // Sketch tab header for xbm images
|
||||
|
||||
#include <TFT_eSPI.h> // Hardware-specific library
|
||||
|
||||
TFT_eSPI tft = TFT_eSPI(); // Invoke library
|
||||
|
||||
|
||||
void setup()
|
||||
{
|
||||
tft.begin(); // Initialise the display
|
||||
tft.fillScreen(TFT_BLACK); // Black screen fill
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
|
||||
// Example 1
|
||||
// =========
|
||||
// Random x and y coordinates
|
||||
int x = random(tft.width() - logoWidth);
|
||||
int y = random(tft.height() - logoHeight);
|
||||
|
||||
// Draw bitmap with top left corner at x,y with foreground only color
|
||||
// Bits set to 1 plot as the defined color, bits set to 0 are not plotted
|
||||
// x y xbm xbm width xbm height color
|
||||
tft.drawXBitmap(x, y, logo, logoWidth, logoHeight, TFT_WHITE);
|
||||
|
||||
delay(500);
|
||||
|
||||
// Erase old one by drawing over with background colour
|
||||
tft.drawXBitmap(x, y, logo, logoWidth, logoHeight, TFT_BLACK);
|
||||
|
||||
|
||||
// Example 2
|
||||
// =========
|
||||
// New random x and y coordinates
|
||||
x = random(tft.width() - logoWidth);
|
||||
y = random(tft.height() - logoHeight);
|
||||
|
||||
// Draw bitmap with top left corner at x,y with foreground and background colors
|
||||
// Bits set to 1 plot as the defined fg color, bits set to 0 are plotted as bg color
|
||||
// x y xbm xbm width xbm height fg color bg color
|
||||
tft.drawXBitmap(x, y, logo, logoWidth, logoHeight, TFT_WHITE, TFT_RED);
|
||||
|
||||
delay(500);
|
||||
|
||||
// Erase old one by drawing over with background colour
|
||||
tft.drawXBitmap(x, y, logo, logoWidth, logoHeight, TFT_BLACK, TFT_BLACK);
|
||||
|
||||
}
|
||||
50
libraries/TFT_eSPI/examples/Generic/drawXBitmap/xbm.h
Normal file
50
libraries/TFT_eSPI/examples/Generic/drawXBitmap/xbm.h
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
// Images can be converted to XBM format by using the online converter here:
|
||||
// https://www.online-utility.org/image/convert/to/XBM
|
||||
|
||||
// The output must be pasted in a header file, renamed and adjusted to appear
|
||||
// as as a const unsigned char array in PROGMEM (FLASH program memory).
|
||||
|
||||
// The xbm format adds padding to pixel rows so they are a whole number of bytes
|
||||
// In this example 50 pixel width means 56 bits = 7 bytes
|
||||
// the 50 height then means array uses 50 x 7 = 350 bytes of FLASH
|
||||
// The library ignores the padding bits when drawing the image on the display.
|
||||
|
||||
// Example of the correct format is shown below
|
||||
|
||||
// Espressif logo 50 x 50 pixel array in XBM format
|
||||
#define logoWidth 50 // logo width
|
||||
#define logoHeight 50 // logo height
|
||||
|
||||
// Image is stored in this array
|
||||
PROGMEM const unsigned char logo[] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x01, 0x00,
|
||||
0x00, 0x00, 0x00, 0x07, 0xFC, 0x07, 0x00, 0x00, 0x00, 0x82, 0x7F, 0xF0,
|
||||
0x1F, 0x00, 0x00, 0x00, 0xC6, 0xFF, 0xC3, 0x3F, 0x00, 0x00, 0x00, 0xE7,
|
||||
0xFF, 0x8F, 0x7F, 0x00, 0x00, 0x80, 0xE3, 0xFF, 0x1F, 0xFE, 0x00, 0x00,
|
||||
0x80, 0xE1, 0xFF, 0x7F, 0xFC, 0x01, 0x00, 0xC0, 0x00, 0xFF, 0xFF, 0xF8,
|
||||
0x03, 0x00, 0xE0, 0x00, 0xE0, 0xFF, 0xF1, 0x03, 0x00, 0x60, 0xF0, 0x81,
|
||||
0xFF, 0xE3, 0x07, 0x00, 0x60, 0xFC, 0x1F, 0xFE, 0xC7, 0x07, 0x00, 0x30,
|
||||
0xFE, 0x7F, 0xF8, 0x8F, 0x0F, 0x00, 0x30, 0xFF, 0xFF, 0xF1, 0x9F, 0x0F,
|
||||
0x00, 0xB0, 0xFF, 0xFF, 0xE3, 0x3F, 0x0F, 0x00, 0xB0, 0xFF, 0xFF, 0xC7,
|
||||
0x3F, 0x1E, 0x00, 0xB8, 0xFF, 0xFF, 0x8F, 0x7F, 0x1E, 0x00, 0x98, 0x1F,
|
||||
0xFC, 0x3F, 0xFF, 0x1C, 0x00, 0xB8, 0x3F, 0xE0, 0x3F, 0xFE, 0x1C, 0x00,
|
||||
0x98, 0xFF, 0xC3, 0x7F, 0xFE, 0x19, 0x00, 0x98, 0xFF, 0x0F, 0xFF, 0xFC,
|
||||
0x19, 0x00, 0x38, 0xFF, 0x3F, 0xFF, 0xFC, 0x01, 0x00, 0x30, 0xFE, 0x7F,
|
||||
0xFE, 0xF9, 0x03, 0x00, 0x30, 0xFC, 0xFF, 0xFC, 0xF9, 0x03, 0x00, 0x30,
|
||||
0xF8, 0xFF, 0xF8, 0xF3, 0x03, 0x00, 0x30, 0x00, 0xFF, 0xF9, 0xF3, 0x03,
|
||||
0x00, 0x70, 0x00, 0xFC, 0xF9, 0xF3, 0x07, 0x00, 0x60, 0x00, 0xF8, 0xF3,
|
||||
0xF3, 0x07, 0x00, 0xE0, 0xF8, 0xF8, 0xF3, 0xF7, 0x03, 0x00, 0xC0, 0xF8,
|
||||
0xF1, 0xF3, 0xE3, 0x03, 0x00, 0xC0, 0xFD, 0xF1, 0xF3, 0xF7, 0x01, 0x00,
|
||||
0x80, 0xFD, 0xF1, 0xF3, 0xE7, 0x00, 0x00, 0x00, 0xFF, 0xF1, 0xF3, 0x07,
|
||||
0x00, 0x00, 0x00, 0xFF, 0xF8, 0xF3, 0x07, 0x00, 0x00, 0x00, 0x7E, 0xF8,
|
||||
0xF3, 0x83, 0x03, 0x00, 0x00, 0x3C, 0xF8, 0xF3, 0xC3, 0x01, 0x00, 0x00,
|
||||
0x70, 0xF8, 0xF9, 0xE3, 0x00, 0x00, 0x00, 0xE0, 0xE1, 0x41, 0x78, 0x00,
|
||||
0x00, 0x00, 0xC0, 0x0F, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFD,
|
||||
0x07, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00,
|
||||
0x80, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, };
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue