Animated Robot That Likes Attention (tamagotchi-like)

by alternativeO2 in Circuits > Arduino

408 Views, 2 Favorites, 0 Comments

Animated Robot That Likes Attention (tamagotchi-like)

rn_image_picker_lib_temp_f4a4bf4f-1a9a-443e-8137-e85dbfc24bbb.jpg

At my school, there is a subject where we need to make something react to an action using Arduino. (If This Then That)

So for this project, I wanted to use either an ultrasonic sensor or a motion sensor. I eventually chose the ultrasonic sensor as it was not as sensitive as the motion sensor.

Both of these products were part of a kit I bought. In this kit, I later found an 8×8 LED Matrix screen. I then and there decided I needed to use it for this project.

With these two criteria, I had the idea to make a little robot that would react to you when you petted it. You would pet him by placing your hand within a certain distance from the ultrasonic sensor. And you would then get to see his reaction on the LED screen. However, if you decide to ignore him he will slowly become more and more sad until eventually he will be angry at you. At this point, you will have to pet him three times until he trusts you again and becomes happy.

Supplies

max7219 matrix module-600x600.jpg
ultrasonic-sensor-hc-sr04-front-1080x1080.jpg
Electronic-Axial-Lead-Resistors-Array.png
passive-buzzer.jpg
arduino_uno_smd_rev3_-_overview.jpg
inm-0814_18.jpg
2851721-40.jpg

- LED Matrix 8*8 with MAX7219 Module (my led matrix had a blue board, however, I'm pretty sure you can also use the green one the same way)

- Ultrasonic sensor hc-sr04

- Resistor (i.e. 100 Ω)

- Buzzer

- Arduino Uno

- Around 14 wires

- Breadboard / A soldering iron

Test Your Products!

20230308_132635.jpg

Be sure to test your products. As did I.

Because I used the first two and a half weeks only to find out that the LED matrix from my kit did not work. I used different versions of the code I found, and I made sure all my wires were correct. I even got other components involved. (as seen in the picture) And still, my matrix did not work.

So I bought a new one.

In the meantime, I made sure the others worked fine. I followed this tutorial from Isaac100:

https://projecthub.arduino.cc/Isaac100/7cabe1ec-70a7-4bf8-a239-325b49b53cd4

After I made sure my sensor worked, I started to try out the buzzer. Might as well add sound to the sensor while I wait until my LED matrix arrived.

So I followed this tutorial, making sure I understood the way it worked, so I could later make my own sounds.

https://www.codingkids.nl/arduino-buzzer.html

Once my matrix arrived, I started work on that with this tutorial by Rui S.

https://randomnerdtutorials.com/guide-for-8x8-dot-matrix-max7219-with-arduino-pong-game/

I only used the first half, because I did not need a pong game for my project.


Combining

Circuit.png

After I knew for sure everything worked on its own. I connected everything together.

In the image, you can see how I connected everything, and how you can too. You can change any pins, just be sure to change them also in the code.

Coding

/*  
//  Most of this code is written by Altair T. for a HKU project, ITTT.
//  For a basis these sites have helped:
//      https://www.codingkids.nl/arduino-buzzer.html
//      https://projecthub.arduino.cc/Isaac100/7cabe1ec-70a7-4bf8-a239-325b49b53cd4
//      https://randomnerdtutorials.com/guide-for-8x8-dot-matrix-max7219-with-arduino-pong-game/


//  Pin locations:
//       LED-MATRIX
//         DIN connects to pin 13
//         CLK connects to pin 12
//         CS connects to pin 11
       
//       SENSOR
//         VCC to 5V
//         GND to GND
//         Trig to pin 9
//         Echo to pin 10


        BUZZER
          pin 8
*/


#include "LedControl.h"
#include "binary.h"
LedControl lc = LedControl(13, 12, 11, 1);


//timers
unsigned long delaytime = 100;
int idle_count = 0;
int blink_interval = 20;
int angry_count = 0;
//phases
int n_phase = 50;
int s_phase = 90;
int d_phase = 131;
int a_phase = 170;
//misc
int brightness = 0.2;
int trust_level = 3;
String bot_state = " :)";


// IDLE FACES //
//idle 1
byte id1[8] = {
  B00000000,
  B00000000,
  B00100100,
  B00100100,
  B00000000,
  B01000010,
  B00111100,
  B00000000,
};
//idle 2
byte id2[8] = {
  B00000000,
  B00000000,
  B00000000,
  B00100100,
  B00000000,
  B01000010,
  B00111100,
  B00000000,
};
//idle 3
byte id3[8] = {
  B00000000,
  B00000000,
  B00000000,
  B00000000,
  B00000000,
  B01000010,
  B00111100,
  B00000000,
};


// NEUTRAL FACES //
//neutral 1
byte neuf1[8] = {
  B00000000,
  B00000000,
  B00100100,
  B00100100,
  B00000000,
  B00000000,
  B01111110,
  B00000000,
};
//neutral 2
byte neuf2[8] = {
  B00000000,
  B00000000,
  B00000000,
  B00100100,
  B00000000,
  B00000000,
  B01111110,
  B00000000,
};
//neutral 3
byte neuf3[8] = {
  B00000000,
  B00000000,
  B00000000,
  B00000000,
  B00000000,
  B00000000,
  B01111110,
  B00000000,
};


// SAD FACES //
//sad 1
byte sadf1[8] = {
  B00000000,
  B00000000,
  B00100100,
  B00100100,
  B00000000,
  B00111100,
  B01000010,
  B00000000,
};
//sad 2
byte sadf2[8] = {
  B00000000,
  B00000000,
  B00000000,
  B00100100,
  B00000000,
  B00111100,
  B01000010,
  B00000000,
};
//sad 3
byte sadf3[8] = {
  B00000000,
  B00000000,
  B00000000,
  B00000000,
  B00000000,
  B00111100,
  B01000010,
  B00000000,
};


// DESPERATE FACES //
//desperate look 1
byte des1[8] = {
  B00000000,
  B00000000,
  B01001000,
  B01001000,
  B00000000,
  B00111100,
  B01000010,
  B00000000,
};
//desperate look 2
byte des2[8] = {
  B00000000,
  B00000000,
  B10010000,
  B10010000,
  B00000000,
  B01111000,
  B10000100,
  B00000000,
};
//desperate look 3
byte des3[8] = {
  B00000000,
  B00000000,
  B00001001,
  B00001001,
  B00000000,
  B00011110,
  B00100001,
  B00000000,
};
//desperate look 4
byte des4[8] = {
  B00000000,
  B00000000,
  B00010010,
  B00010010,
  B00000000,
  B00111100,
  B01000010,
  B00000000,
};


// ANGRY FACES //
//angry look 1
byte angf1[8] = {
  B00000000,
  B00000000,
  B01000010,
  B01100110,
  B00000000,
  B00111100,
  B01000010,
  B00000000,
};


// HAPPY FACE //
//happy 1
byte hpf1[8] = {
  B00000000,
  B00100100,
  B00100100,
  B00000000,
  B01000010,
  B00111100,
  B00000000,
  B00000000,
};
//happy 2
byte hpf2[8] = {
  B00100100,
  B00100100,
  B00000000,
  B01000010,
  B00111100,
  B00000000,
  B00000000,
  B00000000,
};


// BUZZER //
int buzzerPin = 8;


int f_5 = 50;
int f1 = 100;
int f2 = 200;
int f3 = 300;
int f4 = 400;
int f5 = 500;
int f6 = 600;
int f7 = 700;
int f8 = 800;
int f9 = 900;
int f10 = 1000;


// ULTRA SENSOR //
const int trigPin = 9;
const int echoPin = 10;


float duration, distance;


bool cooldown = false;
float cooldownTimer = 0;


void setup() {
  // SENSOR v
  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);
  Serial.begin(9600);


  // BUZZER v
  pinMode(buzzerPin, OUTPUT);


  // LED-MATRIX //
  lc.shutdown(0, false);
  lc.setIntensity(0, brightness);
  // Clear the display
  lc.clearDisplay(0);
}


void loop() {
  // SENSOR
  if (cooldown == false) {
    digitalWrite(trigPin, LOW);
    delayMicroseconds(2);
    digitalWrite(trigPin, HIGH);
    delayMicroseconds(10);
    digitalWrite(trigPin, LOW);
 
  // Serial Monitor Info
    duration = pulseIn(echoPin, HIGH);
    distance = (duration * .0343) / 2;
    Serial.print("State: ");
    Serial.print(bot_state);
    Serial.print(" | ");
    Serial.print("Trust lvl ");
    Serial.print(trust_level);
    Serial.print(" | ");
    Serial.print("Distance: ");
    Serial.println(distance);
    delay(200);


  // The progression from idle > neutral > sad > angry
    angry_count++;
    if ((angry_count > n_phase) && (angry_count < s_phase)) {
      neutralframe();
      bot_state = " :/";
    } else if ((angry_count == n_phase) && (angry_count < s_phase)) {
      downnoise();
    }
    if (angry_count > s_phase) {
      sadframe();
      bot_state = " :(";
    } else if ((angry_count == s_phase) && (angry_count < a_phase)) {
      downnoise();
    }
    if (angry_count > a_phase) {
      angryframe();
      bot_state = ">:[";
      tone(buzzerPin, f1, 50);
    } else if (angry_count == a_phase) {
      downnoise();
      trust_level = 0;
    }
    if (angry_count < n_phase) {
      idleframe();
      bot_state = " :)";
    }

// The counters for eacht phase so the robot blinks
    idle_count++;
    if (idle_count >= blink_interval) {
      if (angry_count < n_phase) {
        idleanimation();
      } else if ((angry_count >= n_phase) && (angry_count < s_phase)) {
        neutralanimation();
      } else if ((angry_count >= s_phase) && (angry_count < d_phase)) {
        sadanimation();
      } else if ((angry_count >= d_phase) && (angry_count < a_phase)) {
        desperateanimation();
        sadnoise();
      }
      idle_count = 0;
    }

// A check for the robot to see if it trusts you enough
    if (distance <= 10) {
      Serial.println("TRIGGERED");
      if (trust_level >= 2) {
        activated();
        cooldown = true;
      }
      if (trust_level < 2) {
        trust_level = trust_level + 1;
        denynoise();
      }
    }
  }

// Cooldown so the sensor won't be constantly triggered
  if (cooldown == true) {
    cooldownTimer = cooldownTimer + .1;
    Serial.print(cooldownTimer);


    if (cooldownTimer >= 30) {
      cooldown = false;
      cooldownTimer = 0;
    }
  }
}

// Happy Sound && Happy Animation
void activated() {
  angry_count = 0;
  happyframe();
  tone(buzzerPin, f1, 100);
  delay(50);
  tone(buzzerPin, f2, 200);
  delay(100);
  tone(buzzerPin, f3, 200);
  delay(100);
  tone(buzzerPin, f5, 250);
  highhappyframe();
  tone(buzzerPin, f8, 250 * 2);
  delay(150);
  delay(250);
  happyframe();
}
// Sad Noise
void sadnoise() {
  tone(buzzerPin, f1, 600);
  delay(500);
  tone(buzzerPin, f2, 300);
  delay(100);
  tone(buzzerPin, f1 + f_5, 300);
  delay(200);
  tone(buzzerPin, f1, 550);
}
// Mode Down Noise
void downnoise() {
  tone(buzzerPin, f3, 300);
  delay(100);
  tone(buzzerPin, f2, 300);
  delay(100);
  tone(buzzerPin, f1, 300);
}
// Deny Noise
void denynoise() {
  tone(buzzerPin, 300, 300);
  delay(300);
  tone(buzzerPin, 150, 230);
  delay(200);
  tone(buzzerPin, 100, 370);
  delay(350);
}

// this is what makes the LED Matrix show the frames //
// LED MATRIX FRAMES //
// Idle Frame
void idleframe() {
  // Display idle 1
  lc.setRow(0, 0, id1[0]);
  lc.setRow(0, 1, id1[1]);
  lc.setRow(0, 2, id1[2]);
  lc.setRow(0, 3, id1[3]);
  lc.setRow(0, 4, id1[4]);
  lc.setRow(0, 5, id1[5]);
  lc.setRow(0, 6, id1[6]);
  lc.setRow(0, 7, id1[7]);
  delay(delaytime);
}
// Neutral Frame
void neutralframe() {
  // Display neutral face
  lc.setRow(0, 0, neuf1[0]);
  lc.setRow(0, 1, neuf1[1]);
  lc.setRow(0, 2, neuf1[2]);
  lc.setRow(0, 3, neuf1[3]);
  lc.setRow(0, 4, neuf1[4]);
  lc.setRow(0, 5, neuf1[5]);
  lc.setRow(0, 6, neuf1[6]);
  lc.setRow(0, 7, neuf1[7]);
  delay(delaytime);
}
// Sad Frame
void sadframe() {
  // Display sad face
  lc.setRow(0, 0, sadf1[0]);
  lc.setRow(0, 1, sadf1[1]);
  lc.setRow(0, 2, sadf1[2]);
  lc.setRow(0, 3, sadf1[3]);
  lc.setRow(0, 4, sadf1[4]);
  lc.setRow(0, 5, sadf1[5]);
  lc.setRow(0, 6, sadf1[6]);
  lc.setRow(0, 7, sadf1[7]);
  delay(delaytime);
}
// Angry Frame
void angryframe() {
  // Display angry face
  lc.setRow(0, 0, angf1[0]);
  lc.setRow(0, 1, angf1[1]);
  lc.setRow(0, 2, angf1[2]);
  lc.setRow(0, 3, angf1[3]);
  lc.setRow(0, 4, angf1[4]);
  lc.setRow(0, 5, angf1[5]);
  lc.setRow(0, 6, angf1[6]);
  lc.setRow(0, 7, angf1[7]);
  delay(delaytime);
}
// Happy Frame
void happyframe() {
  // Display happy face
  lc.setRow(0, 0, hpf1[0]);
  lc.setRow(0, 1, hpf1[1]);
  lc.setRow(0, 2, hpf1[2]);
  lc.setRow(0, 3, hpf1[3]);
  lc.setRow(0, 4, hpf1[4]);
  lc.setRow(0, 5, hpf1[5]);
  lc.setRow(0, 6, hpf1[6]);
  lc.setRow(0, 7, hpf1[7]);
  delay(delaytime);
}
// Happy Frame High
void highhappyframe() {
  // Display high happy face
  lc.setRow(0, 0, hpf2[0]);
  lc.setRow(0, 1, hpf2[1]);
  lc.setRow(0, 2, hpf2[2]);
  lc.setRow(0, 3, hpf2[3]);
  lc.setRow(0, 4, hpf2[4]);
  lc.setRow(0, 5, hpf2[5]);
  lc.setRow(0, 6, hpf2[6]);
  lc.setRow(0, 7, hpf2[7]);
  delay(delaytime);
}


// BLINK ANIMATIONS //
// Normal Blink Animation
void idleanimation() {
  // Display idle 1
  lc.setRow(0, 0, id1[0]);
  lc.setRow(0, 1, id1[1]);
  lc.setRow(0, 2, id1[2]);
  lc.setRow(0, 3, id1[3]);
  lc.setRow(0, 4, id1[4]);
  lc.setRow(0, 5, id1[5]);
  lc.setRow(0, 6, id1[6]);
  lc.setRow(0, 7, id1[7]);
  delay(delaytime);
  // Display idle 2
  lc.setRow(0, 0, id2[0]);
  lc.setRow(0, 1, id2[1]);
  lc.setRow(0, 2, id2[2]);
  lc.setRow(0, 3, id2[3]);
  lc.setRow(0, 4, id2[4]);
  lc.setRow(0, 5, id2[5]);
  lc.setRow(0, 6, id2[6]);
  lc.setRow(0, 7, id2[7]);
  delay(delaytime);
  // Display idle 3
  lc.setRow(0, 0, id3[0]);
  lc.setRow(0, 1, id3[1]);
  lc.setRow(0, 2, id3[2]);
  lc.setRow(0, 3, id3[3]);
  lc.setRow(0, 4, id3[4]);
  lc.setRow(0, 5, id3[5]);
  lc.setRow(0, 6, id3[6]);
  lc.setRow(0, 7, id3[7]);
  delay(delaytime);
}
//Neutral Blink Animation
void neutralanimation() {
  // Display neutral 1
  lc.setRow(0, 0, neuf1[0]);
  lc.setRow(0, 1, neuf1[1]);
  lc.setRow(0, 2, neuf1[2]);
  lc.setRow(0, 3, neuf1[3]);
  lc.setRow(0, 4, neuf1[4]);
  lc.setRow(0, 5, neuf1[5]);
  lc.setRow(0, 6, neuf1[6]);
  lc.setRow(0, 7, neuf1[7]);
  delay(delaytime);
  // Display neutral 2
  lc.setRow(0, 0, neuf2[0]);
  lc.setRow(0, 1, neuf2[1]);
  lc.setRow(0, 2, neuf2[2]);
  lc.setRow(0, 3, neuf2[3]);
  lc.setRow(0, 4, neuf2[4]);
  lc.setRow(0, 5, neuf2[5]);
  lc.setRow(0, 6, neuf2[6]);
  lc.setRow(0, 7, neuf2[7]);
  delay(delaytime);
  // Display neutral 3
  lc.setRow(0, 0, neuf3[0]);
  lc.setRow(0, 1, neuf3[1]);
  lc.setRow(0, 2, neuf3[2]);
  lc.setRow(0, 3, neuf3[4]);
  lc.setRow(0, 5, neuf3[5]);
  lc.setRow(0, 6, neuf3[6]);
  lc.setRow(0, 7, neuf3[7]);
  delay(delaytime);
}
//Sad Blink Animation
void sadanimation() {
  // Display sad 1
  lc.setRow(0, 0, sadf1[0]);
  lc.setRow(0, 1, sadf1[1]);
  lc.setRow(0, 2, sadf1[2]);
  lc.setRow(0, 3, sadf1[3]);
  lc.setRow(0, 4, sadf1[4]);
  lc.setRow(0, 5, sadf1[5]);
  lc.setRow(0, 6, sadf1[6]);
  lc.setRow(0, 7, sadf1[7]);
  delay(delaytime);
  // Display sad 2
  lc.setRow(0, 0, sadf2[0]);
  lc.setRow(0, 1, sadf2[1]);
  lc.setRow(0, 2, sadf2[2]);
  lc.setRow(0, 3, sadf2[3]);
  lc.setRow(0, 4, sadf2[4]);
  lc.setRow(0, 5, sadf2[5]);
  lc.setRow(0, 6, sadf2[6]);
  lc.setRow(0, 7, sadf2[7]);
  delay(delaytime);
  // Display sad 3
  lc.setRow(0, 0, sadf3[0]);
  lc.setRow(0, 1, sadf3[1]);
  lc.setRow(0, 2, sadf3[2]);
  lc.setRow(0, 3, sadf3[4]);
  lc.setRow(0, 5, sadf3[5]);
  lc.setRow(0, 6, sadf3[6]);
  lc.setRow(0, 7, sadf3[7]);
  delay(delaytime);
}
//Desperate Look Animation
void desperateanimation() {
  // Display desperate 1
  lc.setRow(0, 0, des1[0]);
  lc.setRow(0, 1, des1[1]);
  lc.setRow(0, 2, des1[2]);
  lc.setRow(0, 3, des1[3]);
  lc.setRow(0, 4, des1[4]);
  lc.setRow(0, 5, des1[5]);
  lc.setRow(0, 6, des1[6]);
  lc.setRow(0, 7, des1[7]);
  delay(delaytime);
  // Display desperate 2
  lc.setRow(0, 0, des2[0]);
  lc.setRow(0, 1, des2[1]);
  lc.setRow(0, 2, des2[2]);
  lc.setRow(0, 3, des2[3]);
  lc.setRow(0, 4, des2[4]);
  lc.setRow(0, 5, des2[5]);
  lc.setRow(0, 6, des2[6]);
  lc.setRow(0, 7, des2[7]);
  delay(delaytime * 4);
  // Display sad 1
  lc.setRow(0, 0, sadf1[0]);
  lc.setRow(0, 1, sadf1[1]);
  lc.setRow(0, 2, sadf1[2]);
  lc.setRow(0, 3, sadf1[3]);
  lc.setRow(0, 4, sadf1[4]);
  lc.setRow(0, 5, sadf1[5]);
  lc.setRow(0, 6, sadf1[6]);
  lc.setRow(0, 7, sadf1[7]);
  delay(delaytime);
  // Display desperate 3
  lc.setRow(0, 0, des3[0]);
  lc.setRow(0, 1, des3[1]);
  lc.setRow(0, 2, des3[2]);
  lc.setRow(0, 3, des3[3]);
  lc.setRow(0, 4, des3[4]);
  lc.setRow(0, 5, des3[5]);
  lc.setRow(0, 6, des3[6]);
  lc.setRow(0, 7, des3[7]);
  delay(delaytime * 4);
  // Display desperate 4
  lc.setRow(0, 0, des4[0]);
  lc.setRow(0, 1, des4[1]);
  lc.setRow(0, 2, des4[2]);
  lc.setRow(0, 3, des4[3]);
  lc.setRow(0, 4, des4[4]);
  lc.setRow(0, 5, des4[5]);
  lc.setRow(0, 6, des4[6]);
  lc.setRow(0, 7, des4[7]);
  delay(delaytime);
  // Display sad 1
  lc.setRow(0, 0, sadf1[0]);
  lc.setRow(0, 1, sadf1[1]);
  lc.setRow(0, 2, sadf1[2]);
  lc.setRow(0, 3, sadf1[3]);
  lc.setRow(0, 4, sadf1[4]);
  lc.setRow(0, 5, sadf1[5]);
  lc.setRow(0, 6, sadf1[6]);
  lc.setRow(0, 7, sadf1[7]);
  delay(delaytime);
}

This is the code I used for my project. Which is constructed mostly by me. I did use the earlier mentioned tutorials as a base to start from and understand how each component worked.


To clear up some confusion you might have:

n_phase = neutral phase

s_phase = sad phase

d_phase = desperate phase

a_phase = angry phase


If something else is not clear be sure to ask!

Housing

Screenshot 2023-05-18 224800.png

Be sure to make a little house/ body for your robot, as they'll very much like that.

I designed mine in Adobe Illustrator and used a laser cutter to cut it into wood.

Sadly, however, while cutting it went wrong, and I had to start over. This made me lose a lot of time and I had to scrap the engravings I had planned.

Assembling & Naming

FDUKNH8LHRPRN4D.jpg

After you have done everything, you can assemble your robot and give it a name.

I used masking tape to hold him together, as I did not have strong enough glue at home.


So I gave him a name:

TM-0

pronounced, Teemo

In Conclusion

I've very much enjoyed making TM-0 and using the component I wanted to use. I am definitely very satisfied with my final result and hope I'll be using Arduino in the future again.

I have learned a bit more from the coding, and using Arduino overall. Not only that, but I also learned how to solder.


I hope you have a great rest of your day and much luck with your robot!