├── AutoCatLaser └── TrinketProAutoLaser.ino ├── Pro Trinket Wiring.png ├── README.md └── license.txt /AutoCatLaser/TrinketProAutoLaser.ino: -------------------------------------------------------------------------------- 1 | /* Automatic Cat Laser 2 | 3 | This sketch runs one of 12 functions at random. The functions vary - 4 | The range restriction controls the extent of both servos in each direction, making it adjustable to the room. 5 | 0 - 180 is the full range on a standard servo. 6 | The warpSpeed variable controls the speed. It is a divisor of the delay so lower value is slower, 1.0 is normal speed. 7 | 8 | created Jan 2016 9 | by Frank Appio 10 | http://fluxaxiom.com/ 11 | Feel free to modify or use as you wish 12 | */ 13 | 14 | int rangeRestrictX[] {50,170}; 15 | int rangeRestrictY[] {65,160}; 16 | 17 | float warpSpeed = 1.0; 18 | 19 | 20 | 21 | 22 | 23 | #include // Using the standard Arduino servo library to keep things simple. 24 | 25 | // Attach servos and laser 26 | Servo servoX; 27 | Servo servoY; 28 | const int laser=3; 29 | 30 | // Initialize some variables 31 | int posX = 0; 32 | int posY = 0; 33 | int lastPosX = 0; 34 | int lastPosY = 0; 35 | 36 | int rangeX = rangeRestrictX[1]-rangeRestrictX[0]; 37 | int rangeY = rangeRestrictY[1]-rangeRestrictY[0]; 38 | 39 | 40 | 41 | void setup() { 42 | pinMode(laser, OUTPUT); 43 | servoX.attach(4); 44 | servoY.attach(8); 45 | 46 | // Shuffle the random algorithm with an unconnected pin 47 | randomSeed(analogRead(0)); 48 | } 49 | 50 | 51 | 52 | void loop() { 53 | digitalWrite(laser,HIGH); 54 | float angle = 0; 55 | int sPosX = posX; 56 | int sPosY = posY; 57 | 58 | // Random variables 59 | int selectRandom = random(0,12); 60 | int randomDelay = random(10,51)/warpSpeed; 61 | int randomDelay2 = random(10,101)/warpSpeed; 62 | int randomDelay3 = random(20,31)/warpSpeed; 63 | int randomSteps = random(1,4); 64 | int randomSteps2 = random(1,5); 65 | bool luck = random(0,2); 66 | int circleXcenter = random(80,101); 67 | int circleYcenter = random(80,101); 68 | 69 | // Keep things in bounds - needed for the unrestricted functions or you will wind up on the ceiling. 70 | if (posXrangeRestrictX[1]) { 71 | posX=rangeRestrictX[1]-rangeX/2; 72 | servoX.write(posX); } 73 | if (posYrangeRestrictY[1]) { 74 | posY=rangeRestrictY[1]-rangeY/2; 75 | servoY.write(posY); } 76 | 77 | 78 | switch (selectRandom) { 79 | 80 | // Taunt 81 | case 0: 82 | for (int i=0; i<6; i++) { 83 | posX=random(rangeRestrictX[0]+rangeX/4,rangeRestrictX[1]-rangeX/4); 84 | posY=random(rangeRestrictY[0]+rangeY/4,rangeRestrictY[1]-rangeY/4); 85 | servoX.write(posX); 86 | servoY.write(posY); 87 | delay(randomDelay3*30/warpSpeed); 88 | } 89 | break; 90 | 91 | // Wobble 92 | case 1: 93 | for (int rad = 5; rad < 15; rad++) { 94 | for (int i = 5; i > 0; i--) { 95 | angle = i*2*3.14/10; 96 | lastPosX=posX; 97 | lastPosY=posY; 98 | posX = circleXcenter + (cos(angle) * rad); 99 | posY = circleYcenter + (sin(angle) * rad); 100 | if (posX>lastPosX) { // Slow things down 101 | posX=lastPosX+1; 102 | } 103 | else { 104 | posX=lastPosX-1; 105 | } 106 | if (posY>lastPosY) { 107 | posY=lastPosY+1; 108 | } 109 | else { 110 | posY=lastPosY-1; 111 | } 112 | servoX.write(posX); 113 | servoY.write(posY); 114 | 115 | if (luck) { 116 | if (sPosX>rangeX/2) { 117 | circleXcenter-=1; 118 | } 119 | else { 120 | circleXcenter+=1; 121 | } 122 | } 123 | else { 124 | if (sPosY>rangeY/2) { 125 | circleYcenter-=1; 126 | } 127 | else { 128 | circleYcenter+=1; 129 | } 130 | } 131 | delay(randomDelay*2); } 132 | } 133 | break; 134 | 135 | // Scan 136 | case 2: 137 | posY=random(rangeRestrictY[0]+rangeY/10,rangeRestrictY[1]-rangeY/4); 138 | servoY.write(posY); 139 | for (posX = rangeRestrictX[0]; posX <= rangeRestrictX[1]; posX += 1) { 140 | servoX.write(posX); 141 | delay(randomDelay); 142 | } 143 | for (posX = rangeRestrictX[1]; posX >= rangeRestrictX[0]; posX -= 1) { 144 | servoX.write(posX); 145 | delay(randomDelay2); 146 | } 147 | break; 148 | 149 | // Zip 150 | case 3: 151 | for (int i=0; i<3; i++) { 152 | posX=random(rangeRestrictX[0],rangeRestrictX[1]); 153 | posY=random(rangeRestrictY[0],rangeRestrictY[1]); 154 | servoX.write(posX); 155 | servoY.write(posY); 156 | delay(randomDelay2*10); 157 | } 158 | break; 159 | 160 | // Boomerang 161 | case 4: 162 | lastPosX=posX; 163 | sPosX=random(rangeRestrictX[0],rangeRestrictX[1]); 164 | if (posX= sPosX; posX--) { 172 | servoX.write(posX); 173 | delay(randomDelay3); 174 | } 175 | } 176 | for (posY; posY >= rangeRestrictY[0]; posY--) { 177 | posY-=1; 178 | servoY.write(posY); 179 | delay(randomDelay3); 180 | } 181 | for (posY = rangeRestrictY[0]; posY <= rangeRestrictY[1]-rangeY/5; posY += 1) { 182 | if (posY%2){ // Wobble on the throw 183 | posX+=1; 184 | servoX.write(posX); 185 | } 186 | else { 187 | posX-=1; 188 | servoX.write(posX); 189 | } 190 | servoY.write(posY); 191 | delay(randomDelay); 192 | } 193 | if (luck>0) { // If we have no luck, the boomerang doesn't come back 194 | for (posY = rangeRestrictY[1]-rangeY/5; posY >= rangeRestrictY[0]; posY -= 1) { 195 | if (posY>rangeRestrictY[1]-rangeY/2){ // Curve on return 196 | if(posY%2){ 197 | posX+=1; 198 | servoX.write(posX); 199 | } 200 | } 201 | else { 202 | if(posY%2){ 203 | posX-=1; 204 | servoX.write(posX); 205 | } 206 | } 207 | servoY.write(posY); 208 | delay(randomDelay2); 209 | }} 210 | break; 211 | 212 | // Creep 213 | case 5: 214 | lastPosX=posX; 215 | sPosX=(posX+(rangeRestrictX[1]-rangeX/2))/2; 216 | if (posX= sPosX; posX--) { 224 | servoX.write(posX); 225 | delay(randomDelay3); 226 | } 227 | } 228 | lastPosY=posY; 229 | sPosY=(posY+(rangeRestrictY[1]-rangeY/2))/2; 230 | if (posY= sPosY; posY--) { 238 | servoY.write(posY); 239 | delay(randomDelay3); 240 | } 241 | } 242 | for (int i=0; i<20; i++){ 243 | if (luck) { 244 | posX+=randomSteps; 245 | posY+=randomSteps; 246 | } 247 | else { 248 | posX-=randomSteps; 249 | posY-=randomSteps; 250 | } 251 | servoX.write(posX); 252 | servoY.write(posY); 253 | delay(randomDelay3*15/warpSpeed); 254 | } 255 | break; 256 | 257 | // Squiggle 258 | case 6: 259 | for (int i=0; i<120; i++){ 260 | if (i%2) { 261 | posX+=randomSteps2; 262 | posY+=randomSteps; 263 | } 264 | else { 265 | posX-=randomSteps2; 266 | posY-=randomSteps; 267 | } 268 | servoX.write(posX); 269 | servoY.write(posY); 270 | delay(randomSteps*5/warpSpeed); 271 | } 272 | break; 273 | 274 | // Blink 275 | case 7: 276 | for (int i=0; i<10; i++) { 277 | digitalWrite(laser,LOW); 278 | delay(randomDelay+randomDelay2+(20/warpSpeed)); 279 | digitalWrite(laser,HIGH); 280 | delay(randomDelay+randomDelay2+(20/warpSpeed)); 281 | } 282 | break; 283 | 284 | // Circle 285 | case 8: 286 | if (luck){ 287 | for (int rad = 5; rad < 20; rad++) { 288 | for (int i = 0; i < 10; i++) { 289 | angle = i*2*3.14/10; 290 | posX = circleXcenter + (cos(angle) * rad); 291 | posY = circleYcenter + (sin(angle) * rad); 292 | servoX.write(posX); 293 | servoY.write(posY); 294 | delay(randomSteps2*6/warpSpeed); }} 295 | } 296 | else { 297 | for (int rad = 20; rad > 5; rad--) { 298 | for (int i = 10; i > 0; i--) { 299 | angle = i*2*3.14/10; 300 | posX = circleXcenter + (cos(angle) * rad); 301 | posY = circleYcenter + (sin(angle) * rad); 302 | servoX.write(posX); 303 | servoY.write(posY); 304 | delay(randomSteps2*6/warpSpeed); }} 305 | } 306 | break; 307 | 308 | // ZigZag 309 | case 9: 310 | if (luck) { 311 | for (posX = rangeRestrictX[0]; posX <= rangeRestrictX[0]+rangeX/10; posX += 1) { 312 | posY+=randomSteps; 313 | servoX.write(posX); 314 | servoY.write(posY); 315 | delay(randomDelay); 316 | } 317 | for (posX = rangeRestrictX[0]+rangeX/10; posX <= rangeRestrictX[0]+rangeX/5; posX += 1) { 318 | posY-=randomSteps; 319 | servoX.write(posX); 320 | servoY.write(posY); 321 | delay(randomDelay); 322 | } 323 | for (posX = rangeRestrictX[0]+rangeX/5; posX <= rangeRestrictX[0]+rangeX/3.3; posX += 1) { 324 | posY+=randomSteps; 325 | servoX.write(posX); 326 | servoY.write(posY); 327 | delay(randomDelay); 328 | } 329 | for (posX = rangeRestrictX[0]+rangeX/3.3; posX <= rangeRestrictX[0]+rangeX/2.5; posX += 1) { 330 | posY-=randomSteps; 331 | servoX.write(posX); 332 | servoY.write(posY); 333 | delay(randomDelay); 334 | } 335 | for (posX = rangeRestrictX[0]+rangeX/2.5; posX <= rangeRestrictX[0]+rangeX/2; posX += 1) { 336 | posY+=randomSteps; 337 | servoX.write(posX); 338 | servoY.write(posY); 339 | delay(randomDelay); 340 | } 341 | for (posX = rangeRestrictX[0]+rangeX/2; posX <= rangeRestrictX[0]+rangeX/1.65; posX += 1) { 342 | posY-=randomSteps; 343 | servoX.write(posX); 344 | servoY.write(posY); 345 | delay(randomDelay); 346 | } 347 | } 348 | else { 349 | for (posY = rangeRestrictY[1]-rangeY/10; posY >= rangeRestrictY[1]-rangeY/5; posY -= 1) { 350 | posX-=randomSteps2; 351 | servoX.write(posX); 352 | servoY.write(posY); 353 | delay(randomDelay2); 354 | } 355 | for (posY = rangeRestrictY[1]-rangeY/5; posY >= rangeRestrictY[1]-rangeY/3.3; posY -= 1) { 356 | posX+=randomSteps2; 357 | servoX.write(posX); 358 | servoY.write(posY); 359 | delay(randomDelay2); 360 | } 361 | for (posY = rangeRestrictY[1]-rangeY/3.3; posY >= rangeRestrictY[1]-rangeY/2.5; posY -= 1) { 362 | posX-=randomSteps2; 363 | servoX.write(posX); 364 | servoY.write(posY); 365 | delay(randomDelay2); 366 | } 367 | for (posY = rangeRestrictY[1]-rangeY/2.5; posY >= rangeRestrictY[1]-rangeY/2; posY -= 1) { 368 | posX+=randomSteps2; 369 | servoX.write(posX); 370 | servoY.write(posY); 371 | delay(randomDelay2); 372 | } 373 | for (posY = rangeRestrictY[1]-rangeY/2; posY >= rangeRestrictY[1]-rangeY/1.65; posY -= 1) { 374 | posX-=randomSteps2; 375 | servoX.write(posX); 376 | servoY.write(posY); 377 | delay(randomDelay2); 378 | } 379 | for (posY = rangeRestrictY[1]-rangeY/1.65; posY >= rangeRestrictY[1]-rangeY/1.425; posY -= 1) { 380 | posX+=randomSteps2; 381 | servoX.write(posX); 382 | servoY.write(posY); 383 | delay(randomDelay2); 384 | } 385 | } 386 | break; 387 | 388 | // Chase 389 | case 10: 390 | posY=random(rangeRestrictY[0]+rangeY/10,rangeRestrictY[1]-rangeY/4); 391 | servoY.write(posY); 392 | posX=rangeRestrictX[1]-rangeX/2; 393 | servoX.write(posX); 394 | for (int i = 40; i > 0; i--) { 395 | luck = random(0,2); // Making our own luck 396 | if (luck) { 397 | posX+=randomSteps2*2; 398 | servoX.write(posX); 399 | } 400 | else { 401 | posX-=randomSteps*2; 402 | servoX.write(posX); 403 | } 404 | delay(randomDelay2); 405 | } 406 | break; 407 | 408 | // Hide 409 | case 11: 410 | if (luck) { 411 | digitalWrite(laser,LOW); // Laser off 412 | delay(randomSteps2*1000/warpSpeed); 413 | } 414 | break; 415 | } // End switch 416 | 417 | } // End loop 418 | -------------------------------------------------------------------------------- /Pro Trinket Wiring.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluxaxiom/Arduino_AutoCatLaser/3c335f424e25f44d737da894403e4ebe93f20543/Pro Trinket Wiring.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## CheetahBeam - The Automatic Cat Laser ## 2 | 3 | This project code is for use with the Adafruit Pro Trinket 5V, laser module KY 008 and 2 servos. 4 | 5 | https://www.instructables.com/id/CheetahBeam-a-DIY-Automatic-Cat-Laser-Toy/ 6 | 7 | Check out the link above for assembly instructions and example video 8 | 9 | You must place the AutoCatLaser folder inside your Arduino folder in order for it to compile. 10 | 11 | 12 | ## What does this code do? ## 13 | 14 | This code cycles randomly through 12 different functions designed to entice cats to play. 15 | 16 | Functions: 17 | 18 | Taunt 19 | Wobble 20 | Scan 21 | Zip 22 | Boomerang 23 | Creep 24 | Squiggle 25 | Blink 26 | Circle 27 | ZigZag 28 | Chase 29 | Hide 30 | 31 | 32 | ## About this Project ## 33 | 34 | Written by Frank Appio. 35 | BSD license, all text above must be included in any redistribution 36 | 37 | To download. click the **Download ZIP** in the right-hand column. 38 | -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | Software License Agreement (BSD License) 2 | 3 | Copyright (c) 2015, Frank Appio 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 1. Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 3. Neither the name of the copyright holders nor the 14 | names of its contributors may be used to endorse or promote products 15 | derived from this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY 18 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY 21 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 26 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | --------------------------------------------------------------------------------