Yiğit
Published © GPL3+

Another Desk Pet Project

I built a small desk pet with ESP32-C3 Supermini.

BeginnerFull instructions provided2 hours470
Another Desk Pet Project

Things used in this project

Hardware components

XIAO ESP32C3
Seeed Studio XIAO ESP32C3
ESP32C3 Supermini has been chosen for compatibility
×1
Touch Sensor - TTP223B
×1
OLED Screen - 1.3 inch
×1
Charging Component - TP4056
×1
Battery - 650 mAh
×1

Software apps and online services

Arduino IDE
Arduino IDE

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)
Solder Wire, Lead Free
Solder Wire, Lead Free
Hot glue gun (generic)
Hot glue gun (generic)
3D Printer (generic)
3D Printer (generic)
Solder Paste, Tub
Solder Paste, Tub

Story

Read more

Schematics

Schematic

Code

Code

C/C++
#include <Arduino.h>
#include <U8g2lib.h>
#include <Wire.h>
#include "esp_sleep.h"

U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);

const int touchPin = 2;
float eyeX = 0, eyeY = 0;
float targetX = 0, targetY = 0;

unsigned long lastActivityTime = 0;
unsigned long touchStartTime = 0;
const long screenTimeout = 100000;    
const long deepSleepTimeout = 300000; 
bool isScreenOff = false;
bool easterEggActive = false;

void setup() {
  Wire.begin(8, 9); 
  u8g2.begin();
  pinMode(touchPin, INPUT);
  
  esp_deep_sleep_enable_gpio_wakeup(1 << touchPin, ESP_GPIO_WAKEUP_GPIO_HIGH);

  // Custom startup screen
  u8g2.clearBuffer();
  u8g2.setFont(u8g2_font_haxrcorp4089_tr);
  u8g2.drawStr(35, 30, "WELCOME!");
  u8g2.drawStr(25, 45, "TO YOUR DESK PET!");
  u8g2.sendBuffer();
  delay(2500);
  
  lastActivityTime = millis();
}

void drawMiniHeart(int x, int y, int size) {
  u8g2.drawDisc(x - size/2, y, size/2);
  u8g2.drawDisc(x + size/2, y, size/2);
  u8g2.drawTriangle(x - size, y, x + size, y, x, y + size + 1);
}

void drawEasterEgg() {
  u8g2.clearBuffer();
  float pulse = (sin(millis() / 150.0) * 5) + 20; // Pulsing giant heart
  drawMiniHeart(64, 25, pulse);
  u8g2.setFont(u8g2_font_6x10_tf);
  u8g2.drawStr(35, 55, "I LOVE YOU!"); // Secret message
  u8g2.sendBuffer();
}

void drawBlush(int x, int y) {
  for(int i=0; i<3; i++) {
    u8g2.drawPixel(x+i*2, y);
    u8g2.drawPixel(x+i*2+1, y+1);
  }
}

void drawSmilingEye(int x, int y, int radius, int offset) {
  u8g2.drawCircle(x, y + offset, radius, U8G2_DRAW_UPPER_RIGHT | U8G2_DRAW_UPPER_LEFT);
  u8g2.drawCircle(x, y + 1 + offset, radius, U8G2_DRAW_UPPER_RIGHT | U8G2_DRAW_UPPER_LEFT);
}

void drawNormalEye(int x, int y) {
  u8g2.drawRBox(x - 12, y - 6, 25, 12, 3);
}

void drawSleepyEye(int x, int y) {
  u8g2.drawRBox(x - 12, y + 2, 25, 4, 1);
}

void loop() {
  int touchState = digitalRead(touchPin);
  unsigned long currentTime = millis();

  // Handle touch
  if (touchState == HIGH) {
    if (touchStartTime == 0) touchStartTime = currentTime;
    lastActivityTime = currentTime;
    isScreenOff = false;

    // Trigger if held for 2 seconds
    if (currentTime - touchStartTime > 2000) {
      easterEggActive = true;
    }
  } else {
    touchStartTime = 0;
    easterEggActive = false;
  }

  // Deep sleep
  if (currentTime - lastActivityTime > deepSleepTimeout) {
    u8g2.clearBuffer();
    u8g2.sendBuffer();
    esp_deep_sleep_start();
  }

  // Screen sleep
  if (currentTime - lastActivityTime > screenTimeout) {
    if (!isScreenOff) {
      u8g2.clearBuffer();
      u8g2.sendBuffer();
      isScreenOff = true;
    }
    return;
  }

  // Render display
  if (easterEggActive) {
    drawEasterEgg();
  } else {
    u8g2.clearBuffer();
    eyeX += (targetX - eyeX) * 0.15;
    eyeY += (targetY - eyeY) * 0.15;

    if (touchState == HIGH) {
      targetX = 0; targetY = -7; 
      int giggle = sin(currentTime / 40.0) * 2; 
      float floatAnim = sin(currentTime / 200.0) * 4; 

      drawSmilingEye(44 + eyeX, 32 + eyeY, 11, giggle);
      drawBlush(38 + eyeX, 48 + eyeY);
      drawSmilingEye(84 + eyeX, 32 + eyeY, 11, giggle);
      drawBlush(78 + eyeX, 48 + eyeY);
      drawMiniHeart(64, 45 + giggle, 3);
      drawMiniHeart(15, 25 + floatAnim, 4);
      drawMiniHeart(105, 20 - floatAnim, 5); 
    } 
    else if (currentTime - lastActivityTime > (screenTimeout * 0.75)) {
      targetX = 0; targetY = 10;
      drawSleepyEye(44 + eyeX, 32 + eyeY);
      drawSleepyEye(84 + eyeX, 32 + eyeY);
    }
    else {
      if (random(100) > 96) {
        targetX = random(-15, 15);
        targetY = random(-10, 10);
      }
      drawNormalEye(44 + eyeX, 32 + eyeY);
      drawNormalEye(84 + eyeX, 32 + eyeY);

      if (random(100) > 98) { // Blink
        u8g2.clearBuffer();
        u8g2.sendBuffer();
        delay(80); 
      }
    }
    u8g2.sendBuffer();
  }
  delay(20);
}

Credits

Yiğit
5 projects • 2 followers
EEE student
Thanks to Dave0616.

Comments