200 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			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;
 | |
| }
 |