Rohan Barnwal
Published © GPL3+

Smart Focus Cloud

Smart Focus Cloud is a 3D-printed ESP32 desk device with Pomodoro timer, climate sensing & animated progress to boost deep work focus

BeginnerFull instructions provided1 hour2
Smart Focus Cloud

Things used in this project

Hardware components

Espressif ESP32 S3 BOX 3
×1
Jumper wires (generic)
Jumper wires (generic)
×1
Buzzer
Buzzer
×1
DHT11 Temperature & Humidity Sensor (3 pins)
DHT11 Temperature & Humidity Sensor (3 pins)
×1

Software apps and online services

Arduino IDE
Arduino IDE

Hand tools and fabrication machines

Hot glue gun (generic)
Hot glue gun (generic)

Story

Read more

Schematics

Connection

Code

Code

Arduino
#include <Arduino.h>
#include <Wire.h>
#define LGFX_ESP32_S3_BOX_V3
#include <LGFX_AUTODETECT.hpp>
#include <LovyanGFX.hpp>
#include <DHT.h>

static LGFX lcd;

#define DHTPIN 40
#define DHTTYPE DHT11
#define BUZZER_PIN 9

#define W 320
#define H 240

DHT dht(DHTPIN, DHTTYPE);

enum ScreenState { HOME, WORK_SETUP, BREAK_SELECT, RUNNING };
ScreenState screen = HOME;

struct TouchPoint { int16_t x,y; bool touched; };
TouchPoint lastTouch={0,0,false};

// ================= TIMER =================
int work_h=0, work_m=0, work_s=0;
int totalWorkSeconds=0;
int totalBreakSeconds=0;
int initialDuration=0;

bool running=false;
bool isBreak=false;
int selectedField=0;

unsigned long lastTick=0;
int progressPixels=0;

// ================= SOUND =================
void doubleBeep(){
  for(int i=0;i<2;i++){
    tone(BUZZER_PIN,2000,70);
    delay(120);
  }
}

void tripleBeep(){
  for(int i=0;i<3;i++){
    tone(BUZZER_PIN,2500,120);
    delay(180);
  }
}

// ================= TOUCH =================
TouchPoint readTouch(){
  TouchPoint p={0,0,false};
  uint16_t x,y;
  if(lcd.getTouch(&x,&y)){
    p.x=x; p.y=y; p.touched=true;
  }
  return p;
}

// ================= UI =================
void whiteScreen(){
  lcd.fillScreen(TFT_WHITE);
  lcd.setTextColor(TFT_BLACK);
}

void drawButton(int x,int y,int w,int h,String txt){
  lcd.fillRoundRect(x,y,w,h,8,TFT_BLACK);
  lcd.setTextColor(TFT_WHITE);
  lcd.setTextDatum(middle_center);
  lcd.drawString(txt,x+w/2,y+h/2);
}

void showPopup(String l1,String l2){
  lcd.fillRoundRect(40,70,240,100,15,TFT_BLACK);
  lcd.setTextColor(TFT_WHITE);
  lcd.setFont(&fonts::Font2);
  lcd.setTextDatum(middle_center);
  lcd.drawString(l1,W/2,105);
  lcd.drawString(l2,W/2,125);
  delay(2000);
}

// ================= PROGRESS DRAW =================
void drawProgressStep(){

  if(initialDuration<=0) return;

  int sec = isBreak?totalBreakSeconds:totalWorkSeconds;
  float progress = 1.0 - ((float)sec / initialDuration);
  if(progress<0) progress=0;
  if(progress>1) progress=1;

  int perimeter = 2*(W+H);
  int targetPixels = perimeter * progress;

  while(progressPixels < targetPixels){

    int p = progressPixels;

    if(p < W){
      lcd.drawPixel(p,0,TFT_BLACK);
    }
    else if(p < W+H){
      lcd.drawPixel(W-1,p-W,TFT_BLACK);
    }
    else if(p < W+H+W){
      lcd.drawPixel(W-1-(p-(W+H)),H-1,TFT_BLACK);
    }
    else{
      lcd.drawPixel(0,H-1-(p-(W+H+W)),TFT_BLACK);
    }

    progressPixels++;
  }
}

// ================= HOME =================
void drawHome(){
  whiteScreen();
  lcd.setFont(&fonts::FreeSansBold18pt7b);
  lcd.setTextDatum(middle_center);
  lcd.drawString("Ready To Focus?",W/2,90);

  float t=dht.readTemperature();
  float h=dht.readHumidity();

  lcd.setFont(&fonts::Font4);
  lcd.drawString("Temp: "+String(t,1)+" C",W/2,170);
  lcd.drawString("Hum : "+String(h,1)+" %",W/2,205);

  screen=HOME;
}

// ================= WORK SETUP =================
void drawWorkSetup(){
  whiteScreen();
  lcd.setFont(&fonts::Font7);
  lcd.setTextDatum(middle_center);

  char buf[20];
  sprintf(buf,"%02d:%02d:%02d",work_h,work_m,work_s);
  lcd.drawString(buf,W/2,90);

  int baseX=W/2-90;
  if(selectedField==0) lcd.fillRect(baseX,125,40,4,TFT_BLACK);
  if(selectedField==1) lcd.fillRect(baseX+60,125,40,4,TFT_BLACK);
  if(selectedField==2) lcd.fillRect(baseX+120,125,40,4,TFT_BLACK);

  lcd.setFont(&fonts::Font4);
  drawButton(30,170,80,45,"+");
  drawButton(120,170,80,45,"-");
  drawButton(210,170,80,45,"->");

  screen=WORK_SETUP;
}

// ================= BREAK SELECT =================
int breakOptions[8]={5,10,15,20,25,30,40,50};

void drawBreakSelect(){
  whiteScreen();
  lcd.setFont(&fonts::Font4);
  lcd.setTextDatum(middle_center);
  lcd.drawString("Select Break (min)",W/2,30);

  int idx=0;
  for(int r=0;r<3;r++){
    for(int c=0;c<3;c++){
      if(idx>=8) break;
      int x=30+c*95,y=60+r*55;
      drawButton(x,y,80,45,String(breakOptions[idx]));
      idx++;
    }
  }
  screen=BREAK_SELECT;
}

// ================= RUNNING =================
void drawRunning(){
  whiteScreen();
  progressPixels=0;

  lcd.setFont(&fonts::Font7);
  lcd.setTextDatum(middle_center);

  int sec=isBreak?totalBreakSeconds:totalWorkSeconds;
  int hh=sec/3600;
  int mm=(sec%3600)/60;
  int ss=sec%60;

  char buf[20];
  sprintf(buf,"%02d:%02d:%02d",hh,mm,ss);
  lcd.drawString(buf,W/2,90);

  lcd.setFont(&fonts::Font4);
  drawButton(60,170,90,45,running?"Pause":"Play");
  drawButton(170,170,90,45,"Reset");

  screen=RUNNING;
}

// ================= TIMER UPDATE =================
void updateTimer(){

  if(!running) return;

  if(millis()-lastTick>=1000){
    lastTick=millis();

    if(!isBreak){
      if(totalWorkSeconds>0) totalWorkSeconds--;
      else{
        running=false;
        tripleBeep();
        showPopup("Break Time!","Relax πŸ™‚");
        isBreak=true;
        running=true;
        initialDuration=totalBreakSeconds;
        drawRunning();
        return;
      }
    }
    else{
      if(totalBreakSeconds>0) totalBreakSeconds--;
      else{
        running=false;
        tripleBeep();
        showPopup("Back To Work Buddy","Lets Go πŸ’ͺ");
        work_h=work_m=work_s=0;
        totalWorkSeconds=0;
        totalBreakSeconds=0;
        isBreak=false;
        drawHome();
        return;
      }
    }

    drawRunning();
  }

  drawProgressStep();
}

// ================= TOUCH =================
void handleTouch(int x,int y){

  if(screen==HOME){
    drawWorkSetup();
  }

  else if(screen==WORK_SETUP){

    if(y>60 && y<130){
      if(x>W/2-100 && x<W/2-40) selectedField=0;
      else if(x>W/2-20 && x<W/2+40) selectedField=1;
      else if(x>W/2+60 && x<W/2+120) selectedField=2;
      drawWorkSetup();
      return;
    }

    if(x>30&&x<110&&y>170&&y<215){
      doubleBeep();
      if(selectedField==0) work_h++;
      if(selectedField==1&&work_m<59) work_m++;
      if(selectedField==2&&work_s<59) work_s++;
    }

    if(x>120&&x<200&&y>170&&y<215){
      doubleBeep();
      if(selectedField==0&&work_h>0) work_h--;
      if(selectedField==1&&work_m>0) work_m--;
      if(selectedField==2&&work_s>0) work_s--;
    }

    if(x>210&&x<290&&y>170&&y<215){
      totalWorkSeconds=work_h*3600+work_m*60+work_s;
      initialDuration=totalWorkSeconds;
      drawBreakSelect();
      return;
    }

    drawWorkSetup();
  }

  else if(screen==BREAK_SELECT){
    for(int i=0;i<8;i++){
      int r=i/3,c=i%3;
      int bx=30+c*95,by=60+r*55;
      if(x>bx&&x<bx+80&&y>by&&y<by+45){
        doubleBeep();
        totalBreakSeconds=breakOptions[i]*60;
        running=true;
        lastTick=millis();
        initialDuration=totalBreakSeconds;
        drawRunning();
        return;
      }
    }
  }

  else if(screen==RUNNING){

    if(x>60&&x<150&&y>170&&y<215){
      doubleBeep();
      running=!running;
      drawRunning();
    }

    if(x>170&&x<260&&y>170&&y<215){
      doubleBeep();
      running=false;
      work_h=work_m=work_s=0;
      totalWorkSeconds=0;
      totalBreakSeconds=0;
      isBreak=false;
      drawHome();
    }
  }
}

// ================= SETUP =================
void setup(){
  lcd.init();
  lcd.setBrightness(255);
  dht.begin();
  pinMode(BUZZER_PIN,OUTPUT);
  drawHome();
}

// ================= LOOP =================
void loop(){
  TouchPoint t=readTouch();
  if(t.touched && !lastTouch.touched){
    handleTouch(t.x,t.y);
  }
  lastTouch=t;
  updateTimer();
}

Credits

Rohan Barnwal
42 projects β€’ 38 followers
Rohan Barnwal - maker, hacker, tech enthusiast. I explore new tech & find innovative solutions. See my projects on hackster.io!

Comments