├── .github └── FUNDING.yml ├── .gitignore ├── Arduino_Watch ├── Arduino_Watch.ino └── Debug_Printer.h ├── Arduino_Watch_Int ├── Arduino_Watch_Int.ino ├── Debug_Printer.h └── IntTrigonometry.h ├── README.md ├── RTClibSetRTC └── RTClibSetRTC.ino └── benchmark ├── Debug_Printer.h ├── IntTrigonometry.h └── benchmark.ino /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: moononournation 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | *.txt 3 | -------------------------------------------------------------------------------- /Arduino_Watch/Arduino_Watch.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Arduino Watch Pro Micro Version 3 | - read current time from RTC 4 | - draw analog clock face every loop 5 | - shutdown power after 60 seconds 6 | - press power button to restart 7 | */ 8 | #include "Debug_Printer.h" 9 | #include 10 | 11 | // real time clock class 12 | // comment out if no RTC at all 13 | // #define RTC_CLASS RTC_DS1307 14 | #define RTC_CLASS RTC_DS3231 15 | // #define RTC_CLASS RTC_PCF8523 16 | 17 | // auto sleep time (in milliseconds) 18 | // comment out if no need enter sleep, e.g. still in developing stage 19 | #define SLEEP_TIME 20000L 20 | 21 | #define BACKGROUND BLACK 22 | #define MARK_COLOR WHITE 23 | #define SUBMARK_COLOR DARKGREY // LIGHTGREY 24 | #define HOUR_COLOR WHITE 25 | #define MINUTE_COLOR BLUE // LIGHTGREY 26 | #define SECOND_COLOR RED 27 | 28 | #define HOUR_LEN 45 29 | #define MINUTE_LEN 85 30 | #define SECOND_LEN 100 31 | 32 | #define SIXTIETH 0.016666667 33 | #define TWELFTH 0.08333333 34 | #define SIXTIETH_RADIAN 0.10471976 35 | #define TWELFTH_RADIAN 0.52359878 36 | #define RIGHT_ANGLE_RADIAN 1.5707963 37 | #define CENTER 120 38 | 39 | #if defined(ESP32) 40 | #define TFT_CS 5 41 | #define TFT_DC 16 42 | #define TFT_RST 17 43 | #define TFT_BL 22 44 | #define WAKEUP_PIN 36 45 | #else 46 | #define TFT_CS 20 47 | #define TFT_DC 19 48 | #define TFT_RST 18 49 | #define TFT_BL 10 50 | #define WAKEUP_PIN 7 51 | // #define RTC_EN_PIN 4 52 | #endif 53 | //#define LED_LEVEL 128 54 | 55 | #ifdef SLEEP_TIME 56 | #if defined(ESP32) 57 | #include 58 | #elif defined(__AVR__) 59 | #include 60 | #endif 61 | #endif // #ifdef SLEEP_TIME 62 | 63 | #ifdef RTC_CLASS 64 | #include 65 | #include 66 | RTC_CLASS rtc; 67 | #else 68 | static uint8_t conv2d(const char *p) 69 | { 70 | uint8_t v = 0; 71 | return (10 * (*p - '0')) + (*++p - '0'); 72 | } 73 | #endif 74 | 75 | //You can use different type of hardware initialization 76 | Arduino_HWSPI *bus = new Arduino_HWSPI(TFT_DC, TFT_CS); 77 | // 1.3"/1.5" square IPS LCD 240x240 78 | Arduino_TFT *tft = new Arduino_ST7789(bus, TFT_RST, 2 /* rotation */, true /* IPS */, 240 /* width */, 240 /* height */, 0 /* col offset 1 */, 80 /* row offset 1 */); 79 | 80 | static float sdeg, mdeg, hdeg; 81 | static uint8_t osx = CENTER, osy = CENTER, omx = CENTER, omy = CENTER, ohx = CENTER, ohy = CENTER; // Saved H, M, S x & y coords 82 | static uint8_t nsx, nsy, nmx, nmy, nhx, nhy; // H, M, S x & y coords 83 | static uint8_t xMin, yMin, xMax, yMax; // redraw range 84 | static uint8_t hh, mm, ss; 85 | static unsigned long targetTime, sleepTime; // next action time 86 | #ifdef TFT_BL 87 | static bool ledTurnedOn = false; 88 | #endif 89 | #ifdef DEBUG_MODE 90 | static uint16_t loopCount = 0; 91 | #endif 92 | 93 | static uint8_t cached_points[(HOUR_LEN + 1 + MINUTE_LEN + 1 + SECOND_LEN + 1) * 2]; 94 | static int cached_points_idx = 0; 95 | static uint8_t *last_cached_point; 96 | 97 | void setup(void) 98 | { 99 | DEBUG_BEGIN(115200); 100 | DEBUG_PRINTMLN(": start"); 101 | 102 | tft->begin(); 103 | DEBUG_PRINTMLN(": tft->begin()"); 104 | 105 | tft->fillScreen(BACKGROUND); 106 | DEBUG_PRINTMLN(": tft->fillScreen(BACKGROUND)"); 107 | 108 | pinMode(WAKEUP_PIN, INPUT_PULLUP); 109 | #ifdef TFT_BL 110 | pinMode(TFT_BL, OUTPUT); 111 | #endif 112 | 113 | // Draw 60 clock marks 114 | //draw_round_clock_mark(104, 120, 112, 120, 114, 114); 115 | draw_square_clock_mark(102, 120, 108, 120, 114, 120); 116 | //draw_square_clock_mark(72, 90, 78, 90, 84, 90); 117 | DEBUG_PRINTMLN(": Draw 60 clock marks"); 118 | 119 | #ifdef RTC_CLASS 120 | // read date and time from RTC 121 | read_rtc(); 122 | #else 123 | hh = conv2d(__TIME__); 124 | mm = conv2d(__TIME__ + 3); 125 | ss = conv2d(__TIME__ + 6); 126 | #endif 127 | 128 | targetTime = ((millis() / 1000) + 1) * 1000; 129 | 130 | #ifdef SLEEP_TIME 131 | sleepTime = millis() + SLEEP_TIME; 132 | #endif 133 | } 134 | 135 | void loop() 136 | { 137 | unsigned long cur_millis = millis(); 138 | if (cur_millis >= targetTime) 139 | { 140 | targetTime += 1000; 141 | ss++; // Advance second 142 | if (ss == 60) 143 | { 144 | ss = 0; 145 | mm++; // Advance minute 146 | if (mm > 59) 147 | { 148 | mm = 0; 149 | hh++; // Advance hour 150 | if (hh > 23) 151 | { 152 | hh = 0; 153 | } 154 | } 155 | } 156 | } 157 | 158 | // Pre-compute hand degrees, x & y coords for a fast screen update 159 | sdeg = SIXTIETH_RADIAN * ((0.001 * (cur_millis % 1000)) + ss); // 0-59 (includes millis) 160 | nsx = cos(sdeg - RIGHT_ANGLE_RADIAN) * SECOND_LEN + CENTER; 161 | nsy = sin(sdeg - RIGHT_ANGLE_RADIAN) * SECOND_LEN + CENTER; 162 | if ((nsx != osx) || (nsy != osy)) 163 | { 164 | mdeg = (SIXTIETH * sdeg) + (SIXTIETH_RADIAN * mm); // 0-59 (includes seconds) 165 | hdeg = (TWELFTH * mdeg) + (TWELFTH_RADIAN * hh); // 0-11 (includes minutes) 166 | mdeg -= RIGHT_ANGLE_RADIAN; 167 | hdeg -= RIGHT_ANGLE_RADIAN; 168 | nmx = cos(mdeg) * MINUTE_LEN + CENTER; 169 | nmy = sin(mdeg) * MINUTE_LEN + CENTER; 170 | nhx = cos(hdeg) * HOUR_LEN + CENTER; 171 | nhy = sin(hdeg) * HOUR_LEN + CENTER; 172 | 173 | // redraw hands 174 | // redraw_hands_4_split(spi_raw_overwrite_rect); 175 | // redraw_hands_cached_lines(); 176 | redraw_hands_cached_draw_and_erase(); 177 | 178 | ohx = nhx; 179 | ohy = nhy; 180 | omx = nmx; 181 | omy = nmy; 182 | osx = nsx; 183 | osy = nsy; 184 | 185 | #ifdef TFT_BL 186 | if (!ledTurnedOn) 187 | { 188 | backlight(true); 189 | } 190 | #endif 191 | 192 | #ifdef DEBUG_MODE 193 | loopCount++; 194 | #endif 195 | } 196 | 197 | #ifdef SLEEP_TIME 198 | if (cur_millis > sleepTime) 199 | { 200 | // enter sleep 201 | #ifdef DEBUG_MODE 202 | DEBUG_PRINTM(": enter sleep, loopCount: "); 203 | DEBUG_PRINTLN(loopCount); 204 | delay(10); 205 | #endif 206 | 207 | enterSleep(); 208 | 209 | #ifdef DEBUG_MODE 210 | loopCount = 0; 211 | #endif 212 | } 213 | #endif // #ifdef SLEEP_TIME 214 | 215 | delay(1); 216 | } 217 | 218 | #ifdef TFT_BL 219 | void backlight(bool enable) 220 | { 221 | if (enable) 222 | { 223 | #ifdef DEBUG_MODE 224 | DEBUG_PRINTLN("enable backlight"); 225 | #endif 226 | #ifdef LED_LEVEL 227 | #if defined(ESP32) 228 | ledcSetup(1, 12000, 8); // 12 kHz PWM, 8-bit resolution 229 | ledcAttachPin(TFT_BL, 1); // assign TFT_BL pin to channel 1 230 | ledcWrite(1, LED_LEVEL); // brightness 0 - 255 231 | #else 232 | analogWrite(TFT_BL, LED_LEVEL); 233 | #endif 234 | #else 235 | digitalWrite(TFT_BL, HIGH); 236 | #endif // #ifdef LED_LEVEL 237 | ledTurnedOn = true; 238 | } 239 | else 240 | { 241 | #ifdef DEBUG_MODE 242 | DEBUG_PRINTLN("disable backlight"); 243 | #endif 244 | digitalWrite(TFT_BL, LOW); 245 | ledTurnedOn = false; 246 | } 247 | } 248 | #endif 249 | 250 | #ifdef RTC_CLASS 251 | void read_rtc() 252 | { 253 | #ifdef RTC_EN_PIN 254 | pinMode(RTC_EN_PIN, OUTPUT); 255 | digitalWrite(RTC_EN_PIN, HIGH); 256 | #endif 257 | Wire.begin(); 258 | rtc.begin(); 259 | DateTime now = rtc.now(); 260 | hh = now.hour(); 261 | mm = now.minute(); 262 | ss = now.second(); 263 | DEBUG_PRINTM(": read date and time from RTC: "); 264 | DEBUG_PRINT(hh); 265 | DEBUG_PRINT(":"); 266 | DEBUG_PRINT(mm); 267 | DEBUG_PRINT(":"); 268 | DEBUG_PRINTLN(ss); 269 | #if not defined(ESP32) 270 | Wire.end(); 271 | #endif 272 | } 273 | #endif 274 | 275 | #ifdef SLEEP_TIME 276 | void enterSleep() 277 | { 278 | #ifdef TFT_BL 279 | backlight(false); 280 | #endif 281 | tft->displayOff(); 282 | 283 | #if defined(ESP32) 284 | gpio_wakeup_enable((gpio_num_t)WAKEUP_PIN, GPIO_INTR_LOW_LEVEL); 285 | esp_sleep_enable_gpio_wakeup(); 286 | esp_light_sleep_start(); 287 | #elif defined(__AVR__) 288 | // Allow wake up pin to trigger interrupt on low. 289 | attachInterrupt(digitalPinToInterrupt(WAKEUP_PIN), wakeUp, LOW); 290 | 291 | // Enter power down state with ADC and BOD module disabled. 292 | // Wake up when wake up pin is low. 293 | LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF); 294 | 295 | // Disable external pin interrupt on wake up pin. 296 | detachInterrupt(digitalPinToInterrupt(WAKEUP_PIN)); 297 | #endif 298 | 299 | tft->displayOn(); 300 | 301 | // read date and time from RTC 302 | #ifdef RTC_CLASS 303 | read_rtc(); 304 | #endif 305 | 306 | targetTime = ((millis() / 1000) + 1) * 1000; 307 | 308 | #ifdef SLEEP_TIME 309 | sleepTime = millis() + SLEEP_TIME; 310 | #endif 311 | } 312 | 313 | #if defined(__AVR__) 314 | void wakeUp() 315 | { 316 | // Just a handler for the pin interrupt. 317 | } 318 | #endif 319 | #endif // #ifdef SLEEP_TIME 320 | 321 | void draw_round_clock_mark(uint8_t innerR1, uint8_t outerR1, uint8_t innerR2, uint8_t outerR2, uint8_t innerR3, uint8_t outerR3) 322 | { 323 | float x, y; 324 | uint8_t x0, x1, y0, y1, innerR, outerR; 325 | uint16_t c; 326 | 327 | for (int i = 0; i < 60; i++) 328 | { 329 | if ((i % 15) == 0) 330 | { 331 | innerR = innerR1; 332 | outerR = outerR1; 333 | c = MARK_COLOR; 334 | } 335 | else if ((i % 5) == 0) 336 | { 337 | innerR = innerR2; 338 | outerR = outerR2; 339 | c = MARK_COLOR; 340 | } 341 | else 342 | { 343 | innerR = innerR3; 344 | outerR = outerR3; 345 | c = SUBMARK_COLOR; 346 | } 347 | 348 | mdeg = (SIXTIETH_RADIAN * i) - RIGHT_ANGLE_RADIAN; 349 | x = cos(mdeg); 350 | y = sin(mdeg); 351 | x0 = x * outerR + CENTER; 352 | y0 = y * outerR + CENTER; 353 | x1 = x * innerR + CENTER; 354 | y1 = y * innerR + CENTER; 355 | 356 | tft->drawLine(x0, y0, x1, y1, c); 357 | DEBUG_PRINTM(" i: "); 358 | DEBUG_PRINT(i); 359 | DEBUG_PRINT(", x0: "); 360 | DEBUG_PRINT(x0); 361 | DEBUG_PRINT(", y0: "); 362 | DEBUG_PRINT(y0); 363 | DEBUG_PRINT(", x1: "); 364 | DEBUG_PRINT(x1); 365 | DEBUG_PRINT(", y1: "); 366 | DEBUG_PRINTLN(y1); 367 | } 368 | } 369 | 370 | void draw_square_clock_mark(uint8_t innerR1, uint8_t outerR1, uint8_t innerR2, uint8_t outerR2, uint8_t innerR3, uint8_t outerR3) 371 | { 372 | float x, y; 373 | uint8_t x0, x1, y0, y1, innerR, outerR; 374 | uint16_t c; 375 | 376 | for (int i = 0; i < 60; i++) 377 | { 378 | if ((i % 15) == 0) 379 | { 380 | innerR = innerR1; 381 | outerR = outerR1; 382 | c = MARK_COLOR; 383 | } 384 | else if ((i % 5) == 0) 385 | { 386 | innerR = innerR2; 387 | outerR = outerR2; 388 | c = MARK_COLOR; 389 | } 390 | else 391 | { 392 | innerR = innerR3; 393 | outerR = outerR3; 394 | c = SUBMARK_COLOR; 395 | } 396 | 397 | if ((i >= 53) || (i < 8)) 398 | { 399 | x = tan(SIXTIETH_RADIAN * i); 400 | x0 = CENTER + (x * outerR); 401 | y0 = CENTER + (1 - outerR); 402 | x1 = CENTER + (x * innerR); 403 | y1 = CENTER + (1 - innerR); 404 | } 405 | else if (i < 23) 406 | { 407 | y = tan((SIXTIETH_RADIAN * i) - RIGHT_ANGLE_RADIAN); 408 | x0 = CENTER + (outerR); 409 | y0 = CENTER + (y * outerR); 410 | x1 = CENTER + (innerR); 411 | y1 = CENTER + (y * innerR); 412 | } 413 | else if (i < 38) 414 | { 415 | x = tan(SIXTIETH_RADIAN * i); 416 | x0 = CENTER - (x * outerR); 417 | y0 = CENTER + (outerR); 418 | x1 = CENTER - (x * innerR); 419 | y1 = CENTER + (innerR); 420 | } 421 | else if (i < 53) 422 | { 423 | y = tan((SIXTIETH_RADIAN * i) - RIGHT_ANGLE_RADIAN); 424 | x0 = CENTER + (1 - outerR); 425 | y0 = CENTER - (y * outerR); 426 | x1 = CENTER + (1 - innerR); 427 | y1 = CENTER - (y * innerR); 428 | } 429 | tft->drawLine(x0, y0, x1, y1, c); 430 | DEBUG_PRINTM(" i: "); 431 | DEBUG_PRINT(i); 432 | DEBUG_PRINT(", x0: "); 433 | DEBUG_PRINT(x0); 434 | DEBUG_PRINT(", y0: "); 435 | DEBUG_PRINT(y0); 436 | DEBUG_PRINT(", x1: "); 437 | DEBUG_PRINT(x1); 438 | DEBUG_PRINT(", y1: "); 439 | DEBUG_PRINTLN(y1); 440 | } 441 | } 442 | 443 | void redraw_hands_cached_lines() 444 | { 445 | cached_points_idx = 0; 446 | last_cached_point = cached_points; 447 | write_cached_line(nhx, nhy, CENTER, CENTER, HOUR_COLOR, true, false); // cache new hour hand 448 | write_cached_line(nsx, nsy, CENTER, CENTER, SECOND_COLOR, true, false); // cache new second hand 449 | tft->startWrite(); 450 | write_cached_line(nmx, nmy, CENTER, CENTER, MINUTE_COLOR, true, true); // cache and draw new minute hand 451 | write_cached_line(omx, omy, CENTER, CENTER, BACKGROUND, false, true); // erase old minute hand 452 | tft->writeLine(nhx, nhy, CENTER, CENTER, HOUR_COLOR); // draw new hour hand 453 | write_cached_line(ohx, ohy, CENTER, CENTER, BACKGROUND, false, true); // erase old hour hand 454 | tft->writeLine(nsx, nsy, CENTER, CENTER, SECOND_COLOR); // draw new second hand 455 | write_cached_line(osx, osy, CENTER, CENTER, BACKGROUND, false, true); // erase old second hand 456 | tft->endWrite(); 457 | } 458 | 459 | void write_cached_line(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1, uint16_t color, bool save, bool actual_draw) 460 | { 461 | #if defined(ESP8266) 462 | yield(); 463 | #endif 464 | bool steep = _diff(y1, y0) > _diff(x1, x0); 465 | if (steep) 466 | { 467 | _swap_uint8_t(x0, y0); 468 | _swap_uint8_t(x1, y1); 469 | } 470 | 471 | if (x0 > x1) 472 | { 473 | _swap_uint8_t(x0, x1); 474 | _swap_uint8_t(y0, y1); 475 | } 476 | 477 | uint8_t dx, dy; 478 | dx = x1 - x0; 479 | dy = _diff(y1, y0); 480 | 481 | uint8_t err = dx / 2; 482 | int8_t ystep = (y0 < y1) ? 1 : -1; 483 | for (; x0 <= x1; x0++) 484 | { 485 | if (steep) 486 | { 487 | write_cache_point(y0, x0, color, save, actual_draw); 488 | } 489 | else 490 | { 491 | write_cache_point(x0, y0, color, save, actual_draw); 492 | } 493 | if (err < dy) 494 | { 495 | y0 += ystep; 496 | err += dx; 497 | } 498 | err -= dy; 499 | } 500 | } 501 | 502 | void write_cache_point(uint8_t x, uint8_t y, uint16_t color, bool save, bool actual_draw) 503 | { 504 | uint8_t *current_point = cached_points; 505 | for (int i = 0; i < cached_points_idx; i++) 506 | { 507 | if ((x == *(current_point++)) && (y == *(current_point))) 508 | { 509 | return; 510 | } 511 | current_point++; 512 | } 513 | if (save) 514 | { 515 | cached_points_idx++; 516 | *(last_cached_point++) = x; 517 | *(last_cached_point++) = y; 518 | } 519 | if (actual_draw) 520 | { 521 | tft->writePixel(x, y, color); 522 | } 523 | } 524 | 525 | void redraw_hands_cached_draw_and_erase() 526 | { 527 | tft->startWrite(); 528 | draw_and_erase_cached_line(CENTER, CENTER, nsx, nsy, SECOND_COLOR, cached_points, SECOND_LEN + 1, false, false); 529 | draw_and_erase_cached_line(CENTER, CENTER, nhx, nhy, HOUR_COLOR, cached_points + ((SECOND_LEN + 1) * 2), HOUR_LEN + 1, true, false); 530 | draw_and_erase_cached_line(CENTER, CENTER, nmx, nmy, MINUTE_COLOR, cached_points + ((SECOND_LEN + 1 + HOUR_LEN + 1) * 2), MINUTE_LEN + 1, true, true); 531 | tft->endWrite(); 532 | } 533 | 534 | void draw_and_erase_cached_line(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1, uint16_t color, uint8_t *cache, uint16_t cache_len, bool cross_check_second, bool cross_check_hour) 535 | { 536 | #if defined(ESP8266) 537 | yield(); 538 | #endif 539 | bool steep = _diff(y1, y0) > _diff(x1, x0); 540 | if (steep) 541 | { 542 | _swap_uint8_t(x0, y0); 543 | _swap_uint8_t(x1, y1); 544 | } 545 | 546 | uint8_t dx, dy; 547 | dx = _diff(x1, x0); 548 | dy = _diff(y1, y0); 549 | 550 | uint8_t err = dx / 2; 551 | int8_t xstep = (x0 < x1) ? 1 : -1; 552 | int8_t ystep = (y0 < y1) ? 1 : -1; 553 | x1 += xstep; 554 | uint8_t x, y, ox, oy; 555 | for (uint16_t i = 0; i <= dx; i++) 556 | { 557 | if (steep) 558 | { 559 | x = y0; 560 | y = x0; 561 | } 562 | else 563 | { 564 | x = x0; 565 | y = y0; 566 | } 567 | ox = *(cache + (i * 2)); 568 | oy = *(cache + (i * 2) + 1); 569 | if ((x == ox) && (y == oy)) 570 | { 571 | if (cross_check_second || cross_check_hour) 572 | { 573 | write_cache_pixel(x, y, color, cross_check_second, cross_check_hour); 574 | } 575 | } 576 | else 577 | { 578 | write_cache_pixel(x, y, color, cross_check_second, cross_check_hour); 579 | if ((ox > 0) || (oy > 0)) { 580 | write_cache_pixel(ox, oy, BACKGROUND, cross_check_second, cross_check_hour); 581 | } 582 | *(cache + (i * 2)) = x; 583 | *(cache + (i * 2) + 1) = y; 584 | } 585 | if (err < dy) 586 | { 587 | y0 += ystep; 588 | err += dx; 589 | } 590 | err -= dy; 591 | x0 += xstep; 592 | } 593 | for (uint16_t i = dx + 1; i < cache_len; i++) 594 | { 595 | ox = *(cache + (i * 2)); 596 | oy = *(cache + (i * 2) + 1); 597 | if ((ox > 0) || (oy > 0)) 598 | { 599 | write_cache_pixel(ox, oy, BACKGROUND, cross_check_second, cross_check_hour); 600 | } 601 | *(cache + (i * 2)) = 0; 602 | *(cache + (i * 2) + 1) = 0; 603 | } 604 | } 605 | 606 | void write_cache_pixel(uint8_t x, uint8_t y, uint16_t color, bool cross_check_second, bool cross_check_hour) 607 | { 608 | uint8_t *cache = cached_points; 609 | if (cross_check_second) 610 | { 611 | for (int i = 0; i <= SECOND_LEN; i++) 612 | { 613 | if ((x == *(cache++)) && (y == *(cache))) 614 | { 615 | return; 616 | } 617 | cache++; 618 | } 619 | } 620 | if (cross_check_hour) 621 | { 622 | cache = cached_points + ((SECOND_LEN + 1) * 2); 623 | for (int i = 0; i <= HOUR_LEN; i++) 624 | { 625 | if ((x == *(cache++)) && (y == *(cache))) 626 | { 627 | return; 628 | } 629 | cache++; 630 | } 631 | } 632 | tft->writePixel(x, y, color); 633 | } 634 | 635 | void redraw_hands_4_split(void (*redrawFunc)()) 636 | { 637 | if (inSplitRange(1, 1, CENTER, CENTER)) 638 | { 639 | redrawFunc(); 640 | } 641 | 642 | if (inSplitRange(CENTER + 1, 1, CENTER * 2, CENTER)) 643 | { 644 | redrawFunc(); 645 | } 646 | 647 | if (inSplitRange(1, CENTER + 1, CENTER, CENTER * 2)) 648 | { 649 | redrawFunc(); 650 | } 651 | 652 | if (inSplitRange(CENTER + 1, CENTER + 1, CENTER * 2, CENTER * 2)) 653 | { 654 | redrawFunc(); 655 | } 656 | } 657 | 658 | void spi_raw_overwrite_rect() 659 | { 660 | tft->startWrite(); 661 | tft->writeAddrWindow(xMin, yMin, xMax - xMin + 1, yMax - yMin + 1); 662 | for (uint8_t y = yMin; y <= yMax; y++) 663 | { 664 | for (uint8_t x = xMin; x <= xMax; x++) 665 | { 666 | if (inLine(x, y, nsx, nsy, CENTER, CENTER)) 667 | { 668 | tft->writeColor(SECOND_COLOR); // draw second hand 669 | } 670 | else if (inLine(x, y, nhx, nhy, CENTER, CENTER)) 671 | { 672 | tft->writeColor(HOUR_COLOR); // draw hour hand 673 | } 674 | else if (inLine(x, y, nmx, nmy, CENTER, CENTER)) 675 | { 676 | tft->writeColor(MINUTE_COLOR); // draw minute hand 677 | } 678 | else 679 | { 680 | tft->writeColor(BACKGROUND); // over write background color 681 | } 682 | } 683 | } 684 | tft->endWrite(); 685 | } 686 | 687 | bool inSplitRange(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1) 688 | { 689 | bool in_range = false; 690 | 691 | xMin = CENTER; 692 | yMin = CENTER; 693 | xMax = CENTER; 694 | yMax = CENTER; 695 | 696 | if (_ordered_in_range(ohx, x0, x1) && _ordered_in_range(ohy, y0, y1)) 697 | { 698 | in_range = true; 699 | xMin = min(xMin, ohx); 700 | xMax = max(xMax, ohx); 701 | yMin = min(yMin, ohy); 702 | yMax = max(yMax, ohy); 703 | } 704 | 705 | if (_ordered_in_range(omx, x0, x1) && _ordered_in_range(omy, y0, y1)) 706 | { 707 | in_range = true; 708 | xMin = min(xMin, omx); 709 | xMax = max(xMax, omx); 710 | yMin = min(yMin, omy); 711 | yMax = max(yMax, omy); 712 | } 713 | 714 | if (_ordered_in_range(osx, x0, x1) && _ordered_in_range(osy, y0, y1)) 715 | { 716 | in_range = true; 717 | xMin = min(xMin, osx); 718 | xMax = max(xMax, osx); 719 | yMin = min(yMin, osy); 720 | yMax = max(yMax, osy); 721 | } 722 | 723 | if (_ordered_in_range(nhx, x0, x1) && _ordered_in_range(nhy, y0, y1)) 724 | { 725 | in_range = true; 726 | xMin = min(xMin, nhx); 727 | xMax = max(xMax, nhx); 728 | yMin = min(yMin, nhy); 729 | yMax = max(yMax, nhy); 730 | } 731 | 732 | if (_ordered_in_range(nmx, x0, x1) && _ordered_in_range(nmy, y0, y1)) 733 | { 734 | in_range = true; 735 | xMin = min(xMin, nmx); 736 | xMax = max(xMax, nmx); 737 | yMin = min(yMin, nmy); 738 | yMax = max(yMax, nmy); 739 | } 740 | 741 | if (_ordered_in_range(nsx, x0, x1) && _ordered_in_range(nsy, y0, y1)) 742 | { 743 | in_range = true; 744 | xMin = min(xMin, nsx); 745 | xMax = max(xMax, nsx); 746 | yMin = min(yMin, nsy); 747 | yMax = max(yMax, nsy); 748 | } 749 | 750 | return in_range; 751 | } 752 | 753 | bool inLine(uint8_t x, uint8_t y, uint8_t lx0, uint8_t ly0, uint8_t lx1, uint8_t ly1) 754 | { 755 | // range checking 756 | if (!_in_range(x, lx0, lx1)) 757 | { 758 | return false; 759 | } 760 | if (!_in_range(y, ly0, ly1)) 761 | { 762 | return false; 763 | } 764 | 765 | uint8_t dx = _diff(lx1, lx0); 766 | uint8_t dy = _diff(ly1, ly0); 767 | 768 | if (dy > dx) 769 | { 770 | _swap_uint8_t(dx, dy); 771 | _swap_uint8_t(x, y); 772 | _swap_uint8_t(lx0, ly0); 773 | _swap_uint8_t(lx1, ly1); 774 | } 775 | 776 | if (lx0 > lx1) 777 | { 778 | _swap_uint8_t(lx0, lx1); 779 | _swap_uint8_t(ly0, ly1); 780 | } 781 | 782 | uint8_t err = dx >> 1; 783 | int8_t ystep = (ly0 < ly1) ? 1 : -1; 784 | for (; lx0 <= x; lx0++) 785 | { 786 | if (err < dy) 787 | { 788 | ly0 += ystep; 789 | err += dx; 790 | } 791 | err -= dy; 792 | } 793 | 794 | return (y == ly0); 795 | } 796 | -------------------------------------------------------------------------------- /Arduino_Watch/Debug_Printer.h: -------------------------------------------------------------------------------- 1 | #ifndef _DEBUG_PRINTER_H_ 2 | #define _DEBUG_PRINTER_H_ 3 | 4 | // Uncomment to enable printing out nice debug messages. 5 | #define DEBUG_MODE 6 | 7 | #ifdef DEBUG_MODE 8 | // Define where debug output will be printed. 9 | #define DEBUG_PRINTER Serial 10 | #define DEBUG_BEGIN(...) \ 11 | { \ 12 | DEBUG_PRINTER.begin(__VA_ARGS__); \ 13 | delay(1000); \ 14 | } 15 | #define DEBUG_PRINTM(...) \ 16 | { \ 17 | DEBUG_PRINTER.print(millis()); \ 18 | DEBUG_PRINTER.print(__VA_ARGS__); \ 19 | } 20 | #define DEBUG_PRINT(...) \ 21 | { \ 22 | DEBUG_PRINTER.print(__VA_ARGS__); \ 23 | } 24 | #define DEBUG_PRINTMLN(...) \ 25 | { \ 26 | DEBUG_PRINTER.print(millis()); \ 27 | DEBUG_PRINTER.println(__VA_ARGS__); \ 28 | } 29 | #define DEBUG_PRINTLN(...) \ 30 | { \ 31 | DEBUG_PRINTER.println(__VA_ARGS__); \ 32 | } 33 | #else 34 | #define DEBUG_BEGIN(...) \ 35 | { \ 36 | } 37 | #define DEBUG_PRINTM(...) \ 38 | { \ 39 | } 40 | #define DEBUG_PRINT(...) \ 41 | { \ 42 | } 43 | #define DEBUG_PRINTMLN(...) \ 44 | { \ 45 | } 46 | #define DEBUG_PRINTLN(...) \ 47 | { \ 48 | } 49 | #endif 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /Arduino_Watch_Int/Arduino_Watch_Int.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Arduino Watch Pro Micro Version 3 | - read current time from RTC 4 | - draw analog clock face every loop 5 | - shutdown power after 60 seconds 6 | - press power button to restart 7 | */ 8 | // real time clock class 9 | // comment out if no RTC at all 10 | // #define RTC_CLASS RTC_DS1307 11 | #define RTC_CLASS RTC_DS3231 12 | // #define RTC_CLASS RTC_PCF8523 13 | 14 | // auto sleep time (in milliseconds) 15 | // comment out if no need enter sleep, e.g. still in developing stage 16 | #define SLEEP_TIME 20000L 17 | 18 | #define BACKGROUND BLACK 19 | #define MARK_COLOR WHITE 20 | #define SUBMARK_COLOR DARKGREY // LIGHTGREY 21 | #define HOUR_COLOR WHITE 22 | #define MINUTE_COLOR BLUE // LIGHTGREY 23 | #define SECOND_COLOR RED 24 | 25 | #define HOUR_LEN 45 26 | #define MINUTE_LEN 85 27 | #define SECOND_LEN 100 28 | 29 | #define SIXTIETH_DEGREE 6 30 | #define TWELFTH_DEGREE 30 31 | #define RIGHT_ANGLE_DEGREE 90 32 | #define CENTER 120 33 | 34 | #if defined(ESP32) 35 | #define TFT_CS 5 36 | #define TFT_DC 16 37 | #define TFT_RST 17 38 | #define TFT_BL 22 39 | #define WAKEUP_PIN 36 40 | #else 41 | #define TFT_CS 20 42 | #define TFT_DC 19 43 | #define TFT_RST 18 44 | #define TFT_BL 10 45 | #define WAKEUP_PIN 7 46 | // #define RTC_EN_PIN 4 47 | #endif 48 | // #define LED_LEVEL 128 49 | 50 | #include "Debug_Printer.h" 51 | #include "IntTrigonometry.h" 52 | #include 53 | 54 | #ifdef SLEEP_TIME 55 | #if defined(ESP32) 56 | #include 57 | #elif defined(__AVR__) 58 | #include 59 | #endif 60 | #endif // #ifdef SLEEP_TIME 61 | 62 | #ifdef RTC_CLASS 63 | #include 64 | #include 65 | RTC_CLASS rtc; 66 | #else 67 | static uint8_t conv2d(const char *p) 68 | { 69 | uint8_t v = 0; 70 | return (10 * (*p - '0')) + (*++p - '0'); 71 | } 72 | #endif 73 | 74 | //You can use different type of hardware initialization 75 | #ifdef TFT_CS 76 | Arduino_HWSPI *bus = new Arduino_HWSPI(TFT_DC, TFT_CS); 77 | #else 78 | Arduino_HWSPI *bus = new Arduino_HWSPI(TFT_DC); //for display without CS pin 79 | #endif 80 | // 1.3"/1.5" square IPS LCD 240x240 81 | Arduino_ST7789 *tft = new Arduino_ST7789(bus, TFT_RST, 0 /* rotation */, true /* IPS */, 240 /* width */, 240 /* height */, 0 /* col offset 1 */, 80 /* row offset 1 */); 82 | // 2.4" LCD 83 | // Arduino_ST7789 *tft = new Arduino_ST7789(bus, TFT_RST, 1 /* rotation */); 84 | 85 | static int isdeg, imdeg, ihdeg; 86 | static uint8_t osx = CENTER, osy = CENTER, omx = CENTER, omy = CENTER, ohx = CENTER, ohy = CENTER; // Saved H, M, S x & y coords 87 | static uint8_t nsx, nsy, nmx, nmy, nhx, nhy; // H, M, S x & y coords 88 | static uint8_t xMin, yMin, xMax, yMax; // redraw range 89 | static uint8_t hh, mm, ss; 90 | static unsigned long targetTime, sleepTime; // next action time 91 | #ifdef TFT_BL 92 | static bool ledTurnedOn = false; 93 | #endif 94 | #ifdef DEBUG_MODE 95 | static uint16_t loopCount = 0; 96 | #endif 97 | 98 | static uint8_t cached_points[(HOUR_LEN + 1 + MINUTE_LEN + 1 + SECOND_LEN + 1) * 2]; 99 | static int cached_points_idx = 0; 100 | static uint8_t *last_cached_point; 101 | 102 | void setup(void) 103 | { 104 | DEBUG_BEGIN(115200); 105 | DEBUG_PRINTMLN(": start"); 106 | 107 | tft->begin(); 108 | DEBUG_PRINTMLN(": tft->begin()"); 109 | 110 | tft->fillScreen(BACKGROUND); 111 | DEBUG_PRINTMLN(": tft->fillScreen(BACKGROUND)"); 112 | 113 | pinMode(WAKEUP_PIN, INPUT_PULLUP); 114 | #ifdef TFT_BL 115 | pinMode(TFT_BL, OUTPUT); 116 | #endif 117 | 118 | // Draw 60 clock marks 119 | //draw_round_clock_mark(104, 120, 112, 120, 114, 114); 120 | draw_square_clock_mark(102, 120, 108, 120, 114, 120); 121 | //draw_square_clock_mark(72, 90, 78, 90, 84, 90); 122 | DEBUG_PRINTMLN(": Draw 60 clock marks"); 123 | 124 | #ifdef RTC_CLASS 125 | // read date and time from RTC 126 | read_rtc(); 127 | #else 128 | hh = conv2d(__TIME__); 129 | mm = conv2d(__TIME__ + 3); 130 | ss = conv2d(__TIME__ + 6); 131 | #endif 132 | 133 | targetTime = ((millis() / 1000) + 1) * 1000; 134 | 135 | #ifdef SLEEP_TIME 136 | sleepTime = millis() + SLEEP_TIME; 137 | #endif 138 | } 139 | 140 | void loop() 141 | { 142 | unsigned long cur_millis = millis(); 143 | if (cur_millis >= targetTime) 144 | { 145 | targetTime += 1000; 146 | ss++; // Advance second 147 | if (ss == 60) 148 | { 149 | ss = 0; 150 | mm++; // Advance minute 151 | if (mm > 59) 152 | { 153 | mm = 0; 154 | hh++; // Advance hour 155 | if (hh > 23) 156 | { 157 | hh = 0; 158 | } 159 | } 160 | } 161 | } 162 | 163 | // Pre-compute hand degrees, x & y coords for a fast screen update 164 | isdeg = (SIXTIETH_DEGREE * (cur_millis % 1000) / 1000) + (SIXTIETH_DEGREE * ss); // 0-59 (includes millis) 165 | nsx = iCos(isdeg - RIGHT_ANGLE_DEGREE) * SECOND_LEN / I_SCALE + CENTER; 166 | nsy = iSin(isdeg - RIGHT_ANGLE_DEGREE) * SECOND_LEN / I_SCALE + CENTER; 167 | if ((nsx != osx) || (nsy != osy)) 168 | { 169 | imdeg = (isdeg / 60) + (SIXTIETH_DEGREE * mm); // 0-59 (includes seconds) 170 | ihdeg = (imdeg / 12) + (TWELFTH_DEGREE * hh); // 0-11 (includes minutes) 171 | imdeg -= RIGHT_ANGLE_DEGREE; 172 | ihdeg -= RIGHT_ANGLE_DEGREE; 173 | nmx = iCos(imdeg) * MINUTE_LEN / I_SCALE + CENTER; 174 | nmy = iSin(imdeg) * MINUTE_LEN / I_SCALE + CENTER; 175 | nhx = iCos(ihdeg) * HOUR_LEN / I_SCALE + CENTER; 176 | nhy = iSin(ihdeg) * HOUR_LEN / I_SCALE + CENTER; 177 | 178 | // redraw hands 179 | // redraw_hands_cached_lines(); 180 | redraw_hands_cached_draw_and_earse(); 181 | 182 | ohx = nhx; 183 | ohy = nhy; 184 | omx = nmx; 185 | omy = nmy; 186 | osx = nsx; 187 | osy = nsy; 188 | 189 | #ifdef TFT_BL 190 | if (!ledTurnedOn) 191 | { 192 | backlight(true); 193 | } 194 | #endif 195 | 196 | #ifdef DEBUG_MODE 197 | loopCount++; 198 | #endif 199 | } 200 | 201 | #ifdef SLEEP_TIME 202 | if (cur_millis > sleepTime) 203 | { 204 | // enter sleep 205 | #ifdef DEBUG_MODE 206 | DEBUG_PRINTM(": enter sleep, loopCount: "); 207 | DEBUG_PRINTLN(loopCount); 208 | delay(10); 209 | #endif 210 | 211 | enterSleep(); 212 | 213 | #ifdef DEBUG_MODE 214 | loopCount = 0; 215 | #endif 216 | } 217 | #endif // #ifdef SLEEP_TIME 218 | 219 | delay(1); 220 | } 221 | 222 | #ifdef TFT_BL 223 | void backlight(bool enable) 224 | { 225 | if (enable) 226 | { 227 | #ifdef LED_LEVEL 228 | #if defined(ESP32) 229 | ledcSetup(1, 12000, 8); // 12 kHz PWM, 8-bit resolution 230 | ledcAttachPin(TFT_BL, 1); // assign TFT_BL pin to channel 1 231 | ledcWrite(1, LED_LEVEL); // brightness 0 - 255 232 | #else 233 | analogWrite(TFT_BL, LED_LEVEL); 234 | #endif 235 | #else 236 | digitalWrite(TFT_BL, HIGH); 237 | #endif // #ifdef LED_LEVEL 238 | ledTurnedOn = true; 239 | } 240 | else 241 | { 242 | digitalWrite(TFT_BL, LOW); 243 | ledTurnedOn = false; 244 | } 245 | } 246 | #endif 247 | 248 | #ifdef RTC_CLASS 249 | void read_rtc() 250 | { 251 | #ifdef RTC_EN_PIN 252 | pinMode(RTC_EN_PIN, OUTPUT); 253 | digitalWrite(RTC_EN_PIN, HIGH); 254 | #endif 255 | Wire.begin(); 256 | rtc.begin(); 257 | DateTime now = rtc.now(); 258 | hh = now.hour(); 259 | mm = now.minute(); 260 | ss = now.second(); 261 | DEBUG_PRINTM(": read date and time from RTC: "); 262 | DEBUG_PRINT(hh); 263 | DEBUG_PRINT(":"); 264 | DEBUG_PRINT(mm); 265 | DEBUG_PRINT(":"); 266 | DEBUG_PRINTLN(ss); 267 | #if not defined(ESP32) 268 | Wire.end(); 269 | #endif 270 | } 271 | #endif 272 | 273 | #ifdef SLEEP_TIME 274 | void enterSleep() 275 | { 276 | #ifdef TFT_BL 277 | backlight(false); 278 | #endif 279 | tft->displayOff(); 280 | 281 | #if defined(ESP32) 282 | gpio_wakeup_enable((gpio_num_t)WAKEUP_PIN, GPIO_INTR_LOW_LEVEL); 283 | esp_sleep_enable_gpio_wakeup(); 284 | esp_light_sleep_start(); 285 | #elif defined(__AVR__) 286 | // Allow wake up pin to trigger interrupt when the pin goes from high to low. 287 | attachInterrupt(digitalPinToInterrupt(WAKEUP_PIN), wakeUp, FALLING); 288 | 289 | // Enter power down state with ADC and BOD module disabled. 290 | // Wake up when wake up pin is low. 291 | LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF); 292 | 293 | // Disable external pin interrupt on wake up pin. 294 | detachInterrupt(digitalPinToInterrupt(WAKEUP_PIN)); 295 | #endif 296 | 297 | tft->displayOn(); 298 | 299 | // read date and time from RTC 300 | #ifdef RTC_CLASS 301 | read_rtc(); 302 | #endif 303 | 304 | targetTime = ((millis() / 1000) + 1) * 1000; 305 | 306 | #ifdef SLEEP_TIME 307 | sleepTime = millis() + SLEEP_TIME; 308 | #endif 309 | } 310 | 311 | #if defined(__AVR__) 312 | void wakeUp() 313 | { 314 | // Just a handler for the pin interrupt. 315 | } 316 | #endif 317 | #endif // #ifdef SLEEP_TIME 318 | 319 | void draw_round_clock_mark(uint8_t innerR1, uint8_t outerR1, uint8_t innerR2, uint8_t outerR2, uint8_t innerR3, uint8_t outerR3) 320 | { 321 | int x, y; 322 | uint8_t x0, x1, y0, y1, innerR, outerR; 323 | uint16_t c; 324 | 325 | for (int i = 0; i < 60; i++) 326 | { 327 | if ((i % 15) == 0) 328 | { 329 | innerR = innerR1; 330 | outerR = outerR1; 331 | c = MARK_COLOR; 332 | } 333 | else if ((i % 5) == 0) 334 | { 335 | innerR = innerR2; 336 | outerR = outerR2; 337 | c = MARK_COLOR; 338 | } 339 | else 340 | { 341 | innerR = innerR3; 342 | outerR = outerR3; 343 | c = SUBMARK_COLOR; 344 | } 345 | 346 | imdeg = (SIXTIETH_DEGREE * i) - RIGHT_ANGLE_DEGREE; 347 | x = iCos(imdeg); 348 | y = iSin(imdeg); 349 | x0 = x * outerR / I_SCALE + CENTER; 350 | y0 = y * outerR / I_SCALE + CENTER; 351 | x1 = x * innerR / I_SCALE + CENTER; 352 | y1 = y * innerR / I_SCALE + CENTER; 353 | 354 | tft->drawLine(x0, y0, x1, y1, c); 355 | DEBUG_PRINTM(" i: "); 356 | DEBUG_PRINT(i); 357 | DEBUG_PRINT(", x0: "); 358 | DEBUG_PRINT(x0); 359 | DEBUG_PRINT(", y0: "); 360 | DEBUG_PRINT(y0); 361 | DEBUG_PRINT(", x1: "); 362 | DEBUG_PRINT(x1); 363 | DEBUG_PRINT(", y1: "); 364 | DEBUG_PRINTLN(y1); 365 | } 366 | } 367 | 368 | void draw_square_clock_mark(uint8_t innerR1, uint8_t outerR1, uint8_t innerR2, uint8_t outerR2, uint8_t innerR3, uint8_t outerR3) 369 | { 370 | int x, y; 371 | uint8_t x0, x1, y0, y1, innerR, outerR; 372 | uint16_t c; 373 | 374 | for (int i = 0; i < 60; i++) 375 | { 376 | if ((i % 15) == 0) 377 | { 378 | innerR = innerR1; 379 | outerR = outerR1; 380 | c = MARK_COLOR; 381 | } 382 | else if ((i % 5) == 0) 383 | { 384 | innerR = innerR2; 385 | outerR = outerR2; 386 | c = MARK_COLOR; 387 | } 388 | else 389 | { 390 | innerR = innerR3; 391 | outerR = outerR3; 392 | c = SUBMARK_COLOR; 393 | } 394 | 395 | if ((i >= 53) || (i < 8)) 396 | { 397 | x = iTan(SIXTIETH_DEGREE * i); 398 | x0 = CENTER + (x * outerR / I_SCALE); 399 | y0 = CENTER + (1 - outerR); 400 | x1 = CENTER + (x * innerR / I_SCALE); 401 | y1 = CENTER + (1 - innerR); 402 | } 403 | else if (i < 23) 404 | { 405 | y = iTan((SIXTIETH_DEGREE * i) - RIGHT_ANGLE_DEGREE); 406 | x0 = CENTER + (outerR); 407 | y0 = CENTER + (y * outerR / I_SCALE); 408 | x1 = CENTER + (innerR); 409 | y1 = CENTER + (y * innerR / I_SCALE); 410 | } 411 | else if (i < 38) 412 | { 413 | x = iTan(SIXTIETH_DEGREE * i); 414 | x0 = CENTER - (x * outerR / I_SCALE); 415 | y0 = CENTER + (outerR); 416 | x1 = CENTER - (x * innerR / I_SCALE); 417 | y1 = CENTER + (innerR); 418 | } 419 | else if (i < 53) 420 | { 421 | y = iTan((SIXTIETH_DEGREE * i) - RIGHT_ANGLE_DEGREE); 422 | x0 = CENTER + (1 - outerR); 423 | y0 = CENTER - (y * outerR / I_SCALE); 424 | x1 = CENTER + (1 - innerR); 425 | y1 = CENTER - (y * innerR / I_SCALE); 426 | } 427 | tft->drawLine(x0, y0, x1, y1, c); 428 | DEBUG_PRINTM(" i: "); 429 | DEBUG_PRINT(i); 430 | DEBUG_PRINT(", x0: "); 431 | DEBUG_PRINT(x0); 432 | DEBUG_PRINT(", y0: "); 433 | DEBUG_PRINT(y0); 434 | DEBUG_PRINT(", x1: "); 435 | DEBUG_PRINT(x1); 436 | DEBUG_PRINT(", y1: "); 437 | DEBUG_PRINTLN(y1); 438 | } 439 | } 440 | 441 | void redraw_hands_cached_lines() 442 | { 443 | cached_points_idx = 0; 444 | last_cached_point = cached_points; 445 | write_cached_line(nhx, nhy, CENTER, CENTER, HOUR_COLOR, true, false); // cache new hour hand 446 | write_cached_line(nsx, nsy, CENTER, CENTER, SECOND_COLOR, true, false); // cache new second hand 447 | tft->startWrite(); 448 | write_cached_line(nmx, nmy, CENTER, CENTER, MINUTE_COLOR, true, true); // cache and draw new minute hand 449 | write_cached_line(omx, omy, CENTER, CENTER, BACKGROUND, false, true); // earse old minute hand 450 | tft->writeLine(nhx, nhy, CENTER, CENTER, HOUR_COLOR); // draw new hour hand 451 | write_cached_line(ohx, ohy, CENTER, CENTER, BACKGROUND, false, true); // earse old hour hand 452 | tft->writeLine(nsx, nsy, CENTER, CENTER, SECOND_COLOR); // draw new second hand 453 | write_cached_line(osx, osy, CENTER, CENTER, BACKGROUND, false, true); // earse old second hand 454 | tft->endWrite(); 455 | } 456 | 457 | void write_cached_line(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1, uint16_t color, bool save, bool actual_draw) 458 | { 459 | #if defined(ESP8266) 460 | yield(); 461 | #endif 462 | bool steep = _diff(y1, y0) > _diff(x1, x0); 463 | if (steep) 464 | { 465 | _swap_uint8_t(x0, y0); 466 | _swap_uint8_t(x1, y1); 467 | } 468 | 469 | if (x0 > x1) 470 | { 471 | _swap_uint8_t(x0, x1); 472 | _swap_uint8_t(y0, y1); 473 | } 474 | 475 | uint8_t dx, dy; 476 | dx = x1 - x0; 477 | dy = _diff(y1, y0); 478 | 479 | uint8_t err = dx / 2; 480 | int8_t ystep = (y0 < y1) ? 1 : -1; 481 | for (; x0 <= x1; x0++) 482 | { 483 | if (steep) 484 | { 485 | write_cache_point(y0, x0, color, save, actual_draw); 486 | } 487 | else 488 | { 489 | write_cache_point(x0, y0, color, save, actual_draw); 490 | } 491 | if (err < dy) 492 | { 493 | y0 += ystep; 494 | err += dx; 495 | } 496 | err -= dy; 497 | } 498 | } 499 | 500 | void write_cache_point(uint8_t x, uint8_t y, uint16_t color, bool save, bool actual_draw) 501 | { 502 | uint8_t *current_point = cached_points; 503 | for (int i = 0; i < cached_points_idx; i++) 504 | { 505 | if ((x == *(current_point++)) && (y == *(current_point))) 506 | { 507 | return; 508 | } 509 | current_point++; 510 | } 511 | if (save) 512 | { 513 | cached_points_idx++; 514 | *(last_cached_point++) = x; 515 | *(last_cached_point++) = y; 516 | } 517 | if (actual_draw) 518 | { 519 | tft->writePixel(x, y, color); 520 | } 521 | } 522 | 523 | void redraw_hands_cached_draw_and_earse() 524 | { 525 | tft->startWrite(); 526 | draw_and_earse_cached_line(CENTER, CENTER, nsx, nsy, SECOND_COLOR, cached_points, SECOND_LEN + 1, false, false); 527 | draw_and_earse_cached_line(CENTER, CENTER, nhx, nhy, HOUR_COLOR, cached_points + ((SECOND_LEN + 1) * 2), HOUR_LEN + 1, true, false); 528 | draw_and_earse_cached_line(CENTER, CENTER, nmx, nmy, MINUTE_COLOR, cached_points + ((SECOND_LEN + 1 + HOUR_LEN + 1) * 2), MINUTE_LEN + 1, true, true); 529 | tft->endWrite(); 530 | } 531 | 532 | void draw_and_earse_cached_line(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1, uint16_t color, uint8_t *cache, uint16_t cache_len, bool cross_check_second, bool cross_check_hour) 533 | { 534 | #if defined(ESP8266) 535 | yield(); 536 | #endif 537 | bool steep = _diff(y1, y0) > _diff(x1, x0); 538 | if (steep) 539 | { 540 | _swap_uint8_t(x0, y0); 541 | _swap_uint8_t(x1, y1); 542 | } 543 | 544 | uint8_t dx, dy; 545 | dx = _diff(x1, x0); 546 | dy = _diff(y1, y0); 547 | 548 | uint8_t err = dx / 2; 549 | int8_t xstep = (x0 < x1) ? 1 : -1; 550 | int8_t ystep = (y0 < y1) ? 1 : -1; 551 | x1 += xstep; 552 | uint8_t x, y, ox, oy; 553 | for (uint16_t i = 0; i <= dx; i++) 554 | { 555 | if (steep) 556 | { 557 | x = y0; 558 | y = x0; 559 | } 560 | else 561 | { 562 | x = x0; 563 | y = y0; 564 | } 565 | ox = *(cache + (i * 2)); 566 | oy = *(cache + (i * 2) + 1); 567 | if ((x == ox) && (y == oy)) 568 | { 569 | if (cross_check_second || cross_check_hour) 570 | { 571 | write_cache_pixel(x, y, color, cross_check_second, cross_check_hour); 572 | } 573 | } 574 | else 575 | { 576 | write_cache_pixel(x, y, color, cross_check_second, cross_check_hour); 577 | if ((ox > 0) || (oy > 0)) { 578 | write_cache_pixel(ox, oy, BACKGROUND, cross_check_second, cross_check_hour); 579 | } 580 | *(cache + (i * 2)) = x; 581 | *(cache + (i * 2) + 1) = y; 582 | } 583 | if (err < dy) 584 | { 585 | y0 += ystep; 586 | err += dx; 587 | } 588 | err -= dy; 589 | x0 += xstep; 590 | } 591 | for (uint16_t i = dx + 1; i < cache_len; i++) 592 | { 593 | ox = *(cache + (i * 2)); 594 | oy = *(cache + (i * 2) + 1); 595 | if ((ox > 0) || (oy > 0)) 596 | { 597 | write_cache_pixel(ox, oy, BACKGROUND, cross_check_second, cross_check_hour); 598 | } 599 | *(cache + (i * 2)) = 0; 600 | *(cache + (i * 2) + 1) = 0; 601 | } 602 | } 603 | 604 | void write_cache_pixel(uint8_t x, uint8_t y, uint16_t color, bool cross_check_second, bool cross_check_hour) 605 | { 606 | uint8_t *cache = cached_points; 607 | if (cross_check_second) 608 | { 609 | for (int i = 0; i <= SECOND_LEN; i++) 610 | { 611 | if ((x == *(cache++)) && (y == *(cache))) 612 | { 613 | return; 614 | } 615 | cache++; 616 | } 617 | } 618 | if (cross_check_hour) 619 | { 620 | cache = cached_points + ((SECOND_LEN + 1) * 2); 621 | for (int i = 0; i <= HOUR_LEN; i++) 622 | { 623 | if ((x == *(cache++)) && (y == *(cache))) 624 | { 625 | return; 626 | } 627 | cache++; 628 | } 629 | } 630 | tft->writePixel(x, y, color); 631 | } 632 | -------------------------------------------------------------------------------- /Arduino_Watch_Int/Debug_Printer.h: -------------------------------------------------------------------------------- 1 | #ifndef _DEBUG_PRINTER_H_ 2 | #define _DEBUG_PRINTER_H_ 3 | 4 | // Uncomment to enable printing out nice debug messages. 5 | #define DEBUG_MODE 6 | 7 | #ifdef DEBUG_MODE 8 | // Define where debug output will be printed. 9 | #define DEBUG_PRINTER Serial 10 | #define DEBUG_BEGIN(...) \ 11 | { \ 12 | DEBUG_PRINTER.begin(__VA_ARGS__); \ 13 | delay(1000); \ 14 | } 15 | #define DEBUG_PRINTM(...) \ 16 | { \ 17 | DEBUG_PRINTER.print(millis()); \ 18 | DEBUG_PRINTER.print(__VA_ARGS__); \ 19 | } 20 | #define DEBUG_PRINT(...) \ 21 | { \ 22 | DEBUG_PRINTER.print(__VA_ARGS__); \ 23 | } 24 | #define DEBUG_PRINTMLN(...) \ 25 | { \ 26 | DEBUG_PRINTER.print(millis()); \ 27 | DEBUG_PRINTER.println(__VA_ARGS__); \ 28 | } 29 | #define DEBUG_PRINTLN(...) \ 30 | { \ 31 | DEBUG_PRINTER.println(__VA_ARGS__); \ 32 | } 33 | #else 34 | #define DEBUG_BEGIN(...) \ 35 | { \ 36 | } 37 | #define DEBUG_PRINTM(...) \ 38 | { \ 39 | } 40 | #define DEBUG_PRINT(...) \ 41 | { \ 42 | } 43 | #define DEBUG_PRINTMLN(...) \ 44 | { \ 45 | } 46 | #define DEBUG_PRINTLN(...) \ 47 | { \ 48 | } 49 | #endif 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /Arduino_Watch_Int/IntTrigonometry.h: -------------------------------------------------------------------------------- 1 | /* 2 | * start revise from: 3 | * https://forum.arduino.cc/index.php?topic=69723.0 4 | */ 5 | #ifndef _INTTRIGONOMETRY_H_ 6 | #define _INTTRIGONOMETRY_H_ 7 | 8 | #ifdef __AVR__ 9 | #include 10 | #include 11 | #elif defined(ESP8266) 12 | #include 13 | #elif defined(__IMXRT1052__) || defined(__IMXRT1062__) 14 | // PROGMEM is defefind for T4 to place data in specific memory section 15 | #undef PROGMEM 16 | #define PROGMEM 17 | #else 18 | #define PROGMEM 19 | #endif 20 | 21 | #define I_SCALE 127 22 | 23 | // 91 x 2 bytes ==> 182 bytes 24 | static const int8_t iSinTable[] PROGMEM = { 25 | 0, 26 | 2, 27 | 4, 28 | 7, 29 | 9, 30 | 11, 31 | 13, 32 | 15, 33 | 18, 34 | 20, 35 | 22, 36 | 24, 37 | 26, 38 | 29, 39 | 31, 40 | 33, 41 | 35, 42 | 37, 43 | 39, 44 | 41, 45 | 43, 46 | 46, 47 | 48, 48 | 50, 49 | 52, 50 | 54, 51 | 56, 52 | 58, 53 | 60, 54 | 62, 55 | 64, 56 | 65, 57 | 67, 58 | 69, 59 | 71, 60 | 73, 61 | 75, 62 | 76, 63 | 78, 64 | 80, 65 | 82, 66 | 83, 67 | 85, 68 | 87, 69 | 88, 70 | 90, 71 | 91, 72 | 93, 73 | 94, 74 | 96, 75 | 97, 76 | 99, 77 | 100, 78 | 101, 79 | 103, 80 | 104, 81 | 105, 82 | 107, 83 | 108, 84 | 109, 85 | 110, 86 | 111, 87 | 112, 88 | 113, 89 | 114, 90 | 115, 91 | 116, 92 | 117, 93 | 118, 94 | 119, 95 | 119, 96 | 120, 97 | 121, 98 | 121, 99 | 122, 100 | 123, 101 | 123, 102 | 124, 103 | 124, 104 | 125, 105 | 125, 106 | 125, 107 | 126, 108 | 126, 109 | 126, 110 | 127, 111 | 127, 112 | 127, 113 | 127, 114 | 127, 115 | 127, 116 | }; 117 | 118 | int iSin(int x) 119 | { 120 | boolean pos = true; // positive - keeps an eye on the sign. 121 | if (x < 0) 122 | { 123 | x = -x; 124 | pos = !pos; 125 | } 126 | while (x >= 360) { 127 | x -= 360; 128 | } 129 | if (x > 180) 130 | { 131 | x -= 180; 132 | pos = !pos; 133 | } 134 | if (x > 90) { 135 | x = 180 - x; 136 | } 137 | int8_t v = pgm_read_byte(&iSinTable[x]); 138 | if (pos) { 139 | return v; 140 | } 141 | return -v; 142 | } 143 | 144 | int iCos(int x) 145 | { 146 | return iSin(x + 90); 147 | } 148 | 149 | int iTan(int x) 150 | { 151 | return iSin(x) * I_SCALE / iCos(x); 152 | } 153 | 154 | #endif -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ArduinoWatch 2 | This is an Arduino watch core with analog watch face rewrite from TFT_eSPI TFT_Clock example. 3 | This core optimized for slow MCU such as Arduino Pro Micro 8 MHz. 4 | 5 | ## Depended libraries 6 | - https://github.com/adafruit/RTClib.git 7 | - https://github.com/moononournation/Arduino_GFX.git 8 | - https://github.com/rocketscream/Low-Power.git 9 | -------------------------------------------------------------------------------- /RTClibSetRTC/RTClibSetRTC.ino: -------------------------------------------------------------------------------- 1 | // Date and time functions using a DS3231 RTC connected via I2C and Wire lib 2 | #include 3 | #include "RTClib.h" 4 | 5 | RTC_DS3231 rtc; 6 | //RTC_PCF8523 rtc; 7 | 8 | char daysOfTheWeek[7][12] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}; 9 | 10 | void setup () { 11 | 12 | #ifndef ESP8266 13 | while (!Serial); // for Leonardo/Micro/Zero 14 | #endif 15 | 16 | Serial.begin(115200); 17 | 18 | delay(3000); // wait for console opening 19 | 20 | if (! rtc.begin()) { 21 | Serial.println("Couldn't find RTC"); 22 | while (1); 23 | } 24 | 25 | Serial.println("RTC lost power, lets set the time!"); 26 | // following line sets the RTC to the date & time this sketch was compiled 27 | rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); 28 | } 29 | 30 | void loop () { 31 | DateTime now = rtc.now(); 32 | 33 | Serial.print(now.year(), DEC); 34 | Serial.print('/'); 35 | Serial.print(now.month(), DEC); 36 | Serial.print('/'); 37 | Serial.print(now.day(), DEC); 38 | Serial.print(" ("); 39 | Serial.print(daysOfTheWeek[now.dayOfTheWeek()]); 40 | Serial.print(") "); 41 | Serial.print(now.hour(), DEC); 42 | Serial.print(':'); 43 | Serial.print(now.minute(), DEC); 44 | Serial.print(':'); 45 | Serial.println(now.second(), DEC); 46 | 47 | delay(1000); 48 | } 49 | -------------------------------------------------------------------------------- /benchmark/Debug_Printer.h: -------------------------------------------------------------------------------- 1 | #ifndef _DEBUG_PRINTER_H_ 2 | #define _DEBUG_PRINTER_H_ 3 | 4 | // Uncomment to enable printing out nice debug messages. 5 | #define DEBUG_MODE 6 | 7 | #ifdef DEBUG_MODE 8 | // Define where debug output will be printed. 9 | #define DEBUG_PRINTER Serial 10 | #define DEBUG_BEGIN(...) \ 11 | { \ 12 | DEBUG_PRINTER.begin(__VA_ARGS__); \ 13 | delay(1000); \ 14 | } 15 | #define DEBUG_PRINTM(...) \ 16 | { \ 17 | DEBUG_PRINTER.print(millis()); \ 18 | DEBUG_PRINTER.print(__VA_ARGS__); \ 19 | } 20 | #define DEBUG_PRINT(...) \ 21 | { \ 22 | DEBUG_PRINTER.print(__VA_ARGS__); \ 23 | } 24 | #define DEBUG_PRINTMLN(...) \ 25 | { \ 26 | DEBUG_PRINTER.print(millis()); \ 27 | DEBUG_PRINTER.println(__VA_ARGS__); \ 28 | } 29 | #define DEBUG_PRINTLN(...) \ 30 | { \ 31 | DEBUG_PRINTER.println(__VA_ARGS__); \ 32 | } 33 | #else 34 | #define DEBUG_BEGIN(...) \ 35 | { \ 36 | } 37 | #define DEBUG_PRINTM(...) \ 38 | { \ 39 | } 40 | #define DEBUG_PRINT(...) \ 41 | { \ 42 | } 43 | #define DEBUG_PRINTMLN(...) \ 44 | { \ 45 | } 46 | #define DEBUG_PRINTLN(...) \ 47 | { \ 48 | } 49 | #endif 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /benchmark/IntTrigonometry.h: -------------------------------------------------------------------------------- 1 | /* 2 | * start revise from: 3 | * https://forum.arduino.cc/index.php?topic=69723.0 4 | */ 5 | #ifndef _INTTRIGONOMETRY_H_ 6 | #define _INTTRIGONOMETRY_H_ 7 | 8 | #ifdef __AVR__ 9 | #include 10 | #include 11 | #elif defined(ESP8266) 12 | #include 13 | #elif defined(__IMXRT1052__) || defined(__IMXRT1062__) 14 | // PROGMEM is defefind for T4 to place data in specific memory section 15 | #undef PROGMEM 16 | #define PROGMEM 17 | #else 18 | #define PROGMEM 19 | #endif 20 | 21 | // 91 x 2 bytes ==> 182 bytes 22 | unsigned int isinTable16[] = { 23 | 0, 1144, 2287, 3430, 4571, 5712, 6850, 7987, 9121, 10252, 11380, 24 | 12505, 13625, 14742, 15854, 16962, 18064, 19161, 20251, 21336, 22414, 25 | 23486, 24550, 25607, 26655, 27696, 28729, 29752, 30767, 31772, 32768, 26 | 27 | 33753, 34728, 35693, 36647, 37589, 38521, 39440, 40347, 41243, 42125, 28 | 42995, 43851, 44695, 45524, 46340, 47142, 47929, 48702, 49460, 50203, 29 | 50930, 51642, 52339, 53019, 53683, 54331, 54962, 55577, 56174, 56755, 30 | 31 | 57318, 57864, 58392, 58902, 59395, 59869, 60325, 60763, 61182, 61583, 32 | 61965, 62327, 62671, 62996, 63302, 63588, 63855, 64103, 64331, 64539, 33 | 64728, 64897, 65047, 65176, 65286, 65375, 65445, 65495, 65525, 65535, 34 | }; 35 | 36 | // 91 bytes 37 | uint8_t isinTable8[] = { 38 | 0, 4, 9, 13, 18, 22, 27, 31, 35, 40, 44, 39 | 49, 53, 57, 62, 66, 70, 75, 79, 83, 87, 40 | 91, 96, 100, 104, 108, 112, 116, 120, 124, 128, 41 | 42 | 131, 135, 139, 143, 146, 150, 153, 157, 160, 164, 43 | 167, 171, 174, 177, 180, 183, 186, 190, 192, 195, 44 | 198, 201, 204, 206, 209, 211, 214, 216, 219, 221, 45 | 46 | 223, 225, 227, 229, 231, 233, 235, 236, 238, 240, 47 | 241, 243, 244, 245, 246, 247, 248, 249, 250, 251, 48 | 252, 253, 253, 254, 254, 254, 255, 255, 255, 255, 49 | }; 50 | 51 | 52 | float isin(long x) 53 | { 54 | boolean pos = true; // positive - keeps an eye on the sign. 55 | if (x < 0) 56 | { 57 | x = -x; 58 | pos = !pos; 59 | } 60 | if (x >= 360) x %= 360; 61 | if (x > 180) 62 | { 63 | x -= 180; 64 | pos = !pos; 65 | } 66 | if (x > 90) x = 180 - x; 67 | // if (pos) return isinTable8[x] * 0.003921568627; // = /255.0 68 | // return isinTable8[x] * -0.003921568627 ; 69 | if (pos) return isinTable16[x] * 0.0000152590219; // = /65535.0 70 | return isinTable16[x] * -0.0000152590219 ; 71 | } 72 | 73 | float icos(long x) 74 | { 75 | return isin(x+90); 76 | } 77 | 78 | float itan(long x) 79 | { 80 | return isin(x) / icos(x); 81 | } 82 | 83 | float fsin(float d) 84 | { 85 | float a = isin(d); 86 | float b = isin(d+1); 87 | return a + (d-int(d)) * (b-a); 88 | } 89 | 90 | 91 | 92 | #define I_SCALE 127 93 | 94 | // 91 x 2 bytes ==> 182 bytes 95 | static const int8_t iSinTable[] PROGMEM = { 96 | 0, 97 | 2, 98 | 4, 99 | 7, 100 | 9, 101 | 11, 102 | 13, 103 | 15, 104 | 18, 105 | 20, 106 | 22, 107 | 24, 108 | 26, 109 | 29, 110 | 31, 111 | 33, 112 | 35, 113 | 37, 114 | 39, 115 | 41, 116 | 43, 117 | 46, 118 | 48, 119 | 50, 120 | 52, 121 | 54, 122 | 56, 123 | 58, 124 | 60, 125 | 62, 126 | 64, 127 | 65, 128 | 67, 129 | 69, 130 | 71, 131 | 73, 132 | 75, 133 | 76, 134 | 78, 135 | 80, 136 | 82, 137 | 83, 138 | 85, 139 | 87, 140 | 88, 141 | 90, 142 | 91, 143 | 93, 144 | 94, 145 | 96, 146 | 97, 147 | 99, 148 | 100, 149 | 101, 150 | 103, 151 | 104, 152 | 105, 153 | 107, 154 | 108, 155 | 109, 156 | 110, 157 | 111, 158 | 112, 159 | 113, 160 | 114, 161 | 115, 162 | 116, 163 | 117, 164 | 118, 165 | 119, 166 | 119, 167 | 120, 168 | 121, 169 | 121, 170 | 122, 171 | 123, 172 | 123, 173 | 124, 174 | 124, 175 | 125, 176 | 125, 177 | 125, 178 | 126, 179 | 126, 180 | 126, 181 | 127, 182 | 127, 183 | 127, 184 | 127, 185 | 127, 186 | 127, 187 | }; 188 | 189 | int iSin(int x) 190 | { 191 | boolean pos = true; // positive - keeps an eye on the sign. 192 | if (x < 0) 193 | { 194 | x = -x; 195 | pos = !pos; 196 | } 197 | while (x >= 360) { 198 | x -= 360; 199 | } 200 | if (x > 180) 201 | { 202 | x -= 180; 203 | pos = !pos; 204 | } 205 | if (x > 90) { 206 | x = 180 - x; 207 | } 208 | int8_t v = pgm_read_byte(&iSinTable[x]); 209 | if (pos) { 210 | return v; 211 | } 212 | return -v; 213 | } 214 | 215 | int iCos(int x) 216 | { 217 | return iSin(x + 90); 218 | } 219 | 220 | int iTan(int x) 221 | { 222 | return iSin(x) * I_SCALE / iCos(x); 223 | } 224 | 225 | #endif -------------------------------------------------------------------------------- /benchmark/benchmark.ino: -------------------------------------------------------------------------------- 1 | #define BACKGROUND BLACK 2 | #define MARK_COLOR WHITE 3 | #define SUBMARK_COLOR 0x7BEF //0xBDF7 4 | #define HOUR_COLOR WHITE 5 | #define MINUTE_COLOR BLUE // 0x7BEF 6 | #define SECOND_COLOR RED 7 | 8 | #define HOUR_LEN 50 9 | #define MINUTE_LEN 90 10 | #define SECOND_LEN 100 11 | 12 | #define SIXTIETH 0.016666667 13 | #define TWELFTH 0.08333333 14 | #define SIXTIETH_RADIAN 0.10471976 15 | #define TWELFTH_RADIAN 0.52359878 16 | #define RIGHT_ANGLE_RADIAN 1.5707963 17 | #define SIXTIETH_DEGREE 6 18 | #define TWELFTH_DEGREE 30 19 | #define RIGHT_ANGLE_DEGREE 90 20 | #define CENTER 120 21 | 22 | #ifdef ESP32 23 | #define TFT_CS 5 24 | #define TFT_DC 16 25 | #define TFT_RST 17 26 | #define TFT_BL 22 27 | #else 28 | #define TFT_DC 19 29 | #define TFT_RST 18 30 | #define TFT_BL 10 31 | #endif 32 | #define LED_LEVEL 128 33 | 34 | #include "Debug_Printer.h" 35 | #include "IntTrigonometry.h" 36 | #include 37 | 38 | //You can use different type of hardware initialization 39 | #ifdef TFT_CS 40 | Arduino_HWSPI *bus = new Arduino_HWSPI(TFT_DC, TFT_CS); 41 | #else 42 | Arduino_HWSPI *bus = new Arduino_HWSPI(TFT_DC); //for display without CS pin 43 | #endif 44 | // 1.3"/1.5" square IPS LCD 240x240 45 | Arduino_ST7789 *tft = new Arduino_ST7789(bus, TFT_RST, 0 /* rotation */, true /* IPS */, 240 /* width */, 240 /* height */, 0 /* col offset 1 */, 80 /* row offset 1 */); 46 | // 2.4" LCD 47 | // Arduino_ST7789 *tft = new Arduino_ST7789(bus, TFT_RST, 1 /* rotation */); 48 | 49 | static float sdeg, mdeg, hdeg; 50 | static int isdeg, imdeg, ihdeg; 51 | static uint8_t osx = CENTER, osy = CENTER, omx = CENTER, omy = CENTER, ohx = CENTER, ohy = CENTER; // Saved H, M, S x & y coords 52 | static uint8_t nsx, nsy, nmx, nmy, nhx, nhy; // H, M, S x & y coords 53 | static uint8_t xMin, yMin, xMax, yMax; // redraw range 54 | 55 | static uint8_t hh = 0, mm = 0, ss = 0; 56 | static uint8_t tmp; 57 | static uint16_t drawCount; 58 | 59 | static uint8_t cached_points[(HOUR_LEN + 1 + MINUTE_LEN + 1 + SECOND_LEN + 1) * 2]; 60 | static int cached_points_idx = 0; 61 | static uint8_t *last_cached_point; 62 | 63 | void setup(void) 64 | { 65 | DEBUG_BEGIN(115200); 66 | DEBUG_PRINTMLN(": start"); 67 | 68 | #ifdef ESP32 69 | tft->begin(80000000); 70 | #else 71 | tft->begin(); 72 | #endif 73 | DEBUG_PRINTMLN(": tft->begin()"); 74 | 75 | tft->fillScreen(BACKGROUND); 76 | DEBUG_PRINTMLN(": tft->fillScreen(BACKGROUND)"); 77 | 78 | #ifdef TFT_BL 79 | pinMode(TFT_BL, OUTPUT); 80 | #if defined(ESP32) 81 | ledcSetup(1, 12000, 8); // 12 kHz PWM, 8-bit resolution 82 | ledcAttachPin(TFT_BL, 1); // assign TFT_BL pin to channel 1 83 | ledcWrite(1, LED_LEVEL); // brightness 0 - 255 84 | #else 85 | analogWrite(TFT_BL, LED_LEVEL); 86 | #endif 87 | #endif // #ifdef TFT_BL 88 | 89 | //draw_round_clock_mark(104, 120, 112, 120, 114, 114); 90 | draw_square_clock_mark(102, 120, 108, 120, 114, 120); 91 | DEBUG_PRINTMLN(": Draw 60 clock marks"); 92 | 93 | tft->setTextColor(WHITE, BLACK); 94 | tft->setTextSize(2); 95 | } 96 | 97 | void loop() 98 | { 99 | unsigned long maxLoopMs = 0, minLoopMs = 9999, startMillis = millis(); 100 | unsigned long cur_millis, cur_ms, i; 101 | 102 | DEBUG_PRINTMLN(": float version"); 103 | for (i = 0; i < 60000L; i += 167) // Around 1 degree 104 | { 105 | cur_millis = millis(); 106 | ss = i / 1000; 107 | sdeg = SIXTIETH_RADIAN * ((0.001 * (i % 1000)) + ss); // 0-59 (includes millis) 108 | mdeg = (SIXTIETH * sdeg) + (SIXTIETH_RADIAN * mm); // 0-59 (includes seconds) 109 | hdeg = (TWELFTH * mdeg) + (TWELFTH_RADIAN * hh); // 0-11 (includes minutes) 110 | sdeg -= RIGHT_ANGLE_RADIAN; 111 | mdeg -= RIGHT_ANGLE_RADIAN; 112 | hdeg -= RIGHT_ANGLE_RADIAN; 113 | nhx = cos(hdeg) * HOUR_LEN + CENTER; 114 | nhy = sin(hdeg) * HOUR_LEN + CENTER; 115 | nmx = cos(mdeg) * MINUTE_LEN + CENTER; 116 | nmy = sin(mdeg) * MINUTE_LEN + CENTER; 117 | nsx = cos(sdeg) * SECOND_LEN + CENTER; 118 | nsy = sin(sdeg) * SECOND_LEN + CENTER; 119 | cur_ms = millis() - cur_millis; 120 | maxLoopMs = max(maxLoopMs, cur_ms); 121 | minLoopMs = min(minLoopMs, cur_ms); 122 | } 123 | DEBUG_PRINTM(": maxLoopMs: "); 124 | DEBUG_PRINT(maxLoopMs); 125 | DEBUG_PRINT(", minLoopMs: "); 126 | DEBUG_PRINT(minLoopMs); 127 | DEBUG_PRINT(", TotalMs: "); 128 | DEBUG_PRINTLN(millis() - startMillis); 129 | 130 | maxLoopMs = 0, minLoopMs = 9999, startMillis = millis(); 131 | DEBUG_PRINTMLN(": ifloat version"); 132 | for (i = 0; i < 60000L; i += 167) // Around 1 degree 133 | { 134 | cur_millis = millis(); 135 | ss = i / 1000; 136 | isdeg = (SIXTIETH_DEGREE * (i % 1000) / 1000) + (SIXTIETH_DEGREE * ss); // 0-59 (includes millis) 137 | imdeg = (isdeg / 60) + (SIXTIETH_DEGREE * mm); // 0-59 (includes seconds) 138 | ihdeg = (imdeg / 12) + (TWELFTH_DEGREE * hh); // 0-11 (includes minutes) 139 | isdeg -= RIGHT_ANGLE_DEGREE; 140 | imdeg -= RIGHT_ANGLE_DEGREE; 141 | ihdeg -= RIGHT_ANGLE_DEGREE; 142 | nhx = icos(ihdeg) * HOUR_LEN + CENTER; 143 | nhy = isin(ihdeg) * HOUR_LEN + CENTER; 144 | nmx = icos(imdeg) * MINUTE_LEN + CENTER; 145 | nmy = isin(imdeg) * MINUTE_LEN + CENTER; 146 | nsx = icos(isdeg) * SECOND_LEN + CENTER; 147 | nsy = isin(isdeg) * SECOND_LEN + CENTER; 148 | cur_ms = millis() - cur_millis; 149 | maxLoopMs = max(maxLoopMs, cur_ms); 150 | minLoopMs = min(minLoopMs, cur_ms); 151 | } 152 | DEBUG_PRINTM(": maxLoopMs: "); 153 | DEBUG_PRINT(maxLoopMs); 154 | DEBUG_PRINT(", minLoopMs: "); 155 | DEBUG_PRINT(minLoopMs); 156 | DEBUG_PRINT(", TotalMs: "); 157 | DEBUG_PRINTLN(millis() - startMillis); 158 | 159 | maxLoopMs = 0, minLoopMs = 9999, startMillis = millis(); 160 | DEBUG_PRINTMLN(": int version"); 161 | for (i = 0; i < 60000L; i += 167) // Around 1 degree 162 | { 163 | cur_millis = millis(); 164 | ss = i / 1000; 165 | isdeg = (SIXTIETH_DEGREE * (i % 1000) / 1000) + (SIXTIETH_DEGREE * ss); // 0-59 (includes millis) 166 | imdeg = (isdeg / 60) + (SIXTIETH_DEGREE * mm); // 0-59 (includes seconds) 167 | ihdeg = (imdeg / 12) + (TWELFTH_DEGREE * hh); // 0-11 (includes minutes) 168 | isdeg -= RIGHT_ANGLE_DEGREE; 169 | imdeg -= RIGHT_ANGLE_DEGREE; 170 | ihdeg -= RIGHT_ANGLE_DEGREE; 171 | nhx = iCos(ihdeg) * HOUR_LEN / I_SCALE + CENTER; 172 | nhy = iSin(ihdeg) * HOUR_LEN / I_SCALE + CENTER; 173 | nmx = iCos(imdeg) * MINUTE_LEN / I_SCALE + CENTER; 174 | nmy = iSin(imdeg) * MINUTE_LEN / I_SCALE + CENTER; 175 | nsx = iCos(isdeg) * SECOND_LEN / I_SCALE + CENTER; 176 | nsy = iSin(isdeg) * SECOND_LEN / I_SCALE + CENTER; 177 | cur_ms = millis() - cur_millis; 178 | maxLoopMs = max(maxLoopMs, cur_ms); 179 | minLoopMs = min(minLoopMs, cur_ms); 180 | } 181 | DEBUG_PRINTM(": maxLoopMs: "); 182 | DEBUG_PRINT(maxLoopMs); 183 | DEBUG_PRINT(", minLoopMs: "); 184 | DEBUG_PRINT(minLoopMs); 185 | DEBUG_PRINT(", TotalMs: "); 186 | DEBUG_PRINTLN(millis() - startMillis); 187 | 188 | for (uint8_t test_idx = 0; test_idx < 11; test_idx++) 189 | { 190 | switch (test_idx) 191 | { 192 | case 0: 193 | tft->setCursor(0, 0); 194 | tft->print(F("erase_and_draw ")); 195 | DEBUG_PRINTMLN(F(": redraw_hands_erase_and_draw();")); 196 | break; 197 | case 1: 198 | tft->setCursor(0, 0); 199 | tft->print(F("overwrite_rect ")); 200 | DEBUG_PRINTMLN(F(": redraw_hands_rect(overwrite_rect);")); 201 | break; 202 | case 2: 203 | tft->setCursor(0, 0); 204 | tft->print(F("4_split(rect) ")); 205 | DEBUG_PRINTMLN(F(": redraw_hands_4_split(overwrite_rect);")); 206 | break; 207 | case 3: 208 | tft->setCursor(0, 0); 209 | tft->print(F("4_split(spi_rect)")); 210 | DEBUG_PRINTMLN(F(": redraw_hands_4_split(spi_raw_overwrite_rect);")); 211 | break; 212 | case 4: 213 | tft->setCursor(0, 0); 214 | tft->print(F("spi_draw&erase ")); 215 | DEBUG_PRINTMLN(F(": redraw_hands_4_split(spi_raw_draw_and_erase);")); 216 | break; 217 | case 5: 218 | tft->setCursor(0, 0); 219 | tft->print(F("spi_cache_rect ")); 220 | DEBUG_PRINTMLN(F(": redraw_hands_4_split(spi_raw_cache_overwrite_rect);")); 221 | break; 222 | case 6: 223 | tft->setCursor(0, 0); 224 | tft->print(F("cached_lines ")); 225 | DEBUG_PRINTMLN(F(": redraw_hands_cached_lines();")); 226 | break; 227 | case 7: 228 | tft->setCursor(0, 0); 229 | tft->print(F("cached_draw&erase")); 230 | DEBUG_PRINTMLN(F(": redraw_hands_cached_draw_and_erase();")); 231 | break; 232 | case 8: 233 | tft->setCursor(0, 0); 234 | tft->print(F(" ")); 235 | DEBUG_PRINTMLN(F(": redraw_hands_rect(overwrite_rect_no_draw_float);")); 236 | break; 237 | case 9: 238 | DEBUG_PRINTMLN(F(": redraw_hands_rect(overwrite_rect_no_draw_int);")); 239 | break; 240 | case 10: 241 | DEBUG_PRINTMLN(F(": redraw_hands_rect(overwrite_rect_no_draw);")); 242 | break; 243 | } 244 | startMillis = millis(); 245 | maxLoopMs = 0; 246 | minLoopMs = 9999; 247 | drawCount = 0; 248 | //for (i = 0; i < 60000L; i += 167) // Around 1 degree 249 | //for (i = 0; i < 60000L; i += 1000) // 6 degrees 250 | i = 0; 251 | while (i < 60000L) 252 | { 253 | cur_millis = millis() - startMillis; 254 | i = cur_millis; 255 | ss = i / 1000; 256 | 257 | sdeg = SIXTIETH_RADIAN * ((0.001 * (i % 1000)) + ss); // 0-59 (includes millis) 258 | //sdeg = SIXTIETH_RADIAN * ((0.001 * (i % 1000)) + ss); // 0-59 (includes millis) 259 | nsx = cos(sdeg - RIGHT_ANGLE_RADIAN) * SECOND_LEN + CENTER; 260 | nsy = sin(sdeg - RIGHT_ANGLE_RADIAN) * SECOND_LEN + CENTER; 261 | if ((nsx != osx) || (nsy != osy)) 262 | { 263 | mdeg = (SIXTIETH * sdeg) + (SIXTIETH_RADIAN * mm); // 0-59 (includes seconds) 264 | hdeg = (TWELFTH * mdeg) + (TWELFTH_RADIAN * hh); // 0-11 (includes minutes) 265 | mdeg -= RIGHT_ANGLE_RADIAN; 266 | hdeg -= RIGHT_ANGLE_RADIAN; 267 | nmx = cos(mdeg) * MINUTE_LEN + CENTER; 268 | nmy = sin(mdeg) * MINUTE_LEN + CENTER; 269 | nhx = cos(hdeg) * HOUR_LEN + CENTER; 270 | nhy = sin(hdeg) * HOUR_LEN + CENTER; 271 | 272 | switch (test_idx) 273 | { 274 | case 0: 275 | redraw_hands_erase_and_draw(); 276 | break; 277 | case 1: 278 | redraw_hands_rect(overwrite_rect); 279 | break; 280 | case 2: 281 | redraw_hands_4_split(overwrite_rect); 282 | break; 283 | case 3: 284 | redraw_hands_4_split(spi_raw_overwrite_rect); 285 | break; 286 | case 4: 287 | redraw_hands_4_split(spi_raw_draw_and_erase); 288 | break; 289 | case 5: 290 | cache_line_points(nsx, nsy, CENTER, CENTER, cached_points, SECOND_LEN + 1); 291 | cache_line_points(nhx, nhy, CENTER, CENTER, cached_points + ((SECOND_LEN + 1) * 2), HOUR_LEN + 1); 292 | cache_line_points(nmx, nmy, CENTER, CENTER, cached_points + ((SECOND_LEN + 1 + HOUR_LEN + 1) * 2), MINUTE_LEN + 1); 293 | redraw_hands_4_split(spi_raw_cache_overwrite_rect); 294 | break; 295 | case 6: 296 | redraw_hands_cached_lines(); 297 | break; 298 | case 7: 299 | redraw_hands_cached_draw_and_erase(); 300 | break; 301 | case 8: 302 | redraw_hands_rect(overwrite_rect_no_draw_float); 303 | break; 304 | case 9: 305 | redraw_hands_rect(overwrite_rect_no_draw_int); 306 | break; 307 | case 10: 308 | redraw_hands_rect(overwrite_rect_no_draw); 309 | break; 310 | } 311 | ohx = nhx; 312 | ohy = nhy; 313 | omx = nmx; 314 | omy = nmy; 315 | osx = nsx; 316 | osy = nsy; 317 | 318 | unsigned long cur_ms = millis() - cur_millis - startMillis; 319 | maxLoopMs = max(maxLoopMs, cur_ms); 320 | minLoopMs = min(minLoopMs, cur_ms); 321 | drawCount++; 322 | } 323 | } 324 | DEBUG_PRINTM(": maxLoopMs: "); 325 | DEBUG_PRINT(maxLoopMs); 326 | DEBUG_PRINT(", minLoopMs: "); 327 | DEBUG_PRINT(minLoopMs); 328 | DEBUG_PRINT(", drawCount: "); 329 | DEBUG_PRINT(drawCount); 330 | DEBUG_PRINT(", TotalMs: "); 331 | DEBUG_PRINTLN(millis() - startMillis); 332 | } 333 | } 334 | 335 | void draw_round_clock_mark(uint8_t innerR1, uint8_t outerR1, uint8_t innerR2, uint8_t outerR2, uint8_t innerR3, uint8_t outerR3) 336 | { 337 | float x, y; 338 | uint8_t x0, x1, y0, y1, innerR, outerR; 339 | uint16_t c; 340 | 341 | for (int i = 0; i < 60; i++) 342 | { 343 | if ((i % 15) == 0) 344 | { 345 | innerR = innerR1; 346 | outerR = outerR1; 347 | c = MARK_COLOR; 348 | } 349 | else if ((i % 5) == 0) 350 | { 351 | innerR = innerR2; 352 | outerR = outerR2; 353 | c = MARK_COLOR; 354 | } 355 | else 356 | { 357 | innerR = innerR3; 358 | outerR = outerR3; 359 | c = SUBMARK_COLOR; 360 | } 361 | 362 | mdeg = (SIXTIETH_RADIAN * i) - RIGHT_ANGLE_RADIAN; 363 | x = cos(mdeg); 364 | y = sin(mdeg); 365 | x0 = x * outerR + CENTER; 366 | y0 = y * outerR + CENTER; 367 | x1 = x * innerR + CENTER; 368 | y1 = y * innerR + CENTER; 369 | 370 | tft->drawLine(x0, y0, x1, y1, c); 371 | DEBUG_PRINTM(" i: "); 372 | DEBUG_PRINT(i); 373 | DEBUG_PRINT(", x0: "); 374 | DEBUG_PRINT(x0); 375 | DEBUG_PRINT(", y0: "); 376 | DEBUG_PRINT(y0); 377 | DEBUG_PRINT(", x1: "); 378 | DEBUG_PRINT(x1); 379 | DEBUG_PRINT(", y1: "); 380 | DEBUG_PRINTLN(y1); 381 | } 382 | } 383 | 384 | void draw_square_clock_mark(uint8_t innerR1, uint8_t outerR1, uint8_t innerR2, uint8_t outerR2, uint8_t innerR3, uint8_t outerR3) 385 | { 386 | float x, y; 387 | uint8_t x0, x1, y0, y1, innerR, outerR; 388 | uint16_t c; 389 | 390 | for (int i = 0; i < 60; i++) 391 | { 392 | if ((i % 15) == 0) 393 | { 394 | innerR = innerR1; 395 | outerR = outerR1; 396 | c = MARK_COLOR; 397 | } 398 | else if ((i % 5) == 0) 399 | { 400 | innerR = innerR2; 401 | outerR = outerR2; 402 | c = MARK_COLOR; 403 | } 404 | else 405 | { 406 | innerR = innerR3; 407 | outerR = outerR3; 408 | c = SUBMARK_COLOR; 409 | } 410 | 411 | if ((i >= 53) || (i < 8)) 412 | { 413 | x = tan(SIXTIETH_RADIAN * i); 414 | x0 = CENTER + (x * outerR); 415 | y0 = CENTER + (1 - outerR); 416 | x1 = CENTER + (x * innerR); 417 | y1 = CENTER + (1 - innerR); 418 | } 419 | else if (i < 23) 420 | { 421 | y = tan((SIXTIETH_RADIAN * i) - RIGHT_ANGLE_RADIAN); 422 | x0 = CENTER + (outerR); 423 | y0 = CENTER + (y * outerR); 424 | x1 = CENTER + (innerR); 425 | y1 = CENTER + (y * innerR); 426 | } 427 | else if (i < 38) 428 | { 429 | x = tan(SIXTIETH_RADIAN * i); 430 | x0 = CENTER - (x * outerR); 431 | y0 = CENTER + (outerR); 432 | x1 = CENTER - (x * innerR); 433 | y1 = CENTER + (innerR); 434 | } 435 | else if (i < 53) 436 | { 437 | y = tan((SIXTIETH_RADIAN * i) - RIGHT_ANGLE_RADIAN); 438 | x0 = CENTER + (1 - outerR); 439 | y0 = CENTER - (y * outerR); 440 | x1 = CENTER + (1 - innerR); 441 | y1 = CENTER - (y * innerR); 442 | } 443 | tft->drawLine(x0, y0, x1, y1, c); 444 | DEBUG_PRINTM(" i: "); 445 | DEBUG_PRINT(i); 446 | DEBUG_PRINT(", x0: "); 447 | DEBUG_PRINT(x0); 448 | DEBUG_PRINT(", y0: "); 449 | DEBUG_PRINT(y0); 450 | DEBUG_PRINT(", x1: "); 451 | DEBUG_PRINT(x1); 452 | DEBUG_PRINT(", y1: "); 453 | DEBUG_PRINTLN(y1); 454 | } 455 | } 456 | 457 | void redraw_hands_erase_and_draw() 458 | { 459 | // Erase 3 hands old positions 460 | tft->drawLine(ohx, ohy, CENTER, CENTER, BACKGROUND); 461 | tft->drawLine(omx, omy, CENTER, CENTER, BACKGROUND); 462 | tft->drawLine(osx, osy, CENTER, CENTER, BACKGROUND); 463 | // Draw 3 hands new positions 464 | tft->drawLine(nmx, nmy, CENTER, CENTER, MINUTE_COLOR); 465 | tft->drawLine(nhx, nhy, CENTER, CENTER, HOUR_COLOR); 466 | tft->drawLine(nsx, nsy, CENTER, CENTER, SECOND_COLOR); 467 | } 468 | 469 | void redraw_hands_rect(void (*redrawFunc)()) 470 | { 471 | xMin = min(min(min((uint8_t)CENTER, ohx), min(omx, osx)), min(min(nhx, nmx), nsx)); 472 | xMax = max(max(max((uint8_t)CENTER, ohx), max(omx, osx)), max(max(nhx, nmx), nsx)); 473 | yMin = min(min(min((uint8_t)CENTER, ohy), min(omy, osy)), min(min(nhy, nmy), nsy)); 474 | yMax = max(max(max((uint8_t)CENTER, ohy), max(omy, osy)), max(max(nhy, nmy), nsy)); 475 | 476 | redrawFunc(); 477 | } 478 | 479 | void redraw_hands_4_split(void (*redrawFunc)()) 480 | { 481 | if (inSplitRange(1, 1, CENTER, CENTER)) 482 | { 483 | redrawFunc(); 484 | } 485 | 486 | if (inSplitRange(CENTER + 1, 1, CENTER * 2, CENTER)) 487 | { 488 | redrawFunc(); 489 | } 490 | 491 | if (inSplitRange(1, CENTER + 1, CENTER, CENTER * 2)) 492 | { 493 | redrawFunc(); 494 | } 495 | 496 | if (inSplitRange(CENTER + 1, CENTER + 1, CENTER * 2, CENTER * 2)) 497 | { 498 | redrawFunc(); 499 | } 500 | } 501 | 502 | void redraw_hands_cached_lines() 503 | { 504 | cached_points_idx = 0; 505 | last_cached_point = cached_points; 506 | write_cached_line(nhx, nhy, CENTER, CENTER, HOUR_COLOR, true, false); // cache new hour hand 507 | write_cached_line(nsx, nsy, CENTER, CENTER, SECOND_COLOR, true, false); // cache new second hand 508 | tft->startWrite(); 509 | write_cached_line(nmx, nmy, CENTER, CENTER, MINUTE_COLOR, true, true); // cache and draw new minute hand 510 | write_cached_line(omx, omy, CENTER, CENTER, BACKGROUND, false, true); // erase old minute hand 511 | tft->writeLine(nhx, nhy, CENTER, CENTER, HOUR_COLOR); // draw new hour hand 512 | write_cached_line(ohx, ohy, CENTER, CENTER, BACKGROUND, false, true); // erase old hour hand 513 | tft->writeLine(nsx, nsy, CENTER, CENTER, SECOND_COLOR); // draw new second hand 514 | write_cached_line(osx, osy, CENTER, CENTER, BACKGROUND, false, true); // erase old second hand 515 | tft->endWrite(); 516 | } 517 | 518 | void write_cached_line(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1, uint16_t color, bool save, bool actual_draw) 519 | { 520 | #if defined(ESP8266) 521 | yield(); 522 | #endif 523 | bool steep = _diff(y1, y0) > _diff(x1, x0); 524 | if (steep) 525 | { 526 | _swap_uint8_t(x0, y0); 527 | _swap_uint8_t(x1, y1); 528 | } 529 | 530 | if (x0 > x1) 531 | { 532 | _swap_uint8_t(x0, x1); 533 | _swap_uint8_t(y0, y1); 534 | } 535 | 536 | uint8_t dx, dy; 537 | dx = x1 - x0; 538 | dy = _diff(y1, y0); 539 | 540 | uint8_t err = dx / 2; 541 | int8_t ystep = (y0 < y1) ? 1 : -1; 542 | for (; x0 <= x1; x0++) 543 | { 544 | if (steep) 545 | { 546 | write_cache_point(y0, x0, color, save, actual_draw); 547 | } 548 | else 549 | { 550 | write_cache_point(x0, y0, color, save, actual_draw); 551 | } 552 | if (err < dy) 553 | { 554 | y0 += ystep; 555 | err += dx; 556 | } 557 | err -= dy; 558 | } 559 | } 560 | 561 | void write_cache_point(uint8_t x, uint8_t y, uint16_t color, bool save, bool actual_draw) 562 | { 563 | uint8_t *current_point = cached_points; 564 | for (int i = 0; i < cached_points_idx; i++) 565 | { 566 | if ((x == *(current_point++)) && (y == *(current_point))) 567 | { 568 | return; 569 | } 570 | current_point++; 571 | } 572 | if (save) 573 | { 574 | cached_points_idx++; 575 | *(last_cached_point++) = x; 576 | *(last_cached_point++) = y; 577 | } 578 | if (actual_draw) 579 | { 580 | tft->writePixel(x, y, color); 581 | } 582 | } 583 | 584 | void cache_line_points(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1, uint8_t *cache, uint8_t cache_size) 585 | { 586 | #if defined(ESP8266) 587 | yield(); 588 | #endif 589 | bool steep = abs(y1 - y0) > abs(x1 - x0); 590 | if (steep) 591 | { 592 | _swap_uint8_t(x0, y0); 593 | _swap_uint8_t(x1, y1); 594 | } 595 | 596 | if (x0 > x1) 597 | { 598 | _swap_uint8_t(x0, x1); 599 | _swap_uint8_t(y0, y1); 600 | } 601 | 602 | uint8_t dx, dy; 603 | dx = x1 - x0; 604 | dy = abs(y1 - y0); 605 | 606 | uint8_t err = dx / 2; 607 | int8_t ystep = (y0 < y1) ? 1 : -1; 608 | int i = 0; 609 | 610 | for (; x0 <= x1; x0++) 611 | { 612 | i++; 613 | if (steep) 614 | { 615 | *(cache++) = y0; 616 | *(cache++) = x0; 617 | } 618 | else 619 | { 620 | *(cache++) = x0; 621 | *(cache++) = y0; 622 | } 623 | if (err < dy) 624 | { 625 | y0 += ystep; 626 | err += dx; 627 | } 628 | err -= dy; 629 | } 630 | for (; i < cache_size; i++) 631 | { 632 | *(cache++) = 0; 633 | *(cache++) = 0; 634 | } 635 | } 636 | 637 | void redraw_hands_cached_draw_and_erase() 638 | { 639 | tft->startWrite(); 640 | draw_and_erase_cached_line(CENTER, CENTER, nsx, nsy, SECOND_COLOR, cached_points, SECOND_LEN + 1, false, false); 641 | draw_and_erase_cached_line(CENTER, CENTER, nhx, nhy, HOUR_COLOR, cached_points + ((SECOND_LEN + 1) * 2), HOUR_LEN + 1, true, false); 642 | draw_and_erase_cached_line(CENTER, CENTER, nmx, nmy, MINUTE_COLOR, cached_points + ((SECOND_LEN + 1 + HOUR_LEN + 1) * 2), MINUTE_LEN + 1, true, true); 643 | tft->endWrite(); 644 | } 645 | 646 | void draw_and_erase_cached_line(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1, uint16_t color, uint8_t *cache, uint16_t cache_len, bool cross_check_second, bool cross_check_hour) 647 | { 648 | #if defined(ESP8266) 649 | yield(); 650 | #endif 651 | bool steep = _diff(y1, y0) > _diff(x1, x0); 652 | if (steep) 653 | { 654 | _swap_uint8_t(x0, y0); 655 | _swap_uint8_t(x1, y1); 656 | } 657 | 658 | uint8_t dx, dy; 659 | dx = _diff(x1, x0); 660 | dy = _diff(y1, y0); 661 | 662 | uint8_t err = dx / 2; 663 | int8_t xstep = (x0 < x1) ? 1 : -1; 664 | int8_t ystep = (y0 < y1) ? 1 : -1; 665 | x1 += xstep; 666 | uint8_t x, y, ox, oy; 667 | for (uint16_t i = 0; i <= dx; i++) 668 | { 669 | if (steep) 670 | { 671 | x = y0; 672 | y = x0; 673 | } 674 | else 675 | { 676 | x = x0; 677 | y = y0; 678 | } 679 | ox = *(cache + (i * 2)); 680 | oy = *(cache + (i * 2) + 1); 681 | if ((x == ox) && (y == oy)) 682 | { 683 | if (cross_check_second || cross_check_hour) 684 | { 685 | write_cache_pixel(x, y, color, cross_check_second, cross_check_hour); 686 | } 687 | } 688 | else 689 | { 690 | write_cache_pixel(x, y, color, cross_check_second, cross_check_hour); 691 | if ((ox > 0) || (oy > 0)) { 692 | write_cache_pixel(ox, oy, BACKGROUND, cross_check_second, cross_check_hour); 693 | } 694 | *(cache + (i * 2)) = x; 695 | *(cache + (i * 2) + 1) = y; 696 | } 697 | if (err < dy) 698 | { 699 | y0 += ystep; 700 | err += dx; 701 | } 702 | err -= dy; 703 | x0 += xstep; 704 | } 705 | for (uint16_t i = dx + 1; i < cache_len; i++) 706 | { 707 | ox = *(cache + (i * 2)); 708 | oy = *(cache + (i * 2) + 1); 709 | if ((ox > 0) || (oy > 0)) 710 | { 711 | write_cache_pixel(ox, oy, BACKGROUND, cross_check_second, cross_check_hour); 712 | } 713 | *(cache + (i * 2)) = 0; 714 | *(cache + (i * 2) + 1) = 0; 715 | } 716 | } 717 | 718 | void write_cache_pixel(uint8_t x, uint8_t y, uint16_t color, bool cross_check_second, bool cross_check_hour) 719 | { 720 | uint8_t *cache = cached_points; 721 | if (cross_check_second) 722 | { 723 | for (int i = 0; i <= SECOND_LEN; i++) 724 | { 725 | if ((x == *(cache++)) && (y == *(cache))) 726 | { 727 | return; 728 | } 729 | cache++; 730 | } 731 | } 732 | if (cross_check_hour) 733 | { 734 | cache = cached_points + ((SECOND_LEN + 1) * 2); 735 | for (int i = 0; i <= HOUR_LEN; i++) 736 | { 737 | if ((x == *(cache++)) && (y == *(cache))) 738 | { 739 | return; 740 | } 741 | cache++; 742 | } 743 | } 744 | tft->writePixel(x, y, color); 745 | } 746 | 747 | bool inCachedPoints(uint8_t x, uint8_t y, uint8_t *cache, int cache_size) 748 | { 749 | for (int i = 0; i < cache_size; i++) 750 | { 751 | if ((x == *(cache++)) && (y == *(cache))) 752 | { 753 | return true; 754 | } 755 | cache++; 756 | } 757 | return false; 758 | } 759 | 760 | void overwrite_rect_no_draw_float() 761 | { 762 | for (uint8_t y = yMin; y <= yMax; y++) 763 | { 764 | for (uint8_t x = xMin; x <= xMax; x++) 765 | { 766 | if (inLineInterFloat(x, y, nsx, nsy, CENTER, CENTER)) 767 | { 768 | tmp++; 769 | } 770 | else if (inLineInterFloat(x, y, nhx, nhy, CENTER, CENTER)) 771 | { 772 | tmp++; 773 | } 774 | else if (inLineInterFloat(x, y, nmx, nmy, CENTER, CENTER)) 775 | { 776 | tmp++; 777 | } 778 | } 779 | } 780 | } 781 | 782 | void overwrite_rect_no_draw_int() 783 | { 784 | for (uint8_t y = yMin; y <= yMax; y++) 785 | { 786 | for (uint8_t x = xMin; x <= xMax; x++) 787 | { 788 | if (inLineInterInt(x, y, nsx, nsy, CENTER, CENTER)) 789 | { 790 | tmp++; 791 | } 792 | else if (inLineInterInt(x, y, nhx, nhy, CENTER, CENTER)) 793 | { 794 | tmp++; 795 | } 796 | else if (inLineInterInt(x, y, nmx, nmy, CENTER, CENTER)) 797 | { 798 | tmp++; 799 | } 800 | } 801 | } 802 | } 803 | 804 | void overwrite_rect_no_draw() 805 | { 806 | for (uint8_t y = yMin; y <= yMax; y++) 807 | { 808 | for (uint8_t x = xMin; x <= xMax; x++) 809 | { 810 | if (inLine(x, y, nsx, nsy, CENTER, CENTER)) 811 | { 812 | tmp++; 813 | } 814 | else if (inLine(x, y, nhx, nhy, CENTER, CENTER)) 815 | { 816 | tmp++; 817 | } 818 | else if (inLine(x, y, nmx, nmy, CENTER, CENTER)) 819 | { 820 | tmp++; 821 | } 822 | } 823 | } 824 | } 825 | 826 | void overwrite_rect() 827 | { 828 | tft->setAddrWindow(xMin, yMin, xMax - xMin + 1, yMax - yMin + 1); 829 | for (uint8_t y = yMin; y <= yMax; y++) 830 | { 831 | for (uint8_t x = xMin; x <= xMax; x++) 832 | { 833 | if (inLine(x, y, nsx, nsy, CENTER, CENTER)) 834 | { 835 | tft->pushColor(SECOND_COLOR); // draw second hand 836 | } 837 | else if (inLine(x, y, nhx, nhy, CENTER, CENTER)) 838 | { 839 | tft->pushColor(HOUR_COLOR); // draw hour hand 840 | } 841 | else if (inLine(x, y, nmx, nmy, CENTER, CENTER)) 842 | { 843 | tft->pushColor(MINUTE_COLOR); // draw minute hand 844 | } 845 | else 846 | { 847 | tft->pushColor(BACKGROUND); // over write background color 848 | } 849 | } 850 | } 851 | } 852 | 853 | void spi_raw_overwrite_rect() 854 | { 855 | tft->startWrite(); 856 | tft->writeAddrWindow(xMin, yMin, xMax - xMin + 1, yMax - yMin + 1); 857 | for (uint8_t y = yMin; y <= yMax; y++) 858 | { 859 | for (uint8_t x = xMin; x <= xMax; x++) 860 | { 861 | if (inLine(x, y, nsx, nsy, CENTER, CENTER)) 862 | { 863 | tft->writeColor(SECOND_COLOR); // draw second hand 864 | } 865 | else if (inLine(x, y, nhx, nhy, CENTER, CENTER)) 866 | { 867 | tft->writeColor(HOUR_COLOR); // draw hour hand 868 | } 869 | else if (inLine(x, y, nmx, nmy, CENTER, CENTER)) 870 | { 871 | tft->writeColor(MINUTE_COLOR); // draw minute hand 872 | } 873 | else 874 | { 875 | tft->writeColor(BACKGROUND); // over write background color 876 | } 877 | } 878 | } 879 | tft->endWrite(); 880 | } 881 | 882 | void spi_raw_cache_overwrite_rect() 883 | { 884 | tft->startWrite(); 885 | tft->writeAddrWindow(xMin, yMin, xMax - xMin + 1, yMax - yMin + 1); 886 | for (uint8_t y = yMin; y <= yMax; y++) 887 | { 888 | for (uint8_t x = xMin; x <= xMax; x++) 889 | { 890 | if (inCachedPoints(x, y, cached_points, SECOND_LEN + 1)) 891 | { 892 | tft->writeColor(SECOND_COLOR); // draw second hand 893 | } 894 | else if (inCachedPoints(x, y, cached_points + ((SECOND_LEN + 1) * 2), HOUR_LEN + 1)) 895 | { 896 | tft->writeColor(HOUR_COLOR); // draw hour hand 897 | } 898 | else if (inCachedPoints(x, y, cached_points + ((SECOND_LEN + 1 + HOUR_LEN + 1) * 2), MINUTE_LEN + 1)) 899 | { 900 | tft->writeColor(MINUTE_COLOR); // draw minute hand 901 | } 902 | else 903 | { 904 | tft->writeColor(BACKGROUND); // over write background color 905 | } 906 | } 907 | } 908 | tft->endWrite(); 909 | } 910 | 911 | void spi_raw_draw_and_erase() 912 | { 913 | tft->startWrite(); 914 | for (uint8_t y = yMin; y <= yMax; y++) 915 | { 916 | for (uint8_t x = xMin; x <= xMax; x++) 917 | { 918 | if (inLine(x, y, nsx, nsy, CENTER, CENTER)) 919 | { 920 | tft->writePixel(x, y, SECOND_COLOR); // draw second hand 921 | } 922 | else if (inLine(x, y, nhx, nhy, CENTER, CENTER)) 923 | { 924 | tft->writePixel(x, y, HOUR_COLOR); // draw hour hand 925 | } 926 | else if (inLine(x, y, nmx, nmy, CENTER, CENTER)) 927 | { 928 | tft->writePixel(x, y, MINUTE_COLOR); // draw minute hand 929 | } 930 | else if ((inLine(x, y, osx, osy, CENTER, CENTER)) || (inLine(x, y, ohx, ohy, CENTER, CENTER)) || (inLine(x, y, omx, omy, CENTER, CENTER))) 931 | { 932 | tft->writePixel(x, y, BACKGROUND); // erase with background color 933 | } 934 | } 935 | } 936 | tft->endWrite(); 937 | } 938 | 939 | bool inSplitRange(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1) 940 | { 941 | bool in_range = false; 942 | 943 | xMin = CENTER; 944 | yMin = CENTER; 945 | xMax = CENTER; 946 | yMax = CENTER; 947 | 948 | if (_ordered_in_range(ohx, x0, x1) && _ordered_in_range(ohy, y0, y1)) 949 | { 950 | in_range = true; 951 | xMin = min(xMin, ohx); 952 | xMax = max(xMax, ohx); 953 | yMin = min(yMin, ohy); 954 | yMax = max(yMax, ohy); 955 | } 956 | 957 | if (_ordered_in_range(omx, x0, x1) && _ordered_in_range(omy, y0, y1)) 958 | { 959 | in_range = true; 960 | xMin = min(xMin, omx); 961 | xMax = max(xMax, omx); 962 | yMin = min(yMin, omy); 963 | yMax = max(yMax, omy); 964 | } 965 | 966 | if (_ordered_in_range(osx, x0, x1) && _ordered_in_range(osy, y0, y1)) 967 | { 968 | in_range = true; 969 | xMin = min(xMin, osx); 970 | xMax = max(xMax, osx); 971 | yMin = min(yMin, osy); 972 | yMax = max(yMax, osy); 973 | } 974 | 975 | if (_ordered_in_range(nhx, x0, x1) && _ordered_in_range(nhy, y0, y1)) 976 | { 977 | in_range = true; 978 | xMin = min(xMin, nhx); 979 | xMax = max(xMax, nhx); 980 | yMin = min(yMin, nhy); 981 | yMax = max(yMax, nhy); 982 | } 983 | 984 | if (_ordered_in_range(nmx, x0, x1) && _ordered_in_range(nmy, y0, y1)) 985 | { 986 | in_range = true; 987 | xMin = min(xMin, nmx); 988 | xMax = max(xMax, nmx); 989 | yMin = min(yMin, nmy); 990 | yMax = max(yMax, nmy); 991 | } 992 | 993 | if (_ordered_in_range(nsx, x0, x1) && _ordered_in_range(nsy, y0, y1)) 994 | { 995 | in_range = true; 996 | xMin = min(xMin, nsx); 997 | xMax = max(xMax, nsx); 998 | yMin = min(yMin, nsy); 999 | yMax = max(yMax, nsy); 1000 | } 1001 | 1002 | return in_range; 1003 | } 1004 | 1005 | bool inLineInterFloat(uint8_t x, uint8_t y, uint8_t lx0, uint8_t ly0, uint8_t lx1, uint8_t ly1) 1006 | { 1007 | // range checking 1008 | if (!_in_range(x, lx0, lx1)) 1009 | { 1010 | return false; 1011 | } 1012 | if (!_in_range(y, ly0, ly1)) 1013 | { 1014 | return false; 1015 | } 1016 | 1017 | uint8_t dx = _diff(lx1, lx0); 1018 | uint8_t dy = _diff(ly1, ly0); 1019 | 1020 | if (dy > dx) 1021 | { 1022 | _swap_uint8_t(dx, dy); 1023 | _swap_uint8_t(x, y); 1024 | _swap_uint8_t(lx0, ly0); 1025 | _swap_uint8_t(lx1, ly1); 1026 | } 1027 | 1028 | if (lx0 > lx1) 1029 | { 1030 | _swap_uint8_t(lx0, lx1); 1031 | _swap_uint8_t(ly0, ly1); 1032 | } 1033 | 1034 | if (ly0 < ly1) 1035 | { 1036 | return (y == (ly0 + round((float)(x - lx0) * dy / dx))); 1037 | } 1038 | else 1039 | { 1040 | return (y == (ly0 - round((float)(x - lx0) * dy / dx))); 1041 | } 1042 | } 1043 | 1044 | bool inLineInterInt(uint8_t x, uint8_t y, uint8_t lx0, uint8_t ly0, uint8_t lx1, uint8_t ly1) 1045 | { 1046 | // range checking 1047 | if (!_in_range(x, lx0, lx1)) 1048 | { 1049 | return false; 1050 | } 1051 | if (!_in_range(y, ly0, ly1)) 1052 | { 1053 | return false; 1054 | } 1055 | 1056 | uint8_t dx = _diff(lx1, lx0); 1057 | uint8_t dy = _diff(ly1, ly0); 1058 | 1059 | if (dy > dx) 1060 | { 1061 | _swap_uint8_t(dx, dy); 1062 | _swap_uint8_t(x, y); 1063 | _swap_uint8_t(lx0, ly0); 1064 | _swap_uint8_t(lx1, ly1); 1065 | } 1066 | 1067 | if (lx0 > lx1) 1068 | { 1069 | _swap_uint8_t(lx0, lx1); 1070 | _swap_uint8_t(ly0, ly1); 1071 | } 1072 | 1073 | if (ly0 < ly1) 1074 | { 1075 | return (y == (ly0 + ((x - lx0) * dy / dx))); 1076 | } 1077 | else 1078 | { 1079 | return (y == (ly0 - ((x - lx0) * dy / dx))); 1080 | } 1081 | } 1082 | 1083 | bool inLine(uint8_t x, uint8_t y, uint8_t lx0, uint8_t ly0, uint8_t lx1, uint8_t ly1) 1084 | { 1085 | // range checking 1086 | if (!_in_range(x, lx0, lx1)) 1087 | { 1088 | return false; 1089 | } 1090 | if (!_in_range(y, ly0, ly1)) 1091 | { 1092 | return false; 1093 | } 1094 | 1095 | uint8_t dx = _diff(lx1, lx0); 1096 | uint8_t dy = _diff(ly1, ly0); 1097 | 1098 | if (dy > dx) 1099 | { 1100 | _swap_uint8_t(dx, dy); 1101 | _swap_uint8_t(x, y); 1102 | _swap_uint8_t(lx0, ly0); 1103 | _swap_uint8_t(lx1, ly1); 1104 | } 1105 | 1106 | if (lx0 > lx1) 1107 | { 1108 | _swap_uint8_t(lx0, lx1); 1109 | _swap_uint8_t(ly0, ly1); 1110 | } 1111 | 1112 | uint8_t err = dx >> 1; 1113 | int8_t ystep = (ly0 < ly1) ? 1 : -1; 1114 | for (; lx0 <= x; lx0++) 1115 | { 1116 | if (err < dy) 1117 | { 1118 | ly0 += ystep; 1119 | err += dx; 1120 | } 1121 | err -= dy; 1122 | } 1123 | 1124 | return (y == ly0); 1125 | } 1126 | --------------------------------------------------------------------------------