Pavlovs Cat with Arduino IoT Bundle

Time to get Pavlovian on your cat!

Components and Supplies

Apps and Online Services

About This Project

Train your cat with the help of the Arduino Cloud!

If you ever tried to train a cat, you know how hard it is. Cats are their own masters, but now you have the chance to let the cats do your bidding using this IoT-enabled device.

Welcome to Pavlov's Cat Experiment!

In this project, you will learn how to teach your cat when it is (and isn't) dinner time using nothing but the components in the IoT Bundle and some cardboard.

And we all know that cats already love cardboard boxes! Every time the cat hears a certain melody, it receives food. A light sensor detects the presence of a cat.

Another melody does nothing. See how this will work? You will be able to monitor your cat's progression over time and set the food dispensing rate from your phone.

Disclaimer: No cats were hurt in the development of this experiment. Also, no guarantee that the cat will eat the food, but you get the picture, right?

In a Nutshell

You will be able to build your own food dispenser by following these simple step-by-step instructions. The dispenser is basically just some cardboard and a servo motor with some added Arduino magic.

Using the Arduino Cloud Dashboard, you can set the amount of food to be dispensed and trigger the melodies played with the buzzer.

A light sensor is used to detect if the cat reacted to the melody and got to the food.

Get the cardboard blueprint for this project here!

Components

  • Servo moto
  • Phototransistor
  • 220 ohm resistor
  • Buzzer

Arduino IoT Bundle

Learning Goals

In this experiment you will learn how to:

  • Introducing the Arduino Cloud
  • Introducing the Arduino IoT Remote app
  • Play a melody using your Arduino
  • Include additional tabs in your code
  • Creating an Arduino Cloud Dashboard

Want to Know More?

This tutorial is part of a series of experiments that familiarize you with the Arduino RP2040 and IoT. All experiments can be built using the components contained in the IoT Bundle.

Setting up the Arduino Cloud

If you are new to the Arduino Cloud, check out our Getting Started Guide.

Template

To connect your board to the Arduino Cloud, we will use the Pavlov's Cat Template. This template installs a specific sketch on your board and creates a dashboard that allows you to interact with your board: you don't need to write any code at all!

See the image below to understand how to set it up.

Thing overview
Thing overview

We will start by setting up the Arduino Cloud by following the steps below:

  • Login to your Arduino Create account
  • Creating a Thing
  • Attaching a Device
  • Adding Variables
  • Adding Network credentials

Arduino IoT Bundle

Variables

We wills start by adding four variables:

Arduino IoT Bundle
Arduino IoT Bundle

Setup Hardware & Sketch

Play the Song

The next section explains how to play a melody using a piezo buzzer and your Arduino.

Arduino IoT Bundle
Arduino IoT Bundle

Connect the buzzer to digital pin 7, as shown in the picture above. Now, navigate into the Arduino Web Editor through Thing > Sketch tab > open full editor. This will open up our automatically generated sketch in the full Arduino Web Editor. Next we need to add an extra tab containing all the necessary notes to play the song. Click the arrow on the right side to add a new tab called

"pitches.h"
. See the GIF below for more detailed steps:

Arduino IoT Bundle

Paste the following code into

"pitches.h"
.

1#define NOTE_B0 31
2#define NOTE_C1 33
3#define NOTE_CS1 35
4#define NOTE_D1 37
5#define NOTE_DS1 39
6#define NOTE_E1 41
7#define NOTE_F1 44
8#define NOTE_FS1 46
9#define NOTE_G1 49
10#define NOTE_GS1 52
11#define NOTE_A1 55
12#define NOTE_AS1 58
13#define NOTE_B1 62
14#define NOTE_C2 65
15#define NOTE_CS2 69
16#define NOTE_D2 73
17#define NOTE_DS2 78
18#define NOTE_E2 82
19#define NOTE_F2 87
20#define NOTE_FS2 93
21#define NOTE_G2 98
22#define NOTE_GS2 104
23#define NOTE_A2 110
24#define NOTE_AS2 117
25#define NOTE_B2 123
26#define NOTE_C3 131
27#define NOTE_CS3 139
28#define NOTE_D3 147
29#define NOTE_DS3 156
30#define NOTE_E3 165
31#define NOTE_F3 175
32#define NOTE_FS3 185
33#define NOTE_G3 196
34#define NOTE_GS3 208
35#define NOTE_A3 220
36#define NOTE_AS3 233
37#define NOTE_B3 247
38#define NOTE_C4 262
39#define NOTE_CS4 277
40#define NOTE_D4 294
41#define NOTE_DS4 311
42#define NOTE_E4 330
43#define NOTE_F4 349
44#define NOTE_FS4 370
45#define NOTE_G4 392
46#define NOTE_GS4 415
47#define NOTE_A4 440
48#define NOTE_AS4 466
49#define NOTE_B4 494
50#define NOTE_C5 523
51#define NOTE_CS5 554
52#define NOTE_D5 587
53#define NOTE_DS5 622
54#define NOTE_E5 659
55#define NOTE_F5 698
56#define NOTE_FS5 740
57#define NOTE_G5 784
58#define NOTE_GS5 831
59#define NOTE_A5 880
60#define NOTE_AS5 932
61#define NOTE_B5 988
62#define NOTE_C6 1047
63#define NOTE_CS6 1109
64#define NOTE_D6 1175
65#define NOTE_DS6 1245
66#define NOTE_E6 1319
67#define NOTE_F6 1397
68#define NOTE_FS6 1480
69#define NOTE_G6 1568
70#define NOTE_GS6 1661
71#define NOTE_A6 1760
72#define NOTE_AS6 1865
73#define NOTE_B6 1976
74#define NOTE_C7 2093
75#define NOTE_CS7 2217
76#define NOTE_D7 2349
77#define NOTE_DS7 2489
78#define NOTE_E7 2637
79#define NOTE_F7 2794
80#define NOTE_FS7 2960
81#define NOTE_G7 3136
82#define NOTE_GS7 3322
83#define NOTE_A7 3520
84#define NOTE_AS7 3729
85#define NOTE_B7 3951
86#define NOTE_C8 4186
87#define NOTE_CS8 4435
88#define NOTE_D8 4699
89#define NOTE_DS8 4978
90/* notes in the melody */
91int melodyOne[] = {
92NOTE_C4, NOTE_G3, NOTE_G3, NOTE_A3, NOTE_G3, 0, NOTE_B3, NOTE_C4
93};
94/* note durations: 4 = quarter note, 8 = eighth note, etc. */
95int noteDurationsOne[] = {
964, 8, 8, 4, 4, 4, 4, 4
97};
98int melodyTwo[] = {
99NOTE_C5, NOTE_C5, NOTE_A4, NOTE_C5, 0, NOTE_G4, NOTE_C5, NOTE_F5, NOTE_E5, NOTE_C5, NOTE_C5
100};
101/* note durations: 4 = quarter note, 8 = eighth note, etc. */
102int noteDurationsTwo[] = {
1032, 2, 4, 4, 8, 4, 4, 4, 4, 4 , 4
104};

Now, we need to go back to our main sketch to play the melody. In order to use the notes we need to add

#include "pitches.h"
at the top of our code.

Inside setup() we need to add our buzzerPin as Output. At the bottom of the code we will add a function called playMelody() with three parameters: melody,durations and numberOfNotes.

To test our melody we can call the playMelody function inside loop() with our parameters in place.

1void loop() {
2 playMelody(melodyOne, noteDurationsOne, 8);
3 delay(5000);
4}

Copy the code below to test the melody

1#include "thingProperties.h"
2#include "pitches.h"
3int buzzerPin = 7;
4void setup() {
5 /* Initialize serial and wait for port to open: */
6 Serial.begin(9600);
7 /* This delay gives the chance to wait for a Serial Monitor without blocking if none is found */
8 delay(1500);
9 pinMode(buzzerPin, OUTPUT);
10 /* Defined in thingProperties.h */
11 initProperties();
12 /* Connect to Arduino Cloud */
13 ArduinoCloud.begin(ArduinoIoTPreferredConnection);
14
15/*
16The following function allows you to obtain more information
17related to the state of network and IoT Cloud connection and errors
18the higher number the more granular information you’ll get.
19The default is 0 (only errors).
20Maximum is 4
21*/
22setDebugMessageLevel(2);
23ArduinoCloud.printDebugInfo();
24}
25void loop() {
26 ArduinoCloud.update();
27 playMelody(melodyOne, noteDurationsOne, 8);
28 delay(5000);
29}
30void playMelody(int melody[], int noteDurations[], int numberOfNotes ) {
31 Serial.println("Playing melody");
32 for (int thisNote = 0; thisNote < numberOfNotes; thisNote++) {
33 /* to calculate the note duration, take one second divided by the note type.
34 e.g. quarter note = 1000 / 4, eighth note = 1000/8, etc. */
35 int noteDuration = 1000 / noteDurations[thisNote];
36 tone(buzzerPin, melody[thisNote], noteDuration);
37 /* to distinguish the notes, set a minimum time between them.
38 the note's duration + 30% seems to work well */
39 int pauseBetweenNotes = noteDuration * 1.30;
40 delay(pauseBetweenNotes);
41 /* stop the tone playing */
42 noTone(buzzerPin);
43 }
44}
45/*
46Since Notification is READ_WRITE variable, onNotificationChange() is
47executed every time a new value is received from IoT Cloud.
48*/
49void onNotificationChange() {
50/* Add your code here to act upon Notification change */
51}
52/*
53Since SendData is READ_WRITE variable, onSendDataChange() is
54executed every time a new value is received from IoT Cloud.
55*/
56void onSendDataChange() {
57/* Add your code here to act upon SendData change */
58}
59/*
60Since Portion is READ_WRITE variable, onPortionChange() is
61executed every time a new value is received from IoT Cloud.
62*/
63void onPortionChange() {
64/* Add your code here to act upon Portion change */
65}
66/*
67Since Melody is READ_WRITE variable, onMelodyChange() is
68executed every time a new value is received from IoT Cloud.
69*/
70void onMelodyChange() {
71/* Add your code here to act upon Melody change */
72}

Detect the Cat!

In order to detect the presence of the cat we will use a phototransistor, which is able to measure the light intensity and therefore if someone has passed close to it. Connect the sensor to the A0 pin as shown below.

Arduino IoT Bundle
Arduino IoT Bundle

Note that we used a 220 ohm resistor.

To read values from the sensor we will only need to assign a variable to analogRead(A0). Since we are interested in detecting the cat presence only after the melody, and just for a certain amount of time we can use the following logic:

1#include "thingProperties.h"
2#include "pitches.h"
3int buzzerPin = 7;
4unsigned long timer;
5bool startDetecting = true;
6int threshold = 200;
7void setup() {
8 /* Initialize serial and wait for port to open: */
9 Serial.begin(9600);
10 /* This delay gives the chance to wait for a Serial Monitor without blocking if none is found */
11 delay(1500);
12
13 pinMode(buzzerPin, OUTPUT);
14 timer = millis();
15
16 /* Defined in thingProperties.h */
17 initProperties();
18
19 /* Connect to Arduino Cloud */
20 ArduinoCloud.begin(ArduinoIoTPreferredConnection);
21/*
22The following function allows you to obtain more information
23related to the state of network and IoT Cloud connection and errors
24the higher number the more granular information you’ll get.
25The default is 0 (only errors).
26Maximum is 4
27*/
28setDebugMessageLevel(2);
29ArduinoCloud.printDebugInfo();
30}
31void loop() {
32 ArduinoCloud.update();
33
34 if (startDetecting) {
35 int value = analogRead(A0);
36 if (value < threshold) {
37 Serial.println("cat detected!");
38 startDetecting = false;
39 } else if (millis() - timer < 12000) {
40 Serial.println("no cat detected in the past two minutes");
41 startDetecting = false;
42 }
43 }
44}
45void playMelody(int melody[], int noteDurations[], int numberOfNotes ) {
46 Serial.println("Playing melody");
47 for (int thisNote = 0; thisNote < numberOfNotes; thisNote++) {
48 /* to calculate the note duration, take one second divided by the note type.
49 e.g. quarter note = 1000 / 4, eighth note = 1000/8, etc. */
50 int noteDuration = 1000 / noteDurations[thisNote];
51 tone(buzzerPin, melody[thisNote], noteDuration);
52 /* to distinguish the notes, set a minimum time between them.
53 the note's duration + 30% seems to work well */
54 int pauseBetweenNotes = noteDuration * 1.30;
55 delay(pauseBetweenNotes);
56 /* stop the tone playing */
57 noTone(buzzerPin);
58 }
59}
60/*
61Since Notification is READ_WRITE variable, onNotificationChange() is
62executed every time a new value is received from IoT Cloud.
63*/
64void onNotificationChange() {
65/* Add your code here to act upon Notification change */
66}
67/*
68Since SendData is READ_WRITE variable, onSendDataChange() is
69executed every time a new value is received from IoT Cloud.
70*/
71void onSendDataChange() {
72/* Add your code here to act upon SendData change */
73}
74/*
75Since Portion is READ_WRITE variable, onPortionChange() is
76executed every time a new value is received from IoT Cloud.
77*/
78void onPortionChange() {
79/* Add your code here to act upon Portion change */
80}
81/*
82Since Melody is READ_WRITE variable, onMelodyChange() is
83executed every time a new value is received from IoT Cloud.
84*/
85void onMelodyChange() {
86/* Add your code here to act upon Melody change */
87}

Note that we use themillis()function to set a timer.

millis()
gives us the time in milliseconds since the board was up and running. We can use it to set timers and trigger events after a certain amount of time.

We also use a threshold to determine if the cat was detected. That threshold is arbitrary, you can set it according to your light condition.

Add the Servo Motor

Note: for the servo motor you will need a 9V battery which is not included in the IoT Bundle! Alternatively you can use another external power supply such as a phone charger with open ended cables.

The servo is used to open the box and deliver food. Note that we will use the portion variable to set the amount of time the servo has to remain turned 90 degrees. We will be able to change the portion value through the Arduino Cloud Dashboard.

Arduino IoT Bundle
Arduino IoT Bundle

Attach the servo to pin 6 as shown above. To control the will make us of the servo library by adding

#include "servo.h"
at the top. We also need to initialize a variable setting the starting position of the servo by adding
Servo myservo
under our libraries and inside
setup()
add
myservo.attach(6)
which tells the program which pin we are using.

Inside the loop() function we also need to change some things as we put the project together. We only want to dispense food if the portion size is set and the melody has been played. We also want to start detecting if a cat is approaching and log the time it needed to arrive to the serial port.

Finally, we can add a function below

playMelody()
called
moveServo()
.

1#include "thingProperties.h"
2#include "pitches.h"
3#include "Servo.h"
4Servo myservo;
5int pos = 0;
6int buzzerPin = 7;
7unsigned long timer;
8bool startDetecting = true;
9int threshold = 200;
10void setup() {
11 /* Initialize serial and wait for port to open: */
12 Serial.begin(9600);
13 /* This delay gives the chance to wait for a Serial Monitor without blocking if none is found */
14 delay(1500);
15
16 pinMode(buzzerPin, OUTPUT);
17 timer = millis();
18
19 /* Defined in thingProperties.h */
20 initProperties();
21
22 /* Connect to Arduino Cloud */
23 ArduinoCloud.begin(ArduinoIoTPreferredConnection);
24/*
25The following function allows you to obtain more information
26related to the state of network and IoT Cloud connection and errors
27the higher number the more granular information you’ll get.
28The default is 0 (only errors).
29Maximum is 4
30*/
31setDebugMessageLevel(2);
32ArduinoCloud.printDebugInfo();
33}
34void loop() {
35 ArduinoCloud.update();
36 if (sendData) { /* Checks if there are some updates */
37 if (melody) {
38 if (portion > 0) { /* if Melody AND food */
39 notification = "Dispensing " + String(portion) + " portion of food right now"; /* notification of dispensed food */
40 playMelody(melodyOne, noteDurationsOne, 8);
41 moveServo();
42 startDetecting = true;
43 timer = millis();
44 } else if (portion == 0) { /* if Melody and NO food */
45 notification = "At your command";
46 playMelody(melodyTwo, noteDurationsTwo, 11);
47 startDetecting = true;
48 timer = millis();
49 }
50 } else if (!melody) {
51 notification = "Hello! \n Please turn the melody ON to train your cat";
52 }
53}
54if (startDetecting) {
55 int value = analogRead(A0);
56 if (value < 200) {
57 String TimeValue = String((millis() - timer) / 1000);
58 notification = "Cat detected! \nTime to reach the feeder: " + TimeValue + " seconds";
59 startDetecting = false;
60 } else if (millis() - timer > 120000) {
61 notification= "No cat detected in the past two minutes";
62 startDetecting = false;
63 }
64}
65delay(1000);
66}
67void playMelody(int melody[], int noteDurations[], int numberOfNotes ) {
68 Serial.println("Playing melody");
69 for (int thisNote = 0; thisNote < numberOfNotes; thisNote++) {
70 /* to calculate the note duration, take one second divided by the note type.
71 e.g. quarter note = 1000 / 4, eighth note = 1000/8, etc. */
72 int noteDuration = 1000 / noteDurations[thisNote];
73 tone(buzzerPin, melody[thisNote], noteDuration);
74 /* to distinguish the notes, set a minimum time between them.
75 the note's duration + 30% seems to work well */
76 int pauseBetweenNotes = noteDuration * 1.30;
77 delay(pauseBetweenNotes);
78 /* stop the tone playing */
79 noTone(buzzerPin);
80 }
81}
82void moveServo() {
83 if (portion > 0){
84 Serial.println("moving servo");
85 for (pos = 0; pos <= 90; pos += 1) {
86 /* goes from 0 degrees to 90 degrees */
87 myservo.write(pos); /* tell servo to go to position in variable 'pos' */
88 delay(15); /* waits 15ms for the servo to reach the position */
89 }
90 delay(portion * 300); /* keep the box open for a time interval based on the amount of food you want to deliver */
91 for (pos = 90; pos >= 0; pos -= 1) {
92/* goes from 90 degrees to 0 degrees */
93 myservo.write(pos); /* tell servo to go to position in variable 'pos' */
94 delay(15); /* waits 15ms for the servo to reach the position */
95 }
96 }
97}
98/*
99Since Notification is READ_WRITE variable, onNotificationChange() is
100executed every time a new value is received from IoT Cloud.
101*/
102void onNotificationChange() {
103/* Add your code here to act upon Notification change */
104}
105/*
106Since SendData is READ_WRITE variable, onSendDataChange() is
107executed every time a new value is received from IoT Cloud.
108*/
109void onSendDataChange() {
110/* Add your code here to act upon SendData change */
111}
112/*
113Since Portion is READ_WRITE variable, onPortionChange() is
114executed every time a new value is received from IoT Cloud.
115*/
116void onPortionChange() {
117/* Add your code here to act upon Portion change */
118}
119/*
120Since Melody is READ_WRITE variable, onMelodyChange() is
121executed every time a new value is received from IoT Cloud.
122*/
123void onMelodyChange() {
124/* Add your code here to act upon Melody change */
125}

Dashboard

The final step to deploying our project is adding a control panel using the Arduino IoT Dashboards. We can navigate to Dashboards > Build Dashboard > ADD, then we can add four widget and link them to the variable as the following:

  • Messenger widget -> notification variable
  • Slider widget -> portion variable
  • Push button widget -> sendData variable
  • Switch widget -> melody variable

Arduino IoT Bundle

Congratulations! Now you can upload the full sketch below and test your Pavlov Cat Machine!

Want to Know More?

This tutorial is part of a series of experiments that familiarize you with the Arduino IoT Bundle. All experiments can be built using the components contained in the IoT Bundle.

Full Code

Suggest changes

The content on docs.arduino.cc is facilitated through a public GitHub repository. If you see anything wrong, you can edit this page here.

License

The Arduino documentation is licensed under the Creative Commons Attribution-Share Alike 4.0 license.