// ====== Tamagotchi Cat - OLED + FreeTouch (SAMD21/SAMD) ====== #include #include #include #include "Adafruit_FreeTouch.h" // ---------- OLED ---------- #define SCREEN_WIDTH 128 #define SCREEN_HEIGHT 64 #define OLED_ADDR 0x3C // Change to 0x3D if your OLED uses 0x3D Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1); // ---------- TOUCH ---------- #define N_TOUCH 6 // Pads: 05=left, 06=right, 07=up, 04=down, 02, 03 uint8_t touch_pins[N_TOUCH] = {5, 6, 7, 4, 2, 3}; enum { BTN_FEED=0, BTN_PLAY=1, BTN_PET=2, BTN_BATH=3, BTN_SLEEP=4, BTN_MED=5 }; #define THRESHOLD 500 Adafruit_FreeTouch* touch_dev[N_TOUCH]; bool t_now[N_TOUCH]={0}, t_past[N_TOUCH]={0}; int t_val[N_TOUCH]={0}; static inline bool pressed(int b){ return t_now[b] && !t_past[b]; } void updateTouch(){ Ptc *ptc = (Ptc*)PTC; for(int i=0;ibegin(); t_val[i]=touch_dev[i]->measure(); ptc->CTRLA.bit.ENABLE=0; ptc->CTRLA.bit.SWRESET=1; t_past[i]=t_now[i]; t_now[i]= (t_val[i] > THRESHOLD); } } // ---------- PET STATS ---------- struct Stats { int hunger=70; // 0–100 (higher = fuller) int clean=70; // cleanliness int fun=70; // fun int love=70; // affection int health=90; // health bool sleeping=false; } st; int clamp01(int v){ if(v<0) return 0; if(v>100) return 100; return v; } uint32_t lastTick=0; const uint16_t TICK_MS=1000; // tick: decay once per second int computeMood(){ if(st.sleeping) return (st.health+st.love)/2; int base = (st.hunger + st.clean + st.fun + st.love + st.health)/5; int penalty = 0; if(st.hunger<30) penalty += (30-st.hunger); if(st.clean <30) penalty += (30-st.clean); if(st.fun <30) penalty += (30-st.fun); if(st.health<40) penalty += (40-st.health); return clamp01(base - penalty/2); } // ---------- DRAW ---------- void drawBar(int x,int y,int w,int h,int val){ display.drawRect(x,y,w,h,SSD1306_WHITE); int fillw = map(val,0,100,0,w-2); display.fillRect(x+1,y+1,fillw,h-2,SSD1306_WHITE); } void faceBox(int x,int y){ display.drawRoundRect(x,y,64,48,6,SSD1306_WHITE); // ears display.fillTriangle(x+10,y, x+18,y+8, x+4,y+12, SSD1306_WHITE); display.fillTriangle(x+54,y, x+46,y+8, x+60,y+12, SSD1306_WHITE); } void drawFaceHappy(int x,int y){ faceBox(x,y); display.fillCircle(x+20,y+22,2,SSD1306_WHITE); display.fillCircle(x+44,y+22,2,SSD1306_WHITE); display.drawPixel(x+32,y+28,SSD1306_WHITE); display.drawCircle(x+28,y+32,4,SSD1306_WHITE); display.drawCircle(x+36,y+32,4,SSD1306_WHITE); } void drawFaceNeutral(int x,int y){ faceBox(x,y); display.fillRect(x+18,y+22,4,2,SSD1306_WHITE); display.fillRect(x+42,y+22,4,2,SSD1306_WHITE); display.drawLine(x+26,y+34,x+38,y+34,SSD1306_WHITE); } void drawFaceAngry(int x,int y){ faceBox(x,y); display.drawLine(x+16,y+18,x+24,y+22,SSD1306_WHITE); display.drawLine(x+48,y+18,x+40,y+22,SSD1306_WHITE); display.drawLine(x+26,y+36,x+38,y+36,SSD1306_WHITE); } void drawFaceSick(int x,int y){ faceBox(x,y); display.drawLine(x+18,y+20,x+24,y+26,SSD1306_WHITE); display.drawLine(x+24,y+20,x+18,y+26,SSD1306_WHITE); display.drawLine(x+42,y+20,x+48,y+26,SSD1306_WHITE); display.drawLine(x+48,y+20,x+42,y+26,SSD1306_WHITE); display.drawCircle(x+32,y+36,4,SSD1306_WHITE); } void drawFaceSleep(int x,int y){ faceBox(x,y); display.fillRect(x+18,y+24,4,2,SSD1306_WHITE); display.fillRect(x+42,y+24,4,2,SSD1306_WHITE); display.drawLine(x+26,y+34,x+38,y+34,SSD1306_WHITE); display.setCursor(x+45,y+6); display.print("Zz"); } void renderUI(){ int mood = computeMood(); display.clearDisplay(); //: hunger, cleanliness, fun drawBar(0,0,38,8,st.hunger); drawBar(42,0,38,8,st.clean); drawBar(84,0,44,8,st.fun); // cat on the left int x=2, y=12; if(st.sleeping) drawFaceSleep(x,y); else if(st.health<35) drawFaceSick(x,y); else if(mood>70) drawFaceHappy(x,y); else if(mood>40) drawFaceNeutral(x,y); else drawFaceAngry(x,y); // /affectionstatus display.setTextSize(1); display.setTextColor(SSD1306_WHITE); display.setCursor(70,14); display.print("Mood:"); drawBar(70,22,54,8,mood); display.setCursor(70,34); display.print("Love:"); drawBar(70,42,54,8,st.love); display.setCursor(70,54); if(st.sleeping) display.print("Sleeping..."); else if(mood>70) display.print("Happy!"); else if(mood>40) display.print("OK"); else if(st.health<35) display.print("Sick :("); else display.print("Angry!"); display.display(); } // ---------- ACTIONS ---------- void doFeed(){ if(st.sleeping) return; st.hunger = clamp01(st.hunger+25); st.love=clamp01(st.love+5); } void doPlay(){ if(st.sleeping) return; st.fun = clamp01(st.fun+25); st.clean=clamp01(st.clean-10); } void doPet (){ if(st.sleeping) return; st.love = clamp01(st.love+20); st.fun = clamp01(st.fun+10); } void doBath(){ if(st.sleeping) return; st.clean = clamp01(st.clean+30); st.fun = clamp01(st.fun-5); } void doSleepToggle(){ st.sleeping = !st.sleeping; } void doMedicine(){ st.health = clamp01(st.health+30); st.hunger=clamp01(st.hunger-5); } void decayTick(){ int d = st.sleeping?1:2; // slower decay while sleeping st.hunger = clamp01(st.hunger - d); st.clean = clamp01(st.clean - d); st.fun = clamp01(st.fun - (st.sleeping?0:1)); if(st.hunger<25 || st.clean<25) st.health = clamp01(st.health-1); else if(st.health<100 && st.sleeping) st.health = clamp01(st.health+1); if(!st.sleeping && st.love>0) st.love = clamp01(st.love-1); if(st.hunger<10) st.health = clamp01(st.health-1); if(st.clean <10) st.health = clamp01(st.health-1); } // ---------- SETUP / LOOP ---------- void setup(){ Serial.begin(115200); Wire.begin(); if(!display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR)){ while(1){} // OLED init failed } display.clearDisplay(); display.display(); for(int i=0;i= TICK_MS){ lastTick = millis(); decayTick(); } renderUI(); delay(10); }