ESP32 and Round OLED Smart Watch Concept - 10 Steps (With Pictures) - Instructables
ESP32 and Round OLED Smart Watch Concept - 10 Steps (With Pictures) - Instructables
ESP32 and Round OLED Smart Watch Concept - 10 Steps (With Pictures) - Instructables
Here's a super interesting project that utilizes a Round LCD Display connected with an ESP32 board to make a
smartwatch.
This Instructables is gonna be about setting up the Round LCD and basic smartwatch on a breadboard.
Supplies
We use the following materials to set up our smartwatch-
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.
This display was made by Waveshare and you could find all info about this display on their site.
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.
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/
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.
Before testing this display, we need to do some editing on the user setup file.
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
Step 6: Example Sketches #1 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
#include "graphic.h"
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();
startTime = millis();
}
void loop() {
// Start SPI transaction and drop TFT_CS - avoids transaction overhead in loop
tft.startWrite();
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");
}
}
}
Step 7: Example #2 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
#include <TFT_eSPI.h> // Include the graphics library (this includes the sprite functions)
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
delay(WAIT);
}
}
}
// #########################################################################
// Build the scrolling sprite image from scratch, draw text at x = xpos
// #########################################################################
// 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);
// 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);
// #########################################################################
// 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);
// Push sprite to TFT screen CGRAM at coordinate x,y (top left corner)
img.pushSprite(x, y);
// #########################################################################
// 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
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;
}
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.
Step 9: 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 time and only displays hours and minutes.
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
void setup(void) {
//Serial.begin(115200);
tft.init();
tft.setRotation(1);
tft.fillScreen(TFT_BLACK);
tft.setTextSize(1);
tft.setTextColor(TFT_YELLOW, TFT_BLACK);
void loop() {
if (targetTime < millis()) {
// Set next update for 1 second later
targetTime = millis() + 1000;
//Draw seconds
if (ss < 10) xpos += tft.drawChar('0', xpos, ysecs, 6); // Add leading zero
// tft.drawNumber(ss, xpos, ysecs, 6); // Draw seconds
}
}
}
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.