|
arduino IDEArduino
|
ESP32 and Round OLED Smart Watch Concept
Hey, what's up folks.
Here's a super interesting project that utilizes a Round LCD Display connected with an ESP32 board to make a smartwatch.
This article is gonna be about setting up the Round LCD and basic smartwatch on a breadboard.
So let's get started.
Material Required
We use the following materials to set up our smartwatch-
- 1.28" Round LCD Display GC9A01
- ESP32 Board- Lolin D32 Pro
- RTC Module
- Breadboard
- Jumper wires
1.28" GC9A01 ROUND LCD DISPLAY
GC9A01 is a 240x240 Round RGB LCD Display that adopts a four-wire SPI communication interface, SPI is fast so it can greatly save the GPIO port, and the communication speed will be faster.
It's similar in size to the 240x240 square LCD but has rounded edges.
The built-in driver used in this LCD is GC9A01, with a resolution of 240RGB×240 dots.
Checkout its wiki page for more info- https://www.waveshare.com/wiki/1.28inch_LCD_Module
Following are its electrical parameters-
- Operating voltage: 3.3V/5V
- Interface: SPI
- LCD type: IPS
- Controller: GC9A01
- Resolution: 240(H)RGB x 240(V)
- Display size: Φ32.4mm
- Pixel size: 0.135(H)x0.135(V)mm
- Dimension: 40.4×37.5(mm) Φ37.5(mm)
This display was made by Waveshare and you could find all info about this display on their site.
Here's the link- https://www.waveshare.com/product/1.28inch-lcd-module.htm
On their page, they have used an Arduino Uno to run the board, also a Raspberry pi, and a Nucleo Board but we're going to use the ESP32 board because of its connectivity features and faster processing power.
PCBWAY GIFTSHOP
As for sourcing this display, I got this display from PCBWAY's Giftshop.
Aside from PCB Services, PCBWAY also has a dedicated components store.
PCBWAY GIFTSHOP is an online marketplace from where we can source all the major electronics stuff, like Arduino boards, Raspberry Pi boards, Modules, sensors, etc.
PCBWAY have this system that lets us purchase anything from their gift shop through beans, Beans are like a redeemable currency or coupons that we get by placing an order on PCBWAY or by sharing your projects in the community to get beans.
Check PCBWAY out for getting great PCB service from here- https://www.pcbway.com/
TFT_eSPI Screen Library setup
To run this OLED with ESP32, I used the popular TFT_eSPI Library by Bodmer.
https://github.com/Bodmer/TFT_eSPI
TFT_eSPI is an amazing library that supports all major displays that are used, like ILI9430, ST7735, and even the round LCD GC9A01.
- We first go to its Github Page and download the RAW files.
- Next, we extract the folder in Documents>Arduino>Libraries where we keep all our custom libraries.
- We Open the Arduino IDE and see the TFT_eSPI added in the library manager.
User setup edit
Before testing this display, we need to do some editing on the user setup file.
we go to C:\Users\ACER\Documents\Arduino\libraries\TFT_eSPI and make changes in the User Setup.h by replacing it with User setup for GC9A01.
The default one is set for ILI9430 Display and we change it for GC9A01 by adding // in front of ILI9430 User setup and removing // in front of GC9A01.
Schematic - Basic Wiring
- We first connect VCC and BL, we do this to supply BL which is backlight 3.3V through the MCU.
- GND to GND of ESP32
- Din to D23 which is MOSI Pin
- CLK to D19 which is SCK
- CS to D15 Pin
- DC to D2 Pin
- RST to D4 Pin
Example sketches
We can now upload stuff in ESP32 to run the GC9A01 Display.
Boing Ball
This will be the first sketch that we upload to the ESP32 board.
Its called Boing Ball Demo and it can be found in Example>TFT_eSPI> DMA Test>Boing Ball Demo
// 'Boing' ball demo
#define SCREENWIDTH 320
#define SCREENHEIGHT 240
#include "graphic.h"
#include <TFT_eSPI.h> // Hardware-specific library
TFT_eSPI tft = TFT_eSPI(); // Invoke custom library
#define BGCOLOR 0xAD75
#define GRIDCOLOR 0xA815
#define BGSHADOW 0x5285
#define GRIDSHADOW 0x600C
#define RED 0xF800
#define WHITE 0xFFFF
#define YBOTTOM 123 // Ball Y coord at bottom
#define YBOUNCE -3.5 // Upward velocity on ball bounce
// Ball coordinates are stored floating-point because screen refresh
// is so quick, whole-pixel movements are just too fast!
float ballx = 20.0, bally = YBOTTOM, // Current ball position
ballvx = 0.8, ballvy = YBOUNCE, // Ball velocity
ballframe = 3; // Ball animation frame #
int balloldx = ballx, balloldy = bally; // Prior ball position
// Working buffer for ball rendering...2 scanlines that alternate,
// one is rendered while the other is transferred via DMA.
uint16_t renderbuf[2][SCREENWIDTH];
uint16_t palette[16]; // Color table for ball rotation effect
uint32_t startTime, frame = 0; // For frames-per-second estimate
void setup() {
Serial.begin(115200);
// while(!Serial);
tft.begin();
tft.setRotation(3); // Landscape orientation, USB at bottom right
tft.setSwapBytes(false);
// Draw initial framebuffer contents:
//tft.setBitmapColor(GRIDCOLOR, BGCOLOR);
tft.fillScreen(BGCOLOR);
tft.initDMA();
tft.drawBitmap(0, 0, (const uint8_t *)background, SCREENWIDTH, SCREENHEIGHT, GRIDCOLOR);
startTime = millis();
}
void loop() {
balloldx = (int16_t)ballx; // Save prior position
balloldy = (int16_t)bally;
ballx += ballvx; // Update position
bally += ballvy;
ballvy += 0.06; // Update Y velocity
if((ballx <= 15) || (ballx >= SCREENWIDTH - BALLWIDTH))
ballvx *= -1; // Left/right bounce
if(bally >= YBOTTOM) { // Hit ground?
bally = YBOTTOM; // Clip and
ballvy = YBOUNCE; // bounce up
}
// Determine screen area to update. This is the bounds of the ball's
// prior and current positions, so the old ball is fully erased and new
// ball is fully drawn.
int16_t minx, miny, maxx, maxy, width, height;
// Determine bounds of prior and new positions
minx = ballx;
if(balloldx < minx) minx = balloldx;
miny = bally;
if(balloldy < miny) miny = balloldy;
maxx = ballx + BALLWIDTH - 1;
if((balloldx + BALLWIDTH - 1) > maxx) maxx = balloldx + BALLWIDTH - 1;
maxy = bally + BALLHEIGHT - 1;
if((balloldy + BALLHEIGHT - 1) > maxy) maxy = balloldy + BALLHEIGHT - 1;
width = maxx - minx + 1;
height = maxy - miny + 1;
// Ball animation frame # is incremented opposite the ball's X velocity
ballframe -= ballvx * 0.5;
if(ballframe < 0) ballframe += 14; // Constrain from 0 to 13
else if(ballframe >= 14) ballframe -= 14;
// Set 7 palette entries to white, 7 to red, based on frame number.
// This makes the ball spin
for(uint8_t i=0; i<14; i++) {
palette[i+2] = ((((int)ballframe + i) % 14) < 7) ? WHITE : RED;
// Palette entries 0 and 1 aren't used (clear and shadow, respectively)
}
// Only the changed rectangle is drawn into the 'renderbuf' array...
uint16_t c, *destPtr;
int16_t bx = minx - (int)ballx, // X relative to ball bitmap (can be negative)
by = miny - (int)bally, // Y relative to ball bitmap (can be negative)
bgx = minx, // X relative to background bitmap (>= 0)
bgy = miny, // Y relative to background bitmap (>= 0)
x, y, bx1, bgx1; // Loop counters and working vars
uint8_t p; // 'packed' value of 2 ball pixels
int8_t bufIdx = 0;
// Start SPI transaction and drop TFT_CS - avoids transaction overhead in loop
tft.startWrite();
// Set window area to pour pixels into
tft.setAddrWindow(minx, miny, width, height);
// Draw line by line loop
for(y=0; y<height; y++) { // For each row...
destPtr = &renderbuf[bufIdx][0];
bx1 = bx; // Need to keep the original bx and bgx values,
bgx1 = bgx; // so copies of them are made here (and changed in loop below)
for(x=0; x<width; x++) {
if((bx1 >= 0) && (bx1 < BALLWIDTH) && // Is current pixel row/column
(by >= 0) && (by < BALLHEIGHT)) { // inside the ball bitmap area?
// Yes, do ball compositing math...
p = ball[by][bx1 / 2]; // Get packed value (2 pixels)
c = (bx1 & 1) ? (p & 0xF) : (p >> 4); // Unpack high or low nybble
if(c == 0) { // Outside ball - just draw grid
c = background[bgy][bgx1 / 8] & (0x80 >> (bgx1 & 7)) ? GRIDCOLOR : BGCOLOR;
} else if(c > 1) { // In ball area...
c = palette[c];
} else { // In shadow area...
c = background[bgy][bgx1 / 8] & (0x80 >> (bgx1 & 7)) ? GRIDSHADOW : BGSHADOW;
}
} else { // Outside ball bitmap, just draw background bitmap...
c = background[bgy][bgx1 / 8] & (0x80 >> (bgx1 & 7)) ? GRIDCOLOR : BGCOLOR;
}
*destPtr++ = c<<8 | c>>8; // Store pixel color
bx1++; // Increment bitmap position counters (X axis)
bgx1++;
}
tft.pushPixelsDMA(&renderbuf[bufIdx][0], width); // Push line to screen
// Push line to screen (swap bytes false for STM/ESP32)
//tft.pushPixels(&renderbuf[bufIdx][0], width);
bufIdx = 1 - bufIdx;
by++; // Increment bitmap position counters (Y axis)
bgy++;
}
//if (random(100) == 1) delay(2000);
tft.endWrite();
//delay(5);
// Show approximate frame rate
if(!(++frame & 255)) { // Every 256 frames...
uint32_t elapsed = (millis() - startTime) / 1000; // Seconds
if(elapsed) {
Serial.print(frame / elapsed);
Serial.println(" fps");
}
}
}
Scrolling 16Bit Sprite
Second Sketch will be this Scrolling Sprite Message which can be found in Example>TFT_eSPI> Sprite>Scrolling Sprite 16 Bit
#define IWIDTH 240
#define IHEIGHT 30
// Pause in milliseconds to set scroll speed
#define WAIT 0
#include <TFT_eSPI.h> // Include the graphics library (this includes the sprite functions)
TFT_eSPI tft = TFT_eSPI(); // Create object "tft"
TFT_eSprite img = TFT_eSprite(&tft); // Create Sprite object "img" with pointer to "tft" object
// // the pointer is used by pushSprite() to push it onto the TFT
// -------------------------------------------------------------------------
// Setup
// -------------------------------------------------------------------------
void setup(void) {
tft.init();
tft.setRotation(0);
tft.fillScreen(TFT_BLUE);
}
// -------------------------------------------------------------------------
// Main loop
// -------------------------------------------------------------------------
void loop() {
while (1)
{
// Create the sprite and clear background to black
img.createSprite(IWIDTH, IHEIGHT);
//img.fillSprite(TFT_BLACK); // Optional here as we fill the sprite later anyway
for (int pos = IWIDTH; pos > 0; pos--)
{
build_banner("Hello World", pos);
img.pushSprite(0, 0);
build_banner("TFT_eSPI sprite" , pos);
img.pushSprite(0, 50);
delay(WAIT);
}
// Delete sprite to free up the memory
img.deleteSprite();
// Create a sprite of a different size
numberBox(random(100), 60, 100);
}
}
// #########################################################################
// Build the scrolling sprite image from scratch, draw text at x = xpos
// #########################################################################
void build_banner(String msg, int xpos)
{
int h = IHEIGHT;
// We could just use fillSprite(color) but lets be a bit more creative...
// Fill with rainbow stripes
while (h--) img.drawFastHLine(0, h, IWIDTH, rainbow(h * 4));
// Draw some graphics, the text will apear to scroll over these
img.fillRect (IWIDTH / 2 - 20, IHEIGHT / 2 - 10, 40, 20, TFT_YELLOW);
img.fillCircle(IWIDTH / 2, IHEIGHT / 2, 10, TFT_ORANGE);
// Now print text on top of the graphics
img.setTextSize(1); // Font size scaling is x1
img.setTextFont(4); // Font 4 selected
img.setTextColor(TFT_BLACK); // Black text, no background colour
img.setTextWrap(false); // Turn of wrap so we can print past end of sprite
// Need to print twice so text appears to wrap around at left and right edges
img.setCursor(xpos, 2); // Print text at xpos
img.print(msg);
img.setCursor(xpos - IWIDTH, 2); // Print text at xpos - sprite width
img.print(msg);
}
// #########################################################################
// Create sprite, plot graphics in it, plot to screen, then delete sprite
// #########################################################################
void numberBox(int num, int x, int y)
{
// Create a sprite 80 pixels wide, 50 high (8kbytes of RAM needed)
img.createSprite(80, 50);
// Fill it with black
img.fillSprite(TFT_BLACK);
// Draw a backgorund of 2 filled triangles
img.fillTriangle( 0, 0, 0, 49, 40, 25, TFT_RED);
img.fillTriangle( 79, 0, 79, 49, 40, 25, TFT_DARKGREEN);
// Set the font parameters
img.setTextSize(1); // Font size scaling is x1
img.setFreeFont(&FreeSerifBoldItalic24pt7b); // Select free font
img.setTextColor(TFT_WHITE); // White text, no background colour
// Set text coordinate datum to middle centre
img.setTextDatum(MC_DATUM);
// Draw the number in middle of 80 x 50 sprite
img.drawNumber(num, 40, 25);
// Push sprite to TFT screen CGRAM at coordinate x,y (top left corner)
img.pushSprite(x, y);
// Delete sprite to free up the RAM
img.deleteSprite();
}
// #########################################################################
// Return a 16 bit rainbow colour
// #########################################################################
unsigned int rainbow(byte value)
{
// Value is expected to be in range 0-127
// The value is converted to a spectrum colour from 0 = red through to 127 = blue
byte red = 0; // Red is the top 5 bits of a 16 bit colour value
byte green = 0;// Green is the middle 6 bits
byte blue = 0; // Blue is the bottom 5 bits
byte sector = value >> 5;
byte amplit = value & 0x1F;
switch (sector)
{
case 0:
red = 0x1F;
green = amplit;
blue = 0;
break;
case 1:
red = 0x1F - amplit;
green = 0x1F;
blue = 0;
break;
case 2:
red = 0;
green = 0x1F;
blue = amplit;
break;
case 3:
red = 0;
green = 0x1F - amplit;
blue = 0x1F;
break;
}
return red << 11 | green << 6 | blue;
}
Smart Watch Code
Here's the smartwatch code that was originally made by Volos Project.
https://github.com/VolosR/watchESP
It consists of the main file and one header file, header file contains fonts that are used in the main sketch.
#include <TFT_eSPI.h>
#include "fonts.h"
#include "time.h"
#include "RTClib.h"
RTC_DS3231 rtc;
TFT_eSPI tft = TFT_eSPI();
TFT_eSprite img = TFT_eSprite(&tft);
#define color1 TFT_WHITE
#define color2 0x8410
#define color3 0x5ACB
#define color4 0x15B3
#define color5 0x00A3
volatile int counter = 0;
float VALUE;
float lastValue=0;
double rad=0.01745;
float x[360];
float y[360];
float px[360];
float py[360];
float lx[360];
float ly[360];
int r=104;
int sx=120;
int sy=120;
String cc[12]={"45","40","35","30","25","20","15","10","05","0","55","50"};
String days[]={"SUNDAY", "MONDAY", "TUESDAY", "WEDNESDAY", "THURSDAY", "FRIDAY", "SATURDAY"};
int start[12];
int startP[60];
const int pwmFreq = 5000;
const int pwmResolution = 8;
const int pwmLedChannelTFT = 0;
int angle=0;
bool onOff=0;
bool debounce=0;
String h,m,s,d1,d2,m1,m2;
void setup() {
if (! rtc.begin()) {
Serial.println("Couldn't find RTC");
}
pinMode(2,OUTPUT);
pinMode(0,INPUT_PULLUP);
pinMode(35,INPUT_PULLUP);
pinMode(13,INPUT_PULLUP);
digitalWrite(2,0);
ledcSetup(pwmLedChannelTFT, pwmFreq, pwmResolution);
ledcAttachPin(5, pwmLedChannelTFT);
ledcWrite(pwmLedChannelTFT, 200);
tft.init();
tft.setSwapBytes(true);
tft.fillScreen(TFT_BLACK);
img.setSwapBytes(true);
img.createSprite(240, 240);
img.setTextDatum(4);
int b=0;
int b2=0;
for(int i=0;i<360;i++)
{
x[i]=(r*cos(rad*i))+sx;
y[i]=(r*sin(rad*i))+sy;
px[i]=((r-16)*cos(rad*i))+sx;
py[i]=((r-16)*sin(rad*i))+sy;
lx[i]=((r-26)*cos(rad*i))+sx;
ly[i]=((r-26)*sin(rad*i))+sy;
if(i%30==0){
start[b]=i;
b++;
}
if(i%6==0){
startP[b2]=i;
b2++;
}
}
}
int lastAngle=0;
float circle=100;
bool dir=0;
int rAngle=359;
void loop() {
rAngle=rAngle-2;
DateTime now = rtc.now();
angle=now.second()*6;
s=String(now.second());
m=String(now.minute());
h=String(now.hour());
if(m.toInt()<10)
m="0"+m;
if(h.toInt()<10)
h="0"+h;
if(s.toInt()<10)
s="0"+s;
if(now.day()>10)
{
d1=now.day()/10;
d2=now.day()%10;
}
else
{
d1="0";
d2=String(now.day());
}
if(now.month()>10)
{
m1=now.month()/10;
m2=now.month()%10;
}
else
{
m1="0";
m2=String(now.month());
}
if(angle>=360)
angle=0;
if(rAngle<=0)
rAngle=359;
if(dir==0)
circle=circle+0.5;
else
circle=circle-0.5;
if(circle>140)
dir=!dir;
if(circle<100)
dir=!dir;
if(angle>-1)
{
lastAngle=angle;
VALUE=((angle-270)/3.60)*-1;
if(VALUE<0)
VALUE=VALUE+100;
img.fillSprite(TFT_BLACK);
img.fillCircle(sx,sy,124,color5);
img.setTextColor(TFT_WHITE,color5);
img.drawString(days[now.dayOfTheWeek()],circle,120,2);
for(int i=0;i<12;i++)
if(start[i]+angle<360){
img.drawString(cc[i],x[start[i]+angle],y[start[i]+angle],2);
img.drawLine(px[start[i]+angle],py[start[i]+angle],lx[start[i]+angle],ly[start[i]+angle],color1);
}
else
{
img.drawString(cc[i],x[(start[i]+angle)-360],y[(start[i]+angle)-360],2);
img.drawLine(px[(start[i]+angle)-360],py[(start[i]+angle)-360],lx[(start[i]+angle)-360],ly[(start[i]+angle)-360],color1);
}
img.setFreeFont(&DSEG7_Modern_Bold_20);
img.drawString(s,sx,sy-36);
img.setFreeFont(&DSEG7_Classic_Regular_28);
img.drawString(h+":"+m,sx,sy+28);
img.setTextFont(0);
img.fillRect(70,86,12,20,color3);
img.fillRect(84,86,12,20,color3);
img.fillRect(150,86,12,20,color3);
img.fillRect(164,86,12,20,color3);
img.setTextColor(0x35D7,TFT_BLACK);
img.drawString("MONTH",84,78);
img.drawString("DAY",162,78);
img.setTextColor(TFT_ORANGE,TFT_BLACK);
img.drawString("VOLOS PROJECTS",120,174);
img.drawString("***",120,104);
img.setTextColor(TFT_WHITE,color3);
img.drawString(m1,77,96,2);
img.drawString(m2,91,96,2);
img.drawString(d1,157,96,2);
img.drawString(d2,171,96,2);
for(int i=0;i<60;i++)
if(startP[i]+angle<360)
img.fillCircle(px[startP[i]+angle],py[startP[i]+angle],1,color1);
else
img.fillCircle(px[(startP[i]+angle)-360],py[(startP[i]+angle)-360],1,color1);
img.fillTriangle(sx-1,sy-70,sx-5,sy-56,sx+4,sy-56,TFT_ORANGE);
img.fillCircle(px[rAngle],py[rAngle],6,TFT_RED);
img.pushSprite(0, 0);
}
}
This Sketch is great but it requires a Real Time Clock Module which is missing from the current setup.
Instead of this sketch, we use another sketch that is available in example sketches.
Digital Clock Sketch
This will be the final sketch that will be used in version 2 of this smartwatch project with a few tweaks, this sketch can be found in the Example>TFT_eSPI> 320x240>TFT_Clock_Digital
I did some medications in the original sketch so it won't display the seconds and only displays hours and minutes.
Here's the edited sketch-
#include <TFT_eSPI.h> // Hardware-specific library
#include <SPI.h>
#define TFT_GREY 0x5AEB
TFT_eSPI tft = TFT_eSPI(); // Invoke custom library
uint32_t targetTime = 0; // for next 1 second timeout
static uint8_t conv2d(const char* p); // Forward declaration needed for IDE 1.6.x
uint8_t hh = conv2d(__TIME__), mm = conv2d(__TIME__ + 3), ss = conv2d(__TIME__ + 6); // Get H, M, S from compile time
byte omm = 99, oss = 99;
byte xcolon = 0, xsecs = 0;
unsigned int colour = 0;
void setup(void) {
//Serial.begin(115200);
tft.init();
tft.setRotation(1);
tft.fillScreen(TFT_BLACK);
tft.setTextSize(1);
tft.setTextColor(TFT_YELLOW, TFT_BLACK);
targetTime = millis() + 1000;
}
void loop() {
if (targetTime < millis()) {
// Set next update for 1 second later
targetTime = millis() + 1000;
// Adjust the time values by adding 1 second
ss++; // Advance second
if (ss == 60) { // Check for roll-over
ss = 0; // Reset seconds to zero
omm = mm; // Save last minute time for display update
mm++; // Advance minute
if (mm > 59) { // Check for roll-over
mm = 0;
hh++; // Advance hour
if (hh > 23) { // Check for 24hr roll-over (could roll-over on 13)
hh = 0; // 0 for 24 hour clock, set to 1 for 12 hour clock
}
}
}
// Update digital time
int xpos = 0;
int ypos = 85; // Top left corner ot clock text, about half way down
int ysecs = ypos + 24;
if (omm != mm) { // Redraw hours and minutes time every minute
omm = mm;
// Draw hours and minutes
if (hh < 10) xpos += tft.drawChar('0', xpos, ypos, 8); // Add hours leading zero for 24 hr clock
xpos += tft.drawNumber(hh, xpos, ypos, 8); // Draw hours
xcolon = xpos; // Save colon coord for later to flash on/off later
xpos += tft.drawChar(':', xpos, ypos - 8, 8);
if (mm < 10) xpos += tft.drawChar('0', xpos, ypos, 8); // Add minutes leading zero
xpos += tft.drawNumber(mm, xpos, ypos, 8); // Draw minutes
xsecs = xpos; // Sae seconds 'x' position for later display updates
}
if (oss != ss) { // Redraw seconds time every second
oss = ss;
xpos = xsecs;
if (ss % 2) { // Flash the colons on/off
tft.setTextColor(0x39C4, TFT_BLACK); // Set colour to grey to dim colon
tft.drawChar(':', xcolon, ypos - 8, 8); // Hour:minute colon
//xpos += tft.drawChar(':', xsecs, ysecs, 6); // Seconds colon
tft.setTextColor(TFT_YELLOW, TFT_BLACK); // Set colour back to yellow
}
else {
tft.drawChar(':', xcolon, ypos - 8, 8); // Hour:minute colon
// xpos += tft.drawChar(':', xsecs, ysecs, 3); // Seconds colon
}
//Draw seconds
if (ss < 10) xpos += tft.drawChar('0', xpos, ysecs, 6); // Add leading zero
// tft.drawNumber(ss, xpos, ysecs, 6); // Draw seconds
}
}
}
// Function to extract numbers from compile time string
static uint8_t conv2d(const char* p) {
uint8_t v = 0;
if ('0' <= *p && *p <= '9')
v = *p - '0';
return 10 * v + *++p - '0';
}
Version 2
For Version 2, The plan is to prepare a PCB that will have an ESP32 WROOM-32 Module and an Onboard Battery management system so we can add a 3.7V Lipo cell and it will power this watch.
As for the sketch part, we can add an internet clock sketch that will take data over the internet to show accurate time instead of using an RTC Module. Also, this watch will have a sleep mode so, after 10 seconds of inactivity, it will go into sleep mode and save power by doing that.
For now, this was just a concept that needs a lot of time for further development.
This is it for today folks, leave a comment if you want any help regarding this project or leave a suggestion maybe on how I can improve this setup.
Special thanks to PCBWAY for providing components for this project, check them out for getting great PCB, PCBA, and many other services for less cost.
Thanks for reading this article and ill see you guys with the next project.
Peace.
#include <TFT_eSPI.h> // Hardware-specific library
#include <SPI.h>
#define TFT_GREY 0x5AEB
TFT_eSPI tft = TFT_eSPI(); // Invoke custom library
uint32_t targetTime = 0; // for next 1 second timeout
static uint8_t conv2d(const char* p); // Forward declaration needed for IDE 1.6.x
uint8_t hh = conv2d(__TIME__), mm = conv2d(__TIME__ + 3), ss = conv2d(__TIME__ + 6); // Get H, M, S from compile time
byte omm = 99, oss = 99;
byte xcolon = 0, xsecs = 0;
unsigned int colour = 0;
void setup(void) {
//Serial.begin(115200);
tft.init();
tft.setRotation(1);
tft.fillScreen(TFT_BLACK);
tft.setTextSize(1);
tft.setTextColor(TFT_YELLOW, TFT_BLACK);
targetTime = millis() + 1000;
}
void loop() {
if (targetTime < millis()) {
// Set next update for 1 second later
targetTime = millis() + 1000;
// Adjust the time values by adding 1 second
ss++; // Advance second
if (ss == 60) { // Check for roll-over
ss = 0; // Reset seconds to zero
omm = mm; // Save last minute time for display update
mm++; // Advance minute
if (mm > 59) { // Check for roll-over
mm = 0;
hh++; // Advance hour
if (hh > 23) { // Check for 24hr roll-over (could roll-over on 13)
hh = 0; // 0 for 24 hour clock, set to 1 for 12 hour clock
}
}
}
// Update digital time
int xpos = 0;
int ypos = 85; // Top left corner ot clock text, about half way down
int ysecs = ypos + 24;
if (omm != mm) { // Redraw hours and minutes time every minute
omm = mm;
// Draw hours and minutes
if (hh < 10) xpos += tft.drawChar('0', xpos, ypos, 8); // Add hours leading zero for 24 hr clock
xpos += tft.drawNumber(hh, xpos, ypos, 8); // Draw hours
xcolon = xpos; // Save colon coord for later to flash on/off later
xpos += tft.drawChar(':', xpos, ypos - 8, 8);
if (mm < 10) xpos += tft.drawChar('0', xpos, ypos, 8); // Add minutes leading zero
xpos += tft.drawNumber(mm, xpos, ypos, 8); // Draw minutes
xsecs = xpos; // Sae seconds 'x' position for later display updates
}
if (oss != ss) { // Redraw seconds time every second
oss = ss;
xpos = xsecs;
if (ss % 2) { // Flash the colons on/off
tft.setTextColor(0x39C4, TFT_BLACK); // Set colour to grey to dim colon
tft.drawChar(':', xcolon, ypos - 8, 8); // Hour:minute colon
//xpos += tft.drawChar(':', xsecs, ysecs, 6); // Seconds colon
tft.setTextColor(TFT_YELLOW, TFT_BLACK); // Set colour back to yellow
}
else {
tft.drawChar(':', xcolon, ypos - 8, 8); // Hour:minute colon
// xpos += tft.drawChar(':', xsecs, ysecs, 3); // Seconds colon
}
//Draw seconds
if (ss < 10) xpos += tft.drawChar('0', xpos, ysecs, 6); // Add leading zero
// tft.drawNumber(ss, xpos, ysecs, 6); // Draw seconds
}
}
}
// Function to extract numbers from compile time string
static uint8_t conv2d(const char* p) {
uint8_t v = 0;
if ('0' <= *p && *p <= '9')
v = *p - '0';
return 10 * v + *++p - '0';
}
ESP32 and Round OLED Smart Watch Concept
- Comments(0)
- Likes(0)
- 0 USER VOTES
- YOUR VOTE 0.00 0.00
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
More by Arnov Arnov sharma
- WALKPi PCB Version Greetings everyone and welcome back, This is the WalkPi, a homebrew audio player that plays music fr...
- Delete Button XL Greetings everyone and welcome back, and here's something fun and useful.In essence, the Delete Butt...
- Arduino Retro Game Controller Greetings everyone and welcome back. Here's something fun.The Arduino Retro Game Controller was buil...
- Super Power Buck Converter Greetings everyone and welcome back!Here's something powerful, The SUPER POWER BUCK CONVERTER BOARD ...
- Pocket Temp Meter Greetings and welcome back.So here's something portable and useful: the Pocket TEMP Meter project.As...
- Pico Powered DC Fan Driver Hello everyone and welcome back.So here's something cool: a 5V to 12V DC motor driver based around a...
- Mini Solar Light Project with a Twist Greetings.This is the Cube Light, a Small and compact cube-shaped emergency solar light that boasts ...
- PALPi V5 Handheld Retro Game Console Hey, Guys what's up?So this is PALPi which is a Raspberry Pi Zero W Based Handheld Retro Game Consol...
- DIY Thermometer with TTGO T Display and DS18B20 Greetings.So this is the DIY Thermometer made entirely from scratch using a TTGO T display board and...
- Motion Trigger Circuit with and without Microcontroller GreetingsHere's a tutorial on how to use an HC-SR505 PIR Module with and without a microcontroller t...
- Motor Driver Board Atmega328PU and HC01 Hey, what's up folks here's something super cool and useful if you're making a basic Robot Setup, A ...
- Power Block Hey Everyone what's up!So this is Power block, a DIY UPS that can be used to power a bunch of 5V Ope...
- Goku PCB Badge V2 Hey everyone what's up!So here's something SUPER cool, A PCB Board themed after Goku from Dragon Bal...
- RGB Mixinator V2 Hey Everyone how you doin!So here's a fun little project that utilizes an Arduino Nano, THE MIXINATO...
- Gengar PCB Art Hey guys and how you doing!So this is the GENGAR PCB Badge or a Blinky Board which is based around 5...
- R2D2 Mini Edition So here's something special, A Mini R2D2 PCB that speaks ASTROMECH.Astromech is a fictional language...
- C-3PO Blinky Board Hey guys and how you doing!So this is the C3P0 PCB Badge or a Blinky Board which is based around 555...
- WALKPi Breadboard Version Greetings everyone and welcome back, Here's something loud and musical.Similar to a traditional walk...
-
Build a Walking Robot: Theo Jansen Style 3D Printed Octopod
166 0 4 -
-
-
kmMiniSchield MIDI I/O - IN/OUT/THROUGH MIDI extension for kmMidiMini
145 0 0 -
DIY Laser Power Meter with Arduino
227 0 3 -
-
-
Box & Bolt, 3D Printed Cardboard Crafting Tools
192 0 2