TFT_eSPI/examples/Anti-aliased/Anti-aliased_Clock_v3/Anti-aliased_Clock_v3.ino

200 lines
6.3 KiB
C++

// Sketch to draw an analogue clock on the screen
// This uses anti-aliased drawing functions that are built into TFT_eSPI and this sketch
// See AA_functions tab. These functions are the basis for future library updates.
// The sub-pixel resolution is achieved using floating point coordinates
// Anti-aliased lines can be drawn with sub-pixel resolutuion and permit lines to be
// drawn with less obvious pixel resolution imposed jaggedness.
// TODO - limit NotoSansBold15 font characters to those needed?
// Based on a sketch by DavyLandman:
// https://github.com/Bodmer/TFT_eSPI/issues/905
#define WIFI_SSID "Your_SSID"
#define WIFI_PASSWORD "Your_password"
#include <Arduino.h>
#include <TFT_eSPI.h> // Master copy here: https://github.com/Bodmer/TFT_eSPI
#include <SPI.h>
#define ALPHA_GAIN 255.0f // Should be 255.0 for no gain
#include "NotoSansBold15.h"
TFT_eSPI tft = TFT_eSPI(); // Invoke library, pins defined in User_Setup.h
TFT_eSprite face = TFT_eSprite(&tft);
#define CLOCK_FG TFT_SKYBLUE
#define CLOCK_BG TFT_NAVY
#define SECCOND_FG TFT_RED
#define LABEL_FG TFT_GOLD
#define CLOCK_R 127.0f / 2.0f // Clock face radius (float type), ideally diameter is odd so there is a central pixel
#define H_HAND_LENGTH CLOCK_R/2.0f
#define M_HAND_LENGTH CLOCK_R/1.4f
#define S_HAND_LENGTH CLOCK_R/1.3f
#define FACE_W CLOCK_R * 2 + 1
#define FACE_H CLOCK_R * 2 + 1
// Calculate 1 second increment angles. Hours and minute hand angles
// change every second so we see smooth sub-pixel movement
#define SECOND_ANGLE 360.0 / 60.0
#define MINUTE_ANGLE SECOND_ANGLE / 60.0
#define HOUR_ANGLE MINUTE_ANGLE / 12.0
// Add 1 so sprite has a central pixel
#define FACE_W CLOCK_R * 2 + 1
#define FACE_H CLOCK_R * 2 + 1
// Time h:m:s
uint8_t h=0, m=0, s=0;
float time_secs = h * 3600 + m *60 + s;
// Load header after time_secs global variable has been created so it is in scope
#include "NTP_Time.h" // Attached to this sketch, see that tab for library needs
// Time for next 1 second tick
uint32_t targetTime = 0;
// =========================================================================
// Setup
// =========================================================================
void setup() {
Serial.begin(115200);
Serial.println("Booting...");
// Initialise the screen
tft.init();
// Ideally set orientation for good viewing angle range because
// the anti-aliasing effectiveness varies with screen viewing angle
// Usually this is when screen ribbon connector is at the bottom
tft.setRotation(0);
tft.fillScreen(TFT_BLACK);
// Create the clock face sprite
// face.setColorDepth(8);
face.createSprite(FACE_W, FACE_H);
// Only 1 font used in the sprite, so can remain loaded
face.loadFont(NotoSansBold15);
// Temporary to test drawSpot
int colour = 0;
uint32_t dt = millis();
for (int32_t i=0; i<1000; i++)
{
int x = random(tft.width());
int y = random(tft.height());
colour = random(0xFFFF);
float w = random(100)/20.0;
tft.drawSpot(x, y, w, colour);
yield();
}
Serial.println(millis()-dt);
// Draw the whole clock
renderFace(time_secs);
targetTime = millis() + 1000;
}
// =========================================================================
// Loop
// =========================================================================
void loop() {
// Update time periodically
if (targetTime < millis()) {
// Update next tick time ever 100 milliseconds
targetTime = millis()+100;
// Increment time by 100 milliseconds
time_secs+= 0.1;
if (time_secs >= (60*60*24)) time_secs = 0;
// All graphics are drawn in sprite to stop flicker
renderFace(time_secs);
// Request time from NTP server and synchronise the local clock
syncTime();
}
}
// =========================================================================
// Draw the clock face in the sprite
// =========================================================================
static void renderFace(float t) {
float h_angle = t * HOUR_ANGLE;
float m_angle = t * MINUTE_ANGLE;
float s_angle = t * SECOND_ANGLE;
// The face is completely redrawn - this can be done quickly
face.fillSprite(TFT_BLACK);
// Draw the face circle
//face.drawSpot(CLOCK_R, CLOCK_R, CLOCK_R, CLOCK_BG);
fillCircleAA(CLOCK_R, CLOCK_R, CLOCK_R, CLOCK_BG);
// Set text datum to middle centre and the colour
face.setTextDatum(MC_DATUM);
// In a sprite the background colour for the anti-aliasing does not need to be specified
// the sprite pixel colour will be read during the chanracter rendering
face.setTextColor(CLOCK_FG);
// Text offset adjustment
constexpr uint32_t dialOffset = CLOCK_R - 10;
float xp = 0.0, yp = 0.0; // Use float pixel position for smooth AA motion
// Draw digits around clock perimeter
for (uint32_t h = 1; h <= 12; h++) {
getCoord(CLOCK_R, CLOCK_R, &xp, &yp, dialOffset, h * 360.0 / 12);
face.drawNumber(h, xp, 2 + yp);
}
// Add text (could be digital time)
face.setTextColor(LABEL_FG);
face.drawString("TFT_eSPI", CLOCK_R, CLOCK_R * 0.75);
// Draw hour hand
getCoord(CLOCK_R, CLOCK_R, &xp, &yp, H_HAND_LENGTH, h_angle);
face.drawWideLine(CLOCK_R, CLOCK_R, xp, yp, 6.0f, CLOCK_FG, TFT_BLACK);
face.drawWideLine(CLOCK_R, CLOCK_R, xp, yp, 2.0f, CLOCK_BG);
// Draw minute hand
getCoord(CLOCK_R, CLOCK_R, &xp, &yp, M_HAND_LENGTH, m_angle);
face.drawWideLine(CLOCK_R, CLOCK_R, xp, yp, 6.0f, CLOCK_FG, TFT_BLACK);
face.drawWideLine(CLOCK_R, CLOCK_R, xp, yp, 2.0f, CLOCK_BG);
// Draw the central pivot circle
face.drawSpot(CLOCK_R, CLOCK_R, 4, CLOCK_FG);
//tft.drawLine(CLOCK_R, CLOCK_R+128.0, xp, yp+128.0, 2.2, CLOCK_FG, TFT_BLACK);
// Draw Second hand
getCoord(CLOCK_R, CLOCK_R, &xp, &yp, S_HAND_LENGTH, s_angle);
face.drawWedgeLine(CLOCK_R, CLOCK_R, xp, yp, 2.5, 1.0, SECCOND_FG);
face.pushSprite(0, 0, TFT_TRANSPARENT);
}
// =========================================================================
// Get coordinates of end of a line, pivot at x,y, length r, angle a
// =========================================================================
// Coordinates are returned to caller via the xp and yp pointers
#define DEG2RAD 0.0174532925
void getCoord(int16_t x, int16_t y, float *xp, float *yp, int16_t r, float a)
{
float sx1 = cos( (a - 90) * DEG2RAD);
float sy1 = sin( (a - 90) * DEG2RAD);
*xp = sx1 * r + x;
*yp = sy1 * r + y;
}