mirror of
https://github.com/anatolykopyl/cat-ball.git
synced 2026-03-26 12:54:43 +00:00
Initial commit
This commit is contained in:
34
README.md
Normal file
34
README.md
Normal file
@@ -0,0 +1,34 @@
|
||||
# Cat Ball
|
||||
|
||||
The code I wrote to make a cat toy that will come to life when touched or
|
||||
can be controlled remotely via Wi-Fi.
|
||||
|
||||
I found that my cat loses interest in a toy as soon as he sees it not
|
||||
reacting to him catching it.
|
||||
|
||||
The idea is that a relatively strong motor with some mass attached to it's
|
||||
axis off center is glued to a spherical housing along with all the needed
|
||||
electronics. The violent vibration made by turning that motor on when
|
||||
something touches the ball makes this an interesting toy for a cat.
|
||||
|
||||
|
||||
### Components:
|
||||
- NodeMCU
|
||||
- SW-18010P vibration sensor
|
||||
- DC motor
|
||||
- MX1508 DC motor driver (depends on what motor you'll use)
|
||||
- battery
|
||||
|
||||
## Assembly
|
||||
I glued the biggest coin (a nut would be fine too) I could find off axis to
|
||||
the motor and hooked the motor to the nodeMCU through the motor driver (pin 1).
|
||||
And the vibration sensor to pin 3. The same battery powers both the motor and
|
||||
the nodeMCU.
|
||||
|
||||
Flash the .ino and now you can connect to the toy via Wi-Fi.
|
||||
|
||||
```
|
||||
SSID: Cat Ball
|
||||
KEY: churchill
|
||||
```
|
||||
Navigate to `192.168.4.1` when connected.
|
||||
120
cat_ball.ino
Normal file
120
cat_ball.ino
Normal file
@@ -0,0 +1,120 @@
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <WiFiClient.h>
|
||||
#include <ESP8266WebServer.h>
|
||||
|
||||
#ifndef APSSID
|
||||
#define APSSID "Cat Ball"
|
||||
#define APPSK "churchill"
|
||||
#endif
|
||||
|
||||
const char *ssid = APSSID;
|
||||
const char *password = APPSK;
|
||||
|
||||
ESP8266WebServer server(80);
|
||||
|
||||
const int ledPin = 2;
|
||||
const int motorPin = 1;
|
||||
const int sensorPin = 3;
|
||||
bool motorStatus = true;
|
||||
bool manualControl = false;
|
||||
|
||||
const int onTime = 1000*2;
|
||||
const int offTime = 1000*60;
|
||||
const int liveTime = 1000*60*5;
|
||||
unsigned long ticker = millis();
|
||||
unsigned long touchedAt = millis() - liveTime;
|
||||
|
||||
String createHtml() {
|
||||
String res = "<!DOCTYPE html><html>\n";
|
||||
res += "<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">\n";
|
||||
res += "<title>Cat Ball</title>\n";
|
||||
res += "<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}\n";
|
||||
res += "body{margin-top: 50px;} h1 {color: #444444;margin: 50px auto 30px;} h3 {color: #444444;margin-bottom: 50px;}\n";
|
||||
res += ".button {display: block;width: 80px;background-color: #1abc9c;border: none;color: white;padding: 13px 30px;text-decoration: none;font-size: 25px;margin: 0px auto 35px;cursor: pointer;border-radius: 7px;}\n";
|
||||
res += ".button-off {background-color: #1abc9c;}\n";
|
||||
res += ".button-off:active {background-color: #16a085;}\n";
|
||||
res += ".button-on {background-color: #34495e;}\n";
|
||||
res += ".button-on:active {background-color: #2c3e50;}\n";
|
||||
res += "p {font-size: 14px;color: #888;margin-bottom: 10px;}\n";
|
||||
res += "</style>\n";
|
||||
res += "</head>\n";
|
||||
res += "<body>\n";
|
||||
res += "<h1>Cat Ball</h1>\n";
|
||||
|
||||
if (manualControl) {
|
||||
res += "<a class=\"button button-on\" href=\"/manual\">Disable manual mode</a>\n";
|
||||
res += "<a class=\"button\" href=\"/motoron\">Turn motor on</a>\n";
|
||||
res += "<a class=\"button\" href=\"/motoroff\">Turn motor off</a>\n";
|
||||
} else {
|
||||
res += "<a class=\"button button-off\" href=\"/manual\">Enable manual mode</a>\n";
|
||||
}
|
||||
|
||||
res += "</body>\n";
|
||||
res += "</html>\n";
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void handleRoot() {
|
||||
server.send(200, "text/html", createHtml());
|
||||
}
|
||||
|
||||
void manual() {
|
||||
manualControl = !manualControl;
|
||||
motorStatus = false;
|
||||
server.send(200, "text/html", createHtml());
|
||||
}
|
||||
|
||||
void motorOn() {
|
||||
motorStatus = true;
|
||||
server.send(200, "text/html", createHtml());
|
||||
}
|
||||
|
||||
void motorOff() {
|
||||
motorStatus = false;
|
||||
server.send(200, "text/html", createHtml());
|
||||
}
|
||||
|
||||
void setup() {
|
||||
pinMode(ledPin, OUTPUT);
|
||||
pinMode(motorPin, OUTPUT);
|
||||
pinMode(sensorPin, INPUT);
|
||||
WiFi.softAP(ssid, password);
|
||||
server.on("/", handleRoot);
|
||||
server.on("/manual", manual);
|
||||
server.on("/motoron", motorOn);
|
||||
server.on("/motoroff", motorOff);
|
||||
server.begin();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
server.handleClient();
|
||||
if (manualControl) {
|
||||
digitalWrite(motorPin, motorStatus);
|
||||
} else {
|
||||
if (millis()-touchedAt < liveTime) { // If touched less than 5 minutes ago then act alive
|
||||
if (motorStatus) {
|
||||
if (millis()-ticker > onTime) {
|
||||
ticker = millis();
|
||||
motorStatus = false;
|
||||
digitalWrite(motorPin, false);
|
||||
}
|
||||
} else {
|
||||
if (millis()-ticker > offTime) {
|
||||
ticker = millis();
|
||||
motorStatus = true;
|
||||
digitalWrite(motorPin, motorStatus);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
motorStatus = false;
|
||||
digitalWrite(motorPin, false);
|
||||
digitalWrite(ledPin, true);
|
||||
|
||||
if (digitalRead(sensorPin)) {
|
||||
touchedAt = millis();
|
||||
digitalWrite(ledPin, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user