IoT Cat Toy
Cat-to-Cat is a paired set of two cat toys for long distance friends and their felines. Either toy is activated whenever the other cat has started to play! If toy A is rattled with by a cat in Argentina.... toy B's wand will begin to move by a motor and catch the attention of its owner's cat in Norway! The action bounces back and forth between cats as they play. You and your long distance BFF will delight as your cats both share in each other’s fun throughout the day.
A big thank you to Becky Stern for her support throughout the project and to her cats for their product testing!
Supplies
Electronic Components per toy:
- Arduino feather huzzah board
- Micro servo (TowerPro SG92R)
- Fast vibration sensor switch (Easy to trigger)
- Micro USB cable
- USB wall adapter
- 1 green LED
- 1 orange LED
- 2 100ohm resistors
- Stranded wire
- Solder
- Fast shrink tubing
Wall Wand Components per toy:
- Cat wand attachment
- 18 gauge steel wire
Object/Container Components per toy:
- Super glue
- Wood glue
- Hot glue
- 1/4 in basswood
- Wall screws or wall velcro
Equipment:
- Soldering iron
- Hot glue gun
- Laser cutter
- Wire cutter
- Sander
Circuit Diagram and Code
// Combining Inputs and Outputs for paired cat toys
// Flick (vibration sensor) switch sends command to AIO feed
// LED and vibrating motor flash/buzz according to feed data
//
// Modified from Becky Stern's code by Erica Fink 2023
// based on examples from Adafruit IO Arduino Library:
// https://github.com/adafruit/Adafruit_IO_Arduino
//
// Adafruit invests time and resources providing this open source code.
// Please support Adafruit and open source hardware by purchasing
// products from Adafruit!
//
// Written by Todd Treece for Adafruit Industries
// Copyright (c) 2016 Adafruit Industries
// Licensed under the MIT license.
//
// All text above must be included in any redistribution.
/************************ Adafruit IO Configuration *******************************/
// update with your io.adafruit.com information
#define IO_USERNAME "enter your user name here"
#define IO_KEY "enter your key here"
/******************************* WIFI Configuration **************************************/
// update with your wifi information
#define WIFI_SSID "enter your wifi name here"
#define WIFI_PASS "enter your wifi password here"
#include "AdafruitIO_WiFi.h"
AdafruitIO_WiFi io(IO_USERNAME, IO_KEY, WIFI_SSID, WIFI_PASS);
/************************ Main Code Starts Here *******************************/
#include
#include
#include
#include
#include
Servo myservo; // create servo object to control a servo
// twelve servo objects can be created on most boards
#define ToyLEDsentIndicator_PIN 13
#define ToyLEDreceivedInidicator_PIN 12
#define ToyFLICKswitch_PIN 4 // this pin needs pullup capability
// Define some items that will be used to set your FLICK switch into a sleeper mode
unsigned long previousMillis = 0; // this will store last time FLICKswitch was set “on”/activated
// This constant won’t change.
// Set the interval to the time you want the toy to "sleep" in between plays (so that the motor is not constantly setting the other toy off)
// The toy will sleep on reading the FLICKswitch. It is set to sleep for 20 minutes (milliseconds*seconds*minutes)
const long interval = 1000*60*20;
// set up the 'digital' feed
// create your own feed on io.adafruit.com and use the name for "toyforcats" here and throughout below
AdafruitIO_Feed *toyforcatsfeed = io.feed("toyforcatsfeed");
void setup() {
myservo.attach(5); // this is the pin number on your board that the servo is connected to
// set FLICK switch pins as inputs with internal pull-up resistor
pinMode(ToyFLICKswitch_PIN, INPUT_PULLUP);
// set LED pins and motor pin as a digital outputs
pinMode(ToyLEDsentIndicator_PIN, OUTPUT);
pinMode(ToyLEDreceivedInidicator_PIN, OUTPUT);
//pinMode(ToyMOTOR_PIN, OUTPUT);
// start the serial connection
Serial.begin(115200);
// connect to io.adafruit.com
Serial.print("Connecting to Adafruit IO");
io.connect();
// set up a message handler for the 'toyforcatsfeed' feed.
// the handleMessage function (defined below)
// will be called whenever a message is
// received from adafruit io.
toyforcatsfeed->onMessage(handleMessage);
// wait for a connection
while(io.status() < AIO_CONNECTED) {
Serial.print(".");
delay(500);
}
// we are connected
Serial.println();
Serial.println(io.statusText());
}
void loop() {
// io.run(); is required for all sketches.
// it should always be present at the top of your loop
// function. it keeps the client connected to
// io.adafruit.com, and processes any incoming data.
io.run();
//code that needs to be running all the time.
// check to see if it's time to read the FLICKswitch; that is, if the difference
// between the current time and last time you activated the FLICKswitch is bigger than
// the interval at which you want to activate the FLICKswitch.
unsigned long currentMillis = millis();
if (currentMillis - previousMillis <= interval) {
// If it is still less than the interval (20 minutes), then return back to this mini loop (doing nothing) until it has been greater than the interval (20 minutes).
return;
}
// grab the current state of the FLICK switch.
// we have to flip the logic because we are
// using INPUT_PULLUP
if(digitalRead(ToyFLICKswitch_PIN) == LOW) {
// save the flick activation state to the 'digital' toyforcatsfeed feed on adafruit io
Serial.print("sending FLICK switch -> ");
//switch 1 for 2 on opposite toy chip
Serial.println(2);
toyforcatsfeed->save(2);
digitalWrite(ToyLEDsentIndicator_PIN, HIGH);
delay(1000);
digitalWrite(ToyLEDsentIndicator_PIN, LOW);
//Reset the sleeper timer so that it has to reach 20 minutes before it reads for a FLICK switch activation again.
previousMillis = currentMillis;
}
}
// this function is called upon whenever a 'toyforcatsfeed' message
// is received from Adafruit IO. it was attached to
// the toyforcatsfeed feed in the setup() function above.
void handleMessage(AdafruitIO_Data *data) {
int toyforcatsfeed = data->toInt();
//switch 2 for 1 on opposite toy chip
if (toyforcatsfeed == 1){ //light up the "received" LED and move the motor sequentially
Serial.print("received <- ");
Serial.println(toyforcatsfeed);
digitalWrite(ToyLEDreceivedInidicator_PIN, HIGH);
delay(1000);
digitalWrite(ToyLEDreceivedInidicator_PIN, LOW);
//move the motor back and forth 3 times
Serial.print("received <- ");
Serial.println(toyforcatsfeed);
myservo.write(50); // tell servo to go to position in variable 'pos'
delay(500);
myservo.write(0); // tell servo to go to position in variable 'pos'
delay(500);
myservo.write(50); // tell servo to go to position in variable 'pos'
delay(500);
} else {
Serial.print("unexpected value <- ");
Serial.println(toyforcatsfeed);
}
}
Downloads
Circuit Construction From Prototype to Soldered
Test your circuit, code, and the interactivity between your toy circuits before soldering. Change the interval in the code to a few seconds (versus 20 minutes) to be able to easily test.
Form & Material
Create a container per toy that will house your circuit, attach to the wall or sit on the floor, hold your cat toy wand - allowing for its mobility from the motor - and display the LED lights.
I did so by laser cutting 6 layers of 1/4 in basswood (using the attached illustrator file) and wood gluing them together. The main things to consider are:
- A hole at the bottom for your USB cable
- A hole at the top (or elsewhere on the toy) for the wand to extend out
- Divots that will allow you to place the motor into the toy even while soldered to your circuit and to pull it out of the toy for any repairs.
- A back opening to hold your circuit and holes for wall nails
The motor can either be super glued to the back of the top layer or screwed into the top divot section.
Tidy up your design by sanding the edges (before adding your circuit and wand), painting, and adding a logo or front graphic (I used iron-on vinyl).
Cat Time!
Time to test it out with some cats! Share in the comments any customizations you make:
- What type of cat wand attachment best engages your cat?
- Did you adjust the servo motor's code for different motion and duration?
- Does it work best on the floor or attached to the wall?
- How long do you prefer the "sleep" interval to be?
I'll be dreaming up ways to make this more fun for the human users too... maybe an app that allows you to pair with multiple cat-to-cats and to capture your cats pseudo play dates in delightful ways. Let me know if this sparks any ideas for you, I'd love your feedback.