Arduino Tamagotchi Pet

by 818236 in Circuits > Arduino

82 Views, 1 Favorites, 0 Comments

Arduino Tamagotchi Pet

engineering thumbnail.jpg

Want to teach your kid how the idea of taking care of a pet without actually buying one ( does not actually help your kid learn how to take care of a pet) or maybe you want a pet without the hassle

Supplies

IMG_2728.jpeg
IMG_2727.jpeg
IMG_2706.jpeg
IMG_2705.jpeg
IMG_2698.jpeg
Image_260614_120821.jpeg
IMG_2697.jpeg

Wiring and Coding Your LCD I2C

Screenshot 2026-06-14 at 11.59.33 AM.png
Image_260614_121035.jpeg
IMG_2698.jpeg

Your LCD I2C screen is where your going to see your Creature, and their stats so you need to make sure everything is plugged in correctly.

Your Lcd I2C screen has 4 inputs that need to be aligned : Gnd,Vcc,Sda,Scl

Your gnd pin has to be connected to ground and the vcc pin needs to be connected to power. both of these pins cannot be placed anywhere else you sda and scl pins location depend on what type of microcontroller you use.

If your using the same microcontroller as me(arduino uno) the sda pin has to go to A4 and the scl pin needs to connect to A5.

the sda controls the data that goes through its what sends your code to the lcd and the scl controls how fast bytes of data travel through

before adding the code you will need libraries for which i will link a video too here

the code needed for this part is

#include <Wire.h>

#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27,16,2); // set the LCD address to 0x27

this code is needed before your setup to ensure that you have the libraries for the screen



Pre Setup and Loop Code

IMG_2707.jpeg
IMG_2729.jpeg

Once you've added your lcd screen the next important part is add code to your project without it you wont be able to view your tamagotchi. The code that we are going to adding will add our tamagotchi character, its cleanliness,food and enjoyment bars

the first part of the code is create what our character looks like i based my tamagotchi off pochita from chainsaw man:

// --- Pochita Custom Pieces ---
byte topLeft[] = { B00000, B01111, B01111, B00000, B00000, B00000, B00000, B00000 };
byte topCenter[] = { B00111, B01000, B11111, B11101, B11111, B11111, B11111, B11111 };
byte topRight[] = { B11000, B00100, B00111, B01111, B11111, B11111, B11111, B11111 };
byte botLeftF1[] = { B00011, B01100, B01111, B00111, B00011, B00111, B01001, B00000 };
byte botCenterF1[] = { B11111, B11111, B11111, B11111, B11111, B11111, B01001, B00000 };
byte botRightF1[] = { B11111, B11111, B11111, B11111, B11111, B01110, B00100, B00010 };
byte botLeftF2[] = { B00011, B01100, B01111, B00111, B00011, B00111, B00110, B00000 };
byte botCenterF2[] = { B11111, B11111, B11111, B11111, B11111, B11111, B00110, B00000 };

(i do not claim pochita as my own creaton it belongs to mappa and tatsuki fujimoto, The code to create his design was created through ai (because i suck at 8 bit art) everything else was my own making)

This code works by telling each square on your lcd screen whether to be on or off. if we look at byte top left its telling the top left of lcd follow the binary code written out for it following. the 0's mean that part of the display is off and the 1's are signalling a display the top left would look like this

B00111 --> ■■■

B01000 --> ■

B11111 --> ■■■■■

B11101 --> ■■■ ■

B11111 --> ■■■■■

B11111 --> ■■■■■

B11111 --> ■■■■■

B11111 --> ■■■■■


The reason some of them say f1 and f2 is because those parts have two frames which we will code to alternate every so often


the next part of code you will need is your tamagotchis stats; the stats control how big your bars are and their max values ( you can change these values to however you want them to be but remember that they are going to change the size of your bars).:

// --- Tamagotchi Stat Variables (Maxed out at 7 on start) ---
int hunger = 7;
int enjoyment = 7;
int cleanliness = 7;


The next piece of coded added to ensure that your creature is alive theres no point in creating your tamagotchi if you have nothing to lose:

//pochita is alive variable
bool isAlive = true;


This piece of code is for your arduino to understand what is connected to each of its pins:

// --- Hardware Pin Definitions ---
int PR = 13;//pir sensor
int Hbutt = 11;
int Buzz = 8;
int ldr = A3;


Now were just telling giving starting values for each of our input receivers:

//States for Debouncing
bool lastFeedState = HIGH;
bool lastEnjoymentState = HIGH;
int LastCleanState = 1023;


Timers so that when you give an input it stops you from spamming the input, creating a timer to measure if it has been long enough for something to drain or if your animation can switch:

//Timer Baselines
unsigned long previousAnimMillis = 0;
unsigned long previousStatMillis = 0;
unsigned long previousHungerMillis = 0;
unsigned long previousEnjoyMillis = 0;
unsigned long previousCleanMillis = 0;


these values control how what your timer baselines have to reach before restarting; the amount of time it will take for something to drain or change:

// Drain Rates
const long animInterval = 350; // Animation speed
const long statInterval = 2000; // Display rotation speed (Strictly 2 seconds)
const long hungerDrainInterval = 10000; // Hunger drops every 10 seconds
const long enjoyDrainInterval = 18000; // Enjoyment drops every 18 seconds
const long cleanDrainInterval = 6000; // Cleanliness drops every 6 seconds


the boolean controls that the frames will switch and the statcycle controls which stat is showing:

bool animFrame = true;
int statCycle = 0;


void drawbar is what creates the bars that will be showing :

void drawBar(const char* label, int value) {
lcd.setCursor(5, 1);
lcd.print(label);
lcd.print("[");
for (int i = 0; i < 7; i++) {
if (i < value) {
lcd.print((char)255);
} else {
lcd.print(" ");
}
}
lcd.print("]");
}


Incase you plan to add a buzzer your also going to need sounds for said buzzer. The way the code works is by controlling the tone and timing in order to create a sound that represents what your going for, this code is mainly designed for dogs so if you want a different type of animal for your tamagotchi i suggest messing around with the frequency. you will need to find the wiring yourself as i was unable to get a buzzer for my tamgotchi

Tone(variable name, frequency in hertz);

// SOUND EFFECT FUNCTIONS


// hunger sound
void playEatSound() {
tone(Buzz, 400); delay(60);
tone(Buzz, 250); delay(80);
noTone(Buzz); delay(40);
tone(Buzz, 350); delay(60);
tone(Buzz, 200); delay(80);
noTone(Buzz);
}


// cleaning sound
void playBubbleSound() {
for (int i = 0; i < 3; i++) {
tone(Buzz, 1200); delay(20);
tone(Buzz, 1600); delay(25);
noTone(Buzz); delay(40);
}
}


// enjoyment sound
void playBarkSound() {
// First bark
for (int freq = 700; freq > 250; freq -= 45) {
tone(Buzz, freq);
delay(4);
}
noTone(Buzz);
delay(80); // Pause between barks
// Second bark
for (int freq = 750; freq > 300; freq -= 45) {
tone(Buzz, freq);
delay(4);
}
noTone(Buzz);
}


Right here im going to post all of the pre setup code so you dont have to individually paste them

//pre setup

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27,16,2); // set the LCD address to 0x27 for a 16 chars and 2 line display
// --- Pochita Custom Pieces ---
byte topLeft[] = { B00000, B01111, B01111, B00000, B00000, B00000, B00000, B00000 };
byte topCenter[] = { B00111, B01000, B11111, B11101, B11111, B11111, B11111, B11111 };
byte topRight[] = { B11000, B00100, B00111, B01111, B11111, B11111, B11111, B11111 };
byte botLeftF1[] = { B00011, B01100, B01111, B00111, B00011, B00111, B01001, B00000 };
byte botCenterF1[] = { B11111, B11111, B11111, B11111, B11111, B11111, B01001, B00000 };
byte botRightF1[] = { B11111, B11111, B11111, B11111, B11111, B01110, B00100, B00010 };
byte botLeftF2[] = { B00011, B01100, B01111, B00111, B00011, B00111, B00110, B00000 };
byte botCenterF2[] = { B11111, B11111, B11111, B11111, B11111, B11111, B00110, B00000 };

// --- Tamagotchi Stat Variables (Maxed out at 7 on start) ---
int hunger = 7;
int enjoyment = 7;
int cleanliness = 7;
//pochita is alive variable
bool isAlive = true;

// --- Hardware Pin Definitions ---
int PR = 13;//pir sensor
int Hbutt = 11;
int Buzz = 8;
int ldr = A3;

//States for Debouncing
bool lastFeedState = HIGH;
bool lastEnjoymentState = HIGH;
int LastCleanState = 1023;

//Timer Baselines
unsigned long previousAnimMillis = 0;
unsigned long previousStatMillis = 0;
unsigned long previousHungerMillis = 0;
unsigned long previousEnjoyMillis = 0;
unsigned long previousCleanMillis = 0;

// Drain Rates
const long animInterval = 350; // Animation speed
const long statInterval = 2000; // Display rotation speed (Strictly 2 seconds)
const long hungerDrainInterval = 10000; // Hunger drops every 10 seconds
const long enjoyDrainInterval = 18000; // Enjoyment drops every 18 seconds
const long cleanDrainInterval = 6000; // Cleanliness drops every 6 seconds

bool animFrame = true;
int statCycle = 0;

void drawBar(const char* label, int value) {
lcd.setCursor(5, 1);
lcd.print(label);
lcd.print("[");
for (int i = 0; i < 7; i++) {
if (i < value) {
lcd.print((char)255);
} else {
lcd.print(" ");
}
}
lcd.print("]");
}

Void Setup

void setup is the part of your code that only runs once at the start were going to use this code in order to setup our lcd screen positions and telling the arduino whether it should output or input from, were also telling our arduino to start recording the serial values that are being inputted

void setup() {
lcd.init();
lcd.backlight();
Serial.begin(9600);
pinMode (PR, INPUT);
pinMode(Hbutt, INPUT);
pinMode(Buzz, OUTPUT);
//pinMode(ldr,INPUT);
lcd.createChar(0, topLeft);
lcd.createChar(1, topCenter);
lcd.createChar(2, topRight);
lcd.createChar(3, botLeftF1);
lcd.createChar(4, botCenterF1);
lcd.createChar(5, botRightF1);
lcd.createChar(6, botLeftF2);
lcd.createChar(7, botCenterF2);

lcd.setCursor(0, 0); lcd.write(0);
lcd.setCursor(1, 0); lcd.write(1);
lcd.setCursor(2, 0); lcd.write(2);

lcd.setCursor(8, 0);
lcd.print("POCHITA");
}


after this all of the code that we add will be put in void loop

Void Loop

this is the part of the code that when added will continuously go on forever unless either the arduino loses power or the code tells it to stop

void loop() {
Serial.println(digitalRead(PR));
if ((digitalRead(PR) == HIGH)){
Serial.println("High Voltage");}
if ((digitalRead(PR) == LOW)){
Serial.println("low Voltage");}
unsigned long currentMillis = millis();
if(isAlive){
// HOW TO LOSE
if(cleanliness<=0||enjoyment<=0||hunger<=0)
{
isAlive = false;
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Pochita died cuz");
lcd.setCursor(0, 1);
lcd.print("of themes n such");
digitalWrite(Buzz,LOW);
return;
}
// 1. Check Feed Button
bool currentFeedState = digitalRead(Hbutt);
Serial.println(digitalRead(Hbutt));
if ((digitalRead(Hbutt) == HIGH)){
Serial.println("BP");}
if ((digitalRead(Hbutt) == LOW)){
Serial.println("no food");}
if (currentFeedState == LOW && lastFeedState == HIGH) {
if (hunger < 7) hunger++;
if (statCycle == 0) drawBar("H:", hunger); // Force UI update immediately
delay(50);
}
lastFeedState = currentFeedState;
// 2. Check PIR
bool currentEnjoyment = digitalRead(PR);
if (currentEnjoyment == HIGH && lastEnjoymentState == LOW) {
if (enjoyment < 7) enjoyment++;
if (statCycle == 1) drawBar("E:", enjoyment); // Force UI update immediately
delay(50);
}
lastEnjoymentState = currentEnjoyment;
//3. check ldr
Serial.println(analogRead(ldr));
int currentcleanliness = analogRead(ldr);
if(currentcleanliness<200 && LastCleanState>200){
if(cleanliness<7) cleanliness++;
if(statCycle == 2) drawBar("C:", cleanliness); // forces UI update asap
delay(50);
}
LastCleanState = currentcleanliness;


// 1. ANIMATION LOOP
if (currentMillis - previousAnimMillis >= animInterval) {
previousAnimMillis = currentMillis;
if (animFrame) {
lcd.setCursor(0, 1); lcd.write(3);
lcd.setCursor(1, 1); lcd.write(4);
} else {
lcd.setCursor(0, 1); lcd.write(6);
lcd.setCursor(1, 1); lcd.write(7);
}
lcd.setCursor(2, 1); lcd.write(5);
animFrame = !animFrame;
}


// 2. STAT UI CYCLE LOOP (Rotates strictly every 2 seconds)
if (currentMillis - previousStatMillis >= statInterval) {
previousStatMillis = currentMillis;
statCycle = (statCycle + 1) % 3;
if (statCycle == 0) drawBar("H:", hunger);
else if (statCycle == 1) drawBar("E:", enjoyment);
else if (statCycle == 2) drawBar("C:", cleanliness);
}


// 3. SEPARATE HUNGER DRAIN
if (currentMillis - previousHungerMillis >= hungerDrainInterval) {
previousHungerMillis = currentMillis;
if (hunger > 0) hunger--;
}


// 4. SEPARATE ENJOYMENT DRAIN
if (currentMillis - previousEnjoyMillis >= enjoyDrainInterval) {
previousEnjoyMillis = currentMillis;
if (enjoyment > 0) enjoyment--;
}


// 5. SEPARATE CLEANLINESS DRAIN
if (currentMillis - previousCleanMillis >= cleanDrainInterval) {
previousCleanMillis = currentMillis;
if (cleanliness > 0) cleanliness--;
}
}
//Buzzer warning
if(hunger<3||cleanliness<3||enjoyment<3)
{
digitalWrite(Buzz,HIGH);
}
if(hunger>3&&cleanliness>3&&enjoyment>3)
{
digitalWrite(Buzz,LOW);
}
}

Tamagotchi Death Checker

this code ensures that when the values of cleanliness,hunger or enjoyment have a value of 0 pochita dies. this works by having all the code run underneath the previously mentioned boolean isAlive, when the C,H,E values equal 0 the boolean gets a value of false which stops all code in the void loop

if(isAlive){
// HOW TO LOSE
if(cleanliness<=0||enjoyment<=0||hunger<=0)
{
isAlive = false;
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Pochita died cuz");
lcd.setCursor(0, 1);
lcd.print("of themes n such");
digitalWrite(Buzz,LOW);
//noTone(Buzz);
return;
}

this is also how one would make a death screen for their tamagotchi. mine says "pochita died cuz of themes n such" in order to reference the book he comes from but you change it to be anything you want,

Pushbutton

IMG_2727.jpeg

by far the easiest to add input out of all of them. the push button works by the Arduino uno receiving an electrical input when the button is pressed down. In our code i use the button in order to feed our tamagotchi.

// 1. Check Feed Button
bool currentFeedState = digitalRead(Hbutt);
Serial.println(digitalRead(Hbutt));
if ((digitalRead(Hbutt) == HIGH)){
Serial.println("BP");}
if ((digitalRead(Hbutt) == LOW)){
Serial.println("no food");}
if (currentFeedState == LOW && lastFeedState == HIGH) {
if (hunger < 7) hunger++;
if (statCycle == 0) drawBar("H:", hunger); // Force UI update immediately
//playEatSound(); // Plays the eating crunchy noise
delay(50);
}

PIR Sensor

IMG_2706.jpeg
Screenshot 2026-06-15 at 5.56.10 PM.png
Screenshot 2026-06-15 at 5.55.52 PM.png

The Pir sensor/ motion sensor, When creating your tamagotchi you have to ensure that it has ways to input code so you can interact with your pet. I made it so that whenever you wave your hand in front of the pir sensor it increases your tamagotchi's enjoyment. The way this code works is by checking whether the pir sensor has a reading of movement and then when it reads high it add's to the enjoyment bar.

make sure your pir signal receiver is plugged into a digital pin, in our code we plugged it into pin 13.\

the pir sensor works by checking whether something is moving infront of it, when something does it sends a high input to the arduino it also compares it to the last second to see if your hand is moving to make sure that something is not just placed infront of it.


// 2. Check PIR
bool currentEnjoyment = digitalRead(PR);
if (currentEnjoyment == HIGH && lastEnjoymentState == LOW) {
if (enjoyment < 7) enjoyment++;
if (statCycle == 1) drawBar("E:", enjoyment); // Force UI update immediately
//playBubbleSound();
delay(50);
}
lastEnjoymentState = currentEnjoyment;


Ldr Code

IMG_2728.jpeg
Screenshot 2026-06-15 at 6.26.25 PM.png

The pir sensor works by checking to see the light in your room. because you have to put your ldr into a pwm the arduino can actively read what light the ldr is receiving in this scenario my classroom only emmitted a max light of 500 so i set the ldr to check whether the light 1 second ago was greater than 200 (to indicate that their was an original light) and that the light currently was less than 200( so the arduino can verify that it is receiving less light)

//3. check ldr
Serial.println(analogRead(ldr));
int currentcleanliness = analogRead(ldr);
if(currentcleanliness<200 && LastCleanState>200){
if(cleanliness<7) cleanliness++;
if(statCycle == 2) drawBar("C:", cleanliness); // forces UI update asap
delay(50);
// playBarkSound();
}
LastCleanState = currentcleanliness;

Animation and Stats Loop

this code controls the animation of your tamagotchi by making the bottom part of the character switch from frame 1 and frame 2 from the presetup code

// 1. ANIMATION LOOP
if (currentMillis - previousAnimMillis >= animInterval) {
previousAnimMillis = currentMillis;
if (animFrame) {
lcd.setCursor(0, 1); lcd.write(3);
lcd.setCursor(1, 1); lcd.write(4);
} else {
lcd.setCursor(0, 1); lcd.write(6);
lcd.setCursor(1, 1); lcd.write(7);
}
lcd.setCursor(2, 1); lcd.write(5);
animFrame = !animFrame;
}


The stat ui cycle also works in a very similar way but instead of switching between two frames the code uses a modulus equation. statcycle is a constantly increasing number and what the %3 does at the end of the equation is that it finds the remainder of the equation when the statcycle number is put into 3 groups and depending on the remainder/output it switches the bar being shown

// 2. STAT UI CYCLE LOOP (Rotates strictly every 2 seconds)
if (currentMillis - previousStatMillis >= statInterval) {
previousStatMillis = currentMillis;
statCycle = (statCycle + 1) % 3;
if (statCycle == 0) drawBar("H:", hunger);
else if (statCycle == 1) drawBar("E:", enjoyment);
else if (statCycle == 2) drawBar("C:", cleanliness);
}


full code here:

// 1. ANIMATION LOOP
if (currentMillis - previousAnimMillis >= animInterval) {
previousAnimMillis = currentMillis;
if (animFrame) {
lcd.setCursor(0, 1); lcd.write(3);
lcd.setCursor(1, 1); lcd.write(4);
} else {
lcd.setCursor(0, 1); lcd.write(6);
lcd.setCursor(1, 1); lcd.write(7);
}
lcd.setCursor(2, 1); lcd.write(5);
animFrame = !animFrame;
}


// 2. STAT UI CYCLE LOOP (Rotates strictly every 2 seconds)
if (currentMillis - previousStatMillis >= statInterval) {
previousStatMillis = currentMillis;
statCycle = (statCycle + 1) % 3;
if (statCycle == 0) drawBar("H:", hunger);
else if (statCycle == 1) drawBar("E:", enjoyment);
else if (statCycle == 2) drawBar("C:", cleanliness);
}

Drains and Buzzer

// 3. SEPARATE HUNGER DRAIN
if (currentMillis - previousHungerMillis >= hungerDrainInterval) {
previousHungerMillis = currentMillis;
if (hunger > 0) hunger--;
}


// 4. SEPARATE ENJOYMENT DRAIN
if (currentMillis - previousEnjoyMillis >= enjoyDrainInterval) {
previousEnjoyMillis = currentMillis;
if (enjoyment > 0) enjoyment--;
}


// 5. SEPARATE CLEANLINESS DRAIN
if (currentMillis - previousCleanMillis >= cleanDrainInterval) {
previousCleanMillis = currentMillis;
if (cleanliness > 0) cleanliness--;
}
//Buzzer warning
if(hunger<3||cleanliness<3||enjoyment<3)
{
digitalWrite(Buzz,HIGH);
}
if(hunger>3&&cleanliness>3&&enjoyment>3)
{
digitalWrite(Buzz,LOW);
}

this part of the code makes it so that the values of the tamagotchi's stats drain over the set time by subtracting a timer from the amount of seconds you set the drain too and when it is equal it removes a value of 1 from the stat.

the buzzer works in the same way as the light where you select it to turn on depending on whatever reasoning you want (in this scenario we have set it so that whenever the hunger,cleanliness or enjoyment stat has a value of less than 3 the buzzer makes a noise until all stats are above 3).

Full Code

//pre setup
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27,16,2); // set the LCD address to 0x27 for a 16 chars and 2 line display
// --- Pochita Custom Pieces ---
byte topLeft[] = { B00000, B01111, B01111, B00000, B00000, B00000, B00000, B00000 };
byte topCenter[] = { B00111, B01000, B11111, B11101, B11111, B11111, B11111, B11111 };
byte topRight[] = { B11000, B00100, B00111, B01111, B11111, B11111, B11111, B11111 };
byte botLeftF1[] = { B00011, B01100, B01111, B00111, B00011, B00111, B01001, B00000 };
byte botCenterF1[] = { B11111, B11111, B11111, B11111, B11111, B11111, B01001, B00000 };
byte botRightF1[] = { B11111, B11111, B11111, B11111, B11111, B01110, B00100, B00010 };
byte botLeftF2[] = { B00011, B01100, B01111, B00111, B00011, B00111, B00110, B00000 };
byte botCenterF2[] = { B11111, B11111, B11111, B11111, B11111, B11111, B00110, B00000 };


// --- Tamagotchi Stat Variables (Maxed out at 7 on start) ---
int hunger = 7;
int enjoyment = 7;
int cleanliness = 7;
//pochita is alive variable
bool isAlive = true;


// --- Hardware Pin Definitions ---
int PR = 13;//pir sensor
int Hbutt = 11;
int Buzz = 10;
int ldr = A3;


//States for Debouncing
bool lastFeedState = HIGH;
bool lastEnjoymentState = HIGH;
int LastCleanState = 1023;


//Timer Baselines
unsigned long previousAnimMillis = 0;
unsigned long previousStatMillis = 0;
unsigned long previousHungerMillis = 0;
unsigned long previousEnjoyMillis = 0;
unsigned long previousCleanMillis = 0;


// Drain Rates
const long animInterval = 350; // Animation speed
const long statInterval = 2000; // Display rotation speed (Strictly 2 seconds)
const long hungerDrainInterval = 10000; // Hunger drops every 10 seconds
const long enjoyDrainInterval = 18000; // Enjoyment drops every 18 seconds
const long cleanDrainInterval = 6000; // Cleanliness drops every 6 seconds


bool animFrame = true;
int statCycle = 0;


void drawBar(const char* label, int value) {
lcd.setCursor(5, 1);
lcd.print(label);
lcd.print("[");
for (int i = 0; i < 7; i++) {
if (i < value) {
lcd.print((char)255);
} else {
lcd.print(" ");
}
}
lcd.print("]");
}
// SOUND EFFECT FUNCTIONS


// hunger sound
//void playEatSound() {
// tone(Buzz, 400); delay(60);
// tone(Buzz, 250); delay(80);
// noTone(Buzz); delay(40);
// tone(Buzz, 350); delay(60);
// tone(Buzz, 200); delay(80);
// noTone(Buzz);
//}


// cleaning sound
//void playBubbleSound() {
// for (int i = 0; i < 3; i++) {
// tone(Buzz, 1200); delay(20);
// tone(Buzz, 1600); delay(25);
// noTone(Buzz); delay(40);
// }
//}


// enjoyment sound
//void playBarkSound() {
// First bark
// for (int freq = 700; freq > 250; freq -= 45) {
// tone(Buzz, freq);
// delay(4);
// }
// noTone(Buzz);
// delay(80); // Pause between barks
// Second bark
// for (int freq = 750; freq > 300; freq -= 45) {
// tone(Buzz, freq);
// delay(4);
// }
// noTone(Buzz);
//}




void setup() {
lcd.init();
lcd.backlight();
Serial.begin(9600);
pinMode (PR, INPUT);
pinMode(Hbutt, INPUT);
pinMode(Buzz, OUTPUT);
//pinMode(ldr,INPUT);
lcd.createChar(0, topLeft);
lcd.createChar(1, topCenter);
lcd.createChar(2, topRight);
lcd.createChar(3, botLeftF1);
lcd.createChar(4, botCenterF1);
lcd.createChar(5, botRightF1);
lcd.createChar(6, botLeftF2);
lcd.createChar(7, botCenterF2);


lcd.setCursor(0, 0); lcd.write(0);
lcd.setCursor(1, 0); lcd.write(1);
lcd.setCursor(2, 0); lcd.write(2);


lcd.setCursor(8, 0);
lcd.print("POCHITA");
}


void loop() {
Serial.println(digitalRead(PR));
if ((digitalRead(PR) == HIGH)){
Serial.println("High Voltage");}
if ((digitalRead(PR) == LOW)){
Serial.println("low Voltage");}
unsigned long currentMillis = millis();
if(isAlive){
// HOW TO LOSE
if(cleanliness<=0||enjoyment<=0||hunger<=0)
{
isAlive = false;
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Pochita died cuz");
lcd.setCursor(0, 1);
lcd.print("of themes n such");
digitalWrite(Buzz,LOW);
//noTone(Buzz);
return;
}
// 1. Check Feed Button
bool currentFeedState = digitalRead(Hbutt);
Serial.println(digitalRead(Hbutt));
if ((digitalRead(Hbutt) == HIGH)){
Serial.println("BP");}
if ((digitalRead(Hbutt) == LOW)){
Serial.println("no food");}
if (currentFeedState == LOW && lastFeedState == HIGH) {
if (hunger < 7) hunger++;
if (statCycle == 0) drawBar("H:", hunger); // Force UI update immediately
//playEatSound(); // Plays the eating crunchy noise
delay(50);
}
lastFeedState = currentFeedState;
// 2. Check PIR
bool currentEnjoyment = digitalRead(PR);
if (currentEnjoyment == HIGH && lastEnjoymentState == LOW) {
if (enjoyment < 7) enjoyment++;
if (statCycle == 1) drawBar("E:", enjoyment); // Force UI update immediately
//playBubbleSound();
delay(50);
}
lastEnjoymentState = currentEnjoyment;
//3. check ldr
Serial.println(analogRead(ldr));
int currentcleanliness = analogRead(ldr);
if(currentcleanliness<200 && LastCleanState>200){
if(cleanliness<7) cleanliness++;
if(statCycle == 2) drawBar("C:", cleanliness); // forces UI update asap
delay(50);
// playBarkSound();
}
LastCleanState = currentcleanliness;


// 1. ANIMATION LOOP
if (currentMillis - previousAnimMillis >= animInterval) {
previousAnimMillis = currentMillis;
if (animFrame) {
lcd.setCursor(0, 1); lcd.write(3);
lcd.setCursor(1, 1); lcd.write(4);
} else {
lcd.setCursor(0, 1); lcd.write(6);
lcd.setCursor(1, 1); lcd.write(7);
}
lcd.setCursor(2, 1); lcd.write(5);
animFrame = !animFrame;
}


// 2. STAT UI CYCLE LOOP (Rotates strictly every 2 seconds)
if (currentMillis - previousStatMillis >= statInterval) {
previousStatMillis = currentMillis;
statCycle = (statCycle + 1) % 3;
if (statCycle == 0) drawBar("H:", hunger);
else if (statCycle == 1) drawBar("E:", enjoyment);
else if (statCycle == 2) drawBar("C:", cleanliness);
}


// 3. SEPARATE HUNGER DRAIN
if (currentMillis - previousHungerMillis >= hungerDrainInterval) {
previousHungerMillis = currentMillis;
if (hunger > 0) hunger--;
}


// 4. SEPARATE ENJOYMENT DRAIN
if (currentMillis - previousEnjoyMillis >= enjoyDrainInterval) {
previousEnjoyMillis = currentMillis;
if (enjoyment > 0) enjoyment--;
}


// 5. SEPARATE CLEANLINESS DRAIN
if (currentMillis - previousCleanMillis >= cleanDrainInterval) {
previousCleanMillis = currentMillis;
if (cleanliness > 0) cleanliness--;
}
//Buzzer warning
if(hunger<3||cleanliness<3||enjoyment<3)
{
digitalWrite(Buzz,HIGH);
}
if(hunger>3&&cleanliness>3&&enjoyment>3)
{
digitalWrite(Buzz,LOW);
}
}
}

Creating the Tamagotchi

Screenshot 2026-06-15 at 7.38.51 PM.png

these are the steps i took in order to wire my tamagotchi

Lcd I2c

Screenshot 2026-06-15 at 8.16.28 PM.png
Image_260614_121035.jpeg

Your Lcd I2C screen has 4 inputs that need to be aligned : Gnd,Vcc,Sda,Scl

Your gnd pin has to be connected to ground and the vcc pin needs to be connected to power. both of these pins cannot be placed anywhere else you sda and scl pins location depend on what type of microcontroller you use.

If your using the same microcontroller as me(arduino uno) the sda pin has to go to A4 and the scl pin needs to connect to A5.

PIR Sensor Wiring

Screenshot 2026-06-15 at 8.19.59 PM.png
Screenshot 2026-06-15 at 5.55.52 PM.png

when wiring the pir sensor make sure the pin furthest to the left goes to ground , the middle pin connects to a digital pin on your arduino and the pin furthest to the right connects to power

in our schematic and code we put it in pin 13 but it can go into any digital pin

LDR Wiring

Screenshot 2026-06-15 at 8.25.00 PM.png
Screenshot 2026-06-15 at 8.26.29 PM.png
Screenshot 2026-06-15 at 8.35.17 PM.png

when wiring the ldr make sure that whatever side receiving ground is also connected with the signal to the arduino, you also need to ensure that there is a resistor (preferably 10k ohms) in between ground and the ldr. you also need to confirm that the wire is connected to a pwm pin in our case we used A3

Push Button Wiring

Screenshot 2026-06-15 at 8.31.55 PM.png
Screenshot 2026-06-15 at 8.31.49 PM.png
Screenshot 2026-06-15 at 8.35.27 PM.png

when wiring the push button make sure that whatever side is across the button from ground is connected to a digital, just like the ldr ensure that ground is not directly connected to the button and instead there is a resistor (preferably 10k ohms)

Buzzer Wiring

Screenshot 2026-06-15 at 8.33.54 PM.png
Screenshot 2026-06-15 at 8.33.36 PM.png

when wiring the buzzer ensure that that the positive pin is connected to a digital pin and that the negative pin is connected to ground

End Product

IMG_2729.jpeg

in the end your product should look like this (for the exception of the led being swapped with a buzzer) if you have followed all the steps directly