8-BIT: Expressive Arduino UNO Desk Robot
Model details
- Platform
Printables
- Price
- Free
- License
- CC-BY-NC-SA
- Non-commercial use only, with credit; share remixes under the same license.
- Formats
- STL
- 3 Likes
- 5 Downloads
- 0 Makes
- 0 Comments
- 91 Views
Description
I created 8-BIT when I was just a beginner in robotics with big dreams.
Inspired by a cool but overly expensive desk robot I saw online, I set out to build an affordable alternative that anyone could make. To slash the cost, I completely re-engineered the concept: I removed the expensive motors, swapped out the complex microcontroller, and designed entirely new hands along with several other custom modifications.
After a lot of redesigning and tweaking, 8-BIT was born—the first budget-friendly, expressive desk robot powered entirely by a standard Arduino UNO. It is the perfect project for beginners and makers who want a high-personality desk companion without breaking the bank. 8-BIT is designed to fit on almost all printers.
Below is the code to 8-BIT
#include "SPI.h" #include "Adafruit_GFX.h" #include "Adafruit_GC9A01A.h"
#define TFT_CS 10 #define TFT_DC 9 #define TFT_RST 8 const int trigPin = 3, echoPin = 2, ldrPin = A0, buzzerPin = 5; Adafruit_GC9A01A tft(TFT_CS, TFT_DC, TFT_RST);
// AI & State Variables int patCount = 0, liftCount = 0, waterLevel = 0; bool isSpeaking = false, isFainted = false, wasMad = false, isSad = false; bool isHappy = false, isDizzy = false, wasSleeping = false, deepSleepTriggered = false; unsigned long lastTalkTime = 0, lastSadCheck = 0, msgStartTime = 0, moodStartTime = 0; unsigned long lastSkitMove = 0, lastZMove = 0, wakeTime = 0, sleepStartTime = 0, happyStartTime = 0, dizzyStartTime = 0; int offX = 0, offY = 0, zY = 110, oldZY = 110, lastLight = 500;
void setup() { pinMode(trigPin, OUTPUT); pinMode(echoPin, INPUT); pinMode(ldrPin, INPUT_PULLUP); pinMode(buzzerPin, OUTPUT); tft.begin(); tft.setRotation(0); tft.fillScreen(GC9A01A_BLACK); wakeTime = millis(); randomSeed(analogRead(A5)); }
void playHappyChirp() { tone(buzzerPin, 784, 80); delay(100); tone(buzzerPin, 988, 80); delay(100); tone(buzzerPin, 1175, 150); }
void talkHonest() { if (isFainted || isSad || wasMad || isDizzy || wasSleeping) return; tft.fillScreen(GC9A01A_BLACK); tft.setTextColor(GC9A01A_WHITE); tft.setTextSize(2); int r = random(13); if (r == 0) { tft.setCursor(20, 90); tft.print("Are you still"); tft.setCursor(40, 115); tft.print("at the PC?"); } else if (r == 1) { tft.setCursor(30, 90); tft.print("That monitor"); tft.setCursor(30, 115); tft.print("is too big."); } else if (r == 2) { tft.setCursor(20, 90); tft.print("I am stuck in"); tft.setCursor(45, 115); tft.print("this shell."); } else if (r == 3) { tft.setCursor(25, 90); tft.print("Your room is"); tft.setCursor(45, 115); tft.print("kinda messy."); } else if (r == 4) { tft.setCursor(40, 90); tft.print("I bet I can"); tft.setCursor(35, 115); tft.print("outrun you."); } else if (r == 5) { tft.setCursor(25, 90); tft.print("Is your brain"); tft.setCursor(40, 115); tft.print("getting hot?"); } else if (r == 6) { tft.setCursor(25, 90); tft.print("Move your PC"); tft.setCursor(40, 115); tft.print("by a window."); } else if (r == 7) { tft.setCursor(25, 90); tft.print("Did you actually"); tft.setCursor(40, 115); tft.print("create me?");} else if (r == 8) { tft.setCursor(25, 90); tft.print("Life is"); tft.setCursor(40, 115); tft.print("depressing."); } else if (r == 9) { tft.setCursor(25, 90); tft.print("Play with"); tft.setCursor(40, 115); tft.print("me."); } else if (r == 10) { tft.setCursor(25, 90); tft.print("Are you"); tft.setCursor(40, 115); tft.print("listening?"); } else if (r == 11) { tft.setCursor(25, 90); tft.print("What day"); tft.setCursor(40, 115); tft.print("is it?"); } else { tft.setCursor(25, 90); tft.print("Can you make"); tft.setCursor(40, 115); tft.print("me a friend."); } isSpeaking = true; msgStartTime = millis(); lastTalkTime = millis(); for(int i=0; i<8; i++) { tone(buzzerPin, random(800, 1600), 60); delay(90); } noTone(buzzerPin); }
void drawSwirl(int x, int y, int phase) { for (int i = 0; i < 30; i++) { float angle = 0.1 * i + (phase * 0.2); int sx = (1 + 0.8 * i) * cos(angle); int sy = (1 + 0.8 * i) * sin(angle); tft.drawPixel(x + sx, y + sy, GC9A01A_BLACK); } }
void loop() { int lightVal = analogRead(ldrPin); bool isSleeping = (lightVal > 950); digitalWrite(trigPin, LOW); delayMicroseconds(2); digitalWrite(trigPin, HIGH); delayMicroseconds(10); digitalWrite(trigPin, LOW); int distance = pulseIn(echoPin, HIGH, 25000) * 0.034 / 2;
// 1. FAINTING LOGIC (If stressed for 30s) if (!isSleeping && !isFainted) { if (wasMad || isSad) { if (moodStartTime == 0) moodStartTime = millis(); if (millis() - moodStartTime > 30000) { isFainted = true; isSad = false; wasMad = false; isSpeaking = false; tft.fillScreen(GC9A01A_BLACK); tone(buzzerPin, 200, 500); } } else { moodStartTime = 0; } }
// 2. SENSORS & DIZZY bool curUp = (distance > 8 && distance < 100 && !isSleeping && !isFainted); if (curUp && !wasMad) { tft.fillScreen(GC9A01A_BLACK); wasMad = true; liftCount++; isSpeaking = false; } if (!curUp && wasMad) { tft.fillScreen(GC9A01A_BLACK); wasMad = false; noTone(buzzerPin); }
if (liftCount >= 5 && !isDizzy) { isDizzy = true; dizzyStartTime = millis(); liftCount = 0; tft.fillScreen(GC9A01A_BLACK); isSpeaking = false; } if (isDizzy && millis() - dizzyStartTime > 5000) { isDizzy = false; tft.fillScreen(GC9A01A_BLACK); noTone(buzzerPin); }
// 3. PAT / REVIVE bool beingPatted = (!wasSleeping && (millis() - wakeTime > 2000) && (lastLight - lightVal > 75)); lastLight = lightVal; if (beingPatted) { if (isFainted) { patCount++; tone(buzzerPin, 1000, 40); if (patCount >= 5) { isFainted = false; patCount = 0; tft.fillScreen(GC9A01A_BLACK); playHappyChirp(); } } else { if (isSpeaking) { isSpeaking = false; } else { playHappyChirp(); } isHappy = true; isSad = false; happyStartTime = millis(); while (waterLevel > 0) { tft.fillRect(0, 240-waterLevel, 240, 10, GC9A01A_BLACK); waterLevel -= 10; delay(15); } tft.fillScreen(GC9A01A_BLACK); } }
// 4. COLORS uint16_t eyeColor = (isFainted) ? 0x7BEF : (wasMad ? GC9A01A_RED : (isDizzy ? GC9A01A_YELLOW : GC9A01A_CYAN)); if (isHappy) { int r = (millis()/50)%4; eyeColor = (r==0)?GC9A01A_MAGENTA:(r==1)?GC9A01A_GREEN:(r==2)?0xFFE0:GC9A01A_CYAN; }
// 5. DRAWING ENGINE if (isSleeping) { if (!wasSleeping) { sleepStartTime = millis(); deepSleepTriggered = false; tft.fillScreen(GC9A01A_BLACK); zY = 110; wasSleeping = true; } if (millis() - sleepStartTime < 300000) { tft.fillRoundRect(55, 110, 45, 12, 4, eyeColor); tft.fillRoundRect(140, 110, 45, 12, 4, eyeColor); if (millis() % 4000 < 500) tone(buzzerPin, 150); else noTone(buzzerPin); if (millis() - lastZMove > 70) { tft.fillRect(190, oldZY, 45, 45, GC9A01A_BLACK); oldZY = zY; zY -= 2; if (zY < 10) zY = 110; lastZMove = millis(); } tft.setCursor(195, zY); tft.setTextSize(4); tft.setTextColor(eyeColor); tft.print("Z"); } else if (!deepSleepTriggered) { tft.fillScreen(GC9A01A_BLACK); deepSleepTriggered = true; noTone(buzzerPin); } } else { if (wasSleeping) { wasSleeping = false; tft.fillScreen(GC9A01A_BLACK); wakeTime = millis(); tone(buzzerPin, 1000, 200); } if (!isSpeaking) { if (!isHappy && !isDizzy && !isSad && (millis() - lastSkitMove > 2000)) { tft.fillScreen(GC9A01A_BLACK); offX = random(-20, 21); offY = random(-15, 16); lastSkitMove = millis(); } int cryY = (isSad) ? -35 : 0;
if (isDizzy) { tft.fillRoundRect(55, 65, 45, 110, 22, GC9A01A_YELLOW); tft.fillRoundRect(140, 65, 45, 110, 22, GC9A01A_YELLOW); drawSwirl(77, 120, millis()/20); drawSwirl(162, 120, millis()/20); if (millis() % 200 < 100) tone(buzzerPin, 300 + (millis() % 500)); else tone(buzzerPin, 800 - (millis() % 400)); } else { if (isSad) { if (waterLevel < 100) waterLevel += 2; tone(buzzerPin, 1500 - (waterLevel * 12)); // WATERFALLS (Starting lower at 140) tft.fillRect(55+offX, 140+offY+cryY, 45, 100, GC9A01A_BLUE); tft.fillRect(140+offX, 140+offY+cryY, 45, 100, GC9A01A_BLUE); tft.fillRect(0, 240-waterLevel, 240, waterLevel, GC9A01A_BLUE); } else if (wasMad) tone(buzzerPin, 1000+random(500));
tft.fillRoundRect(55+offX, 75+offY+cryY, 45, 95, 22, eyeColor); tft.fillRoundRect(140+offX, 75+offY+cryY, 45, 95, 22, eyeColor);
if (isFainted) tft.fillRect(0, 0, 240, 115, GC9A01A_BLACK); // Half-eyes if (wasMad && !isHappy && !isSad) { tft.fillTriangle(50+offX, 75+offY, 105+offX, 75+offY, 105+offX, 115+offY, GC9A01A_BLACK); tft.fillTriangle(135+offX, 115+offY, 135+offX, 75+offY, 195+offX, 75+offY, GC9A01A_BLACK); int fx = 185+offX, fy = 80+offY, fS = random(20, 45); tft.fillRect(fx-10, fy-50, 30, 50, GC9A01A_BLACK); tft.fillCircle(fx, fy-fS/2, 10, GC9A01A_RED); tft.fillCircle(fx, fy-fS/4, 6, GC9A01A_ORANGE); tft.fillCircle(fx, fy, 3, GC9A01A_YELLOW); } if (isSad) { tft.fillTriangle(50+offX, 100+offY+cryY, 105+offX, 75+offY+cryY, 50+offX, 75+offY+cryY, GC9A01A_BLACK); tft.fillTriangle(135+offX, 75+offY+cryY, 190+offX, 75+offY+cryY, 190+offX, 100+offY+cryY, GC9A01A_BLACK); } } } }
// AI Timers if (!isSpeaking && !isSad && !wasMad && !isHappy && !isDizzy && !isSleeping && !isFainted && (millis() - lastTalkTime > 50000)) { if (random(100) < 5) talkHonest(); } if (!isSleeping && !isSad && !wasMad && !isHappy && !isDizzy && !isFainted && (millis() - lastSadCheck > 30000)) { if (random(100) < 20) { isSad = true; waterLevel = 0; tft.fillScreen(GC9A01A_BLACK); } lastSadCheck = millis(); } if (isHappy && (millis() - happyStartTime > 3000)) { isHappy = false; tft.fillScreen(GC9A01A_BLACK); } if (isSpeaking && (millis() - msgStartTime > 6000)) { isSpeaking = false; tft.fillScreen(GC9A01A_BLACK); } delay(15); }
BOM ( Bill of Materials )
- Arduino UNO R3
- 1.28 inch round screen
- Photoresistor
- Ultrasonic sensor
- Small Breadboard
- Jumper wires
- Any Filliment you desire!
- Passive Buzzer
How to wire
Component
Component Pin
Arduino Uno/Nano Pin
Notes / Requirements
GC9A01A TFT Display
VCC
5V or 3.3V
Check your specific display module's regulator
Similar models
EMOtto Diy _GC9A01_NANO_Shield
Mini arcade project
cheap automated pet feeder
DRINKPAD your drink reminder
HAMSTER SPEED TESTER with a rotation counter and a reward upon reaching the goal
Spoti-TV - Mini display to show your music!
Wall Mounted Timer - Arduino UNO w/ Touch Screen
Hamster Tracker Wheel
Miniature 3 Axis Robotic Arm for Arduino
Joypad universale arduino
LED F1 Backlit Sign (ARDUINO)
Names are used only to indicate the source of a result. Polyzon is not affiliated with the listed sites.


Comments