last updated: 2020-12-29
XMas :). So here is the not completely useless box
. All the material are recycled or were part of my basic electronics stock. The idea and parts of the code are from this page: https://www.labdomotic.com/2017/10/24/youtube-useless-box-fai-da-te/. To ensure that the box is not completely useless, it has a 3D printed compartment for jewellery (right corner).
Here some of the 16 different actions:
Two standard servos are needed (40.6x20.3x40mm). My model was a PDI-6221MG-180. As thy needed to be fixed on the box, I designed and printed servo holder. The Arduino board is an old Teensy 2.0. A relay breakout board (www.wemos.cc) helps to switch off the box after some time of inactivity. A push-button is used to switch on. The pet switch is a simple on/off switch (SPST On-Off). Here as an alternative we could use a double switch (DPST On-Off) and omit the push-button.
A little piezo speaker is directly soldered to the Teensy.
The box is powered with 4 AA batteries (6 V) in a box with battery snap connector. Because of the high currents of the servos the batteries don't last very long. You can add a power supply (6 V / 3 A) if you want to use the box for a longer time (Maker Faire :)).
A diode 1N4007
destroys 0.8 V, so the Teensy gets his 5 V. A big capacitor can prevent voltage sacks when the servos turn.
The pets head is fixed with a powerful magnet from an old hard-disk to a sheet metal plate on the lid. The box dimensions are 300x150x120 mm³.
As we have a minimal circuit I used a piece of prototyping PCB to solder the whole thing:
To ensure that the box is not completely useless, it has a 3D printed compartment for jewellery (right corner). All parts were designed with freecad and can be adapted (downloads bottom of the page).
To play music I used the PlayRtttl
library from ArminJo. Install it with the Arduino library manager.
/* useless_box.ino
* weigu.lu
* idea: https://www.labdomotic.com/2017/10/24/youtube-useless-box-fai-da-te/
*/
#include <Servo.h>
#include <PlayRtttl.h>
Servo Servo_Arm;
Servo Servo_Lid;
const byte PIN_SWITCH = 6;
const byte PIN_SERVO_ARM = 0;
const byte PIN_SERVO_LID = 2;
const byte PIN_SPEAKER = 10;
const byte PIN_RELAY = 13;
const byte MIN_SERVO_ARM = 10;
const byte MAX_SERVO_ARM_NS = 165;
const byte MAX_SERVO_ARM_S = 178;
const byte SERVO_ARM_1 = 125;
const byte SERVO_ARM_2 = 135;
const byte SERVO_ARM_3 = 160;
const byte SERVO_LID_1 = 85;
const byte SERVO_LID_2 = 95;
const byte SERVO_LID_3 = 115;
const byte SERVO_LID_4 = 120;
const byte SERVO_LID_5 = 140;
int switch_state=0, action=1;
char *song1 = "MissionImp:d=16,o=6,b=95:32d,32d#,32d,32d#,32d,32d#,32d,32d#,32d,32d,32d#,
32e,32f,32f#,32g,g,8p,g,8p,a#,p,c7,p,g,8p,g,8p,f,p,f#,p,g,8p,g,8p,a#,p,c7,p,
g,8p,g,8p,f,p,f#,p,a#,g,2d,32p,a#,g,2c#,32p,a#,g,2c,a#5,8c,2p,32p,a#5,g5,
2f#,32p,a#5,g5,2f,32p,a#5,g5,2e,d#,8d";
char *song2 = "MahnaMahna:d=16,o=6,b=125:c#,c.,b5,8a#.5,8f.,4g#,a#,g.,4d#,8p,c#,c.,b5,
8a#.5,8f.,g#.,8a#.,4g,8p,c#,c.,b5,8a#.5,8f.,4g#,f,g.,8d#.,f,g.,8d#.,f,8g,
8d#.,f,8g,d#,8c,a#5,8d#.,8d#.,4d#,8d#.";
void setup() {
pinMode(PIN_RELAY, OUTPUT);
digitalWrite(PIN_RELAY,HIGH);
Serial.begin(115200);
delay(1000);
Serial.println("Hello from the useless box!");
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN,HIGH);
pinMode(PIN_SWITCH, INPUT_PULLUP);
Servo_Arm.attach(PIN_SERVO_ARM);
Servo_Lid.attach(PIN_SERVO_LID);
Servo_Arm.write(MIN_SERVO_ARM);
Servo_Lid.write(SERVO_LID_1);
}
void loop() {
switch_state = digitalRead(PIN_SWITCH);
//action = random(1,16);
if (non_blocking_delay(1200000)) { // 20 minutes
digitalWrite(PIN_RELAY,LOW);
}
if (switch_state == LOW) {
// slow = s, quick = q, o = opening, c = closing
switch(action) {
case 1:
Serial.println("Action 1"); // s_o s_s+1s s_c
for (int i = SERVO_LID_1; i <= SERVO_LID_5; i++) {
Servo_Lid.write(i);
delay(20);
}
for (int i = MIN_SERVO_ARM; i <= MAX_SERVO_ARM_NS; i++) {
Servo_Arm.write(i);
delay(20);
}
delay(1000);
Servo_Arm.write(MAX_SERVO_ARM_S);
delay(200);
for (int i = MAX_SERVO_ARM_S; i >= MIN_SERVO_ARM; i--) {
Servo_Arm.write(i);
delay(20);
}
for (int i = SERVO_LID_5; i >= SERVO_LID_1; i--) {
Servo_Lid.write(i);
delay(20);
}
action++;
break;
case 2:
Serial.println("Action 2"); // q_o q_s q_c 1.5s q_o 3s q_c
for (int i = SERVO_LID_1; i <= SERVO_LID_5; i++) {
Servo_Lid.write(i);
delay(6);
}
delay(550);
Servo_Arm.write(MAX_SERVO_ARM_S);
delay(700);
Servo_Arm.write(MIN_SERVO_ARM);
delay(550);
Servo_Lid.write(SERVO_LID_1);
delay(1500);
for (int i = SERVO_LID_1; i <= SERVO_LID_5; i++) {
Servo_Lid.write(i);
delay(6);
}
delay(3000);
Servo_Lid.write(SERVO_LID_1);
action++;
break;
case 3:
Serial.println("Action 3"); // q_o 1s q_c 2s q_o q_s q_c
for (int i = SERVO_LID_1; i <= SERVO_LID_5; i++) {
Servo_Lid.write(i);
delay(6);
}
delay(1000);
Servo_Lid.write(SERVO_LID_1);
delay(2000);
for (int i = SERVO_LID_1; i <= SERVO_LID_5; i++) {
Servo_Lid.write(i);
delay(6);
}
Servo_Arm.write(MAX_SERVO_ARM_S);
delay(700);
Servo_Arm.write(MIN_SERVO_ARM);
delay(500);
Servo_Lid.write(SERVO_LID_1);
action++;
break;
case 4:
Serial.println("Action 4"); // s_o s_s+1s q_c
for (int i = SERVO_LID_1; i <= SERVO_LID_5; i++) {
Servo_Lid.write(i);
delay(20);
}
for (int i = MIN_SERVO_ARM; i <= MAX_SERVO_ARM_NS; i++) {
Servo_Arm.write(i);
delay(20);
}
delay(2000);
Servo_Arm.write(MAX_SERVO_ARM_S);
delay(700);
Servo_Arm.write(MIN_SERVO_ARM);
delay(400);
Servo_Lid.write(SERVO_LID_1);
action++;
break;
case 5:
Serial.println("Action 5"); // q_o 2s arm_toggling q_c
for (int i = SERVO_LID_1; i <= SERVO_LID_5; i++) {
Servo_Lid.write(i);
delay(6);
}
delay(2000);
Servo_Arm.write(SERVO_ARM_2);
delay(550);
Servo_Arm.write(SERVO_ARM_3);
delay(200);
Servo_Arm.write(SERVO_ARM_2);
delay(200);
Servo_Arm.write(SERVO_ARM_3);
delay(200);
Servo_Arm.write(SERVO_ARM_2);
delay(200);
Servo_Arm.write(SERVO_ARM_3);
delay(200);
Servo_Arm.write(SERVO_ARM_2);
delay(200);
Servo_Arm.write(SERVO_ARM_3);
delay(200);
Servo_Arm.write(SERVO_ARM_2);
delay(200);
Servo_Arm.write(SERVO_ARM_3);
delay(200);
Servo_Arm.write(SERVO_ARM_2);
delay(2000);
Servo_Arm.write(MAX_SERVO_ARM_S);
delay(700);
Servo_Arm.write(MIN_SERVO_ARM);
delay(400);
Servo_Lid.write(SERVO_LID_1);
action++;
break;
case 6:
Serial.println("Action 6"); // s_o lid_toggling s_s q_c 1.5s q_o 3s q_c
for (int i = SERVO_LID_1; i <SERVO_LID_5; i++) {
Servo_Lid.write(i);
delay(20);
}
delay(1000);
for (int i = 0; i < 12; i++) {
for (int j = SERVO_LID_1; j <= SERVO_LID_5; j++) {
Servo_Lid.write(j);
delay(6);
}
delay(200);
}
for (int i = MIN_SERVO_ARM; i <= MAX_SERVO_ARM_NS; i++) {
Servo_Arm.write(i);
delay(20);
}
Servo_Arm.write(MAX_SERVO_ARM_S);
delay(700);
Servo_Arm.write(MIN_SERVO_ARM);
delay(400);
Servo_Lid.write(SERVO_LID_1);
delay(1500);
for (int i = SERVO_LID_1; i <= SERVO_LID_5; i++) {
Servo_Lid.write(i);
delay(6);
}
delay(3000);
Servo_Lid.write(SERVO_LID_2);
action++;
break;
case 7:
Serial.println("Action 7"); // q_o lid_toggle_3 song1 q_s q_c
for (int i = SERVO_LID_1; i <= SERVO_LID_5; i++) {
Servo_Lid.write(i);
delay(6);
}
delay(400);
Serial.println("here");
Servo_Arm.write(MIN_SERVO_ARM);
Serial.println("here2");
delay(1000);
for (int i = 0; i < 3; i++) {
for (int j = SERVO_LID_1; j <= SERVO_LID_5; j++) {
Servo_Lid.write(j);
delay(6);
}
delay(200);
}
//delay(500);
playRtttlBlocking(PIN_SPEAKER, song1);
Servo_Arm.write(MAX_SERVO_ARM_S);
delay(700);
Servo_Arm.write(MIN_SERVO_ARM);
delay(400);
Servo_Lid.write(SERVO_LID_1);
action++;
break;
case 8:
Serial.println("Action 8"); //
for (int i = SERVO_LID_1; i <= SERVO_LID_5; i++) {
Servo_Lid.write(i);
delay(6);
}
Servo_Arm.write(MAX_SERVO_ARM_S);
delay(2000);
for (int i = 0; i < 7; i++) {
Servo_Arm.write(SERVO_ARM_1);
delay(100);
Servo_Arm.write(SERVO_ARM_3);
delay(100);
}
delay(500);
Servo_Arm.write(MAX_SERVO_ARM_S);
delay(700);
Servo_Arm.write(MIN_SERVO_ARM);
delay(400);
Servo_Lid.write(SERVO_LID_1);
action++;
break;
case 9:
Serial.println("Action 9"); //
for (int i = SERVO_LID_1; i <= SERVO_LID_4; i++) {
Servo_Lid.write(i);
delay(6);
}
delay(2000);
Servo_Lid.write(SERVO_LID_5);
for (int i = MIN_SERVO_ARM; i <= MAX_SERVO_ARM_NS; i++) {
Servo_Arm.write(i);
delay(40);
}
delay(500);
Servo_Arm.write(MAX_SERVO_ARM_S);
delay(200);
for (int i = MAX_SERVO_ARM_S; i >= MIN_SERVO_ARM; i--) {
Servo_Arm.write(i);
delay(40);
}
Servo_Lid.write(SERVO_LID_1);
delay(2000);
Servo_Lid.write(SERVO_LID_3);
delay(3000);
Servo_Lid.write(SERVO_LID_1);
action++;
break;
case 10:
Serial.println("Action 10"); //
for (int i = SERVO_LID_1; i <= SERVO_LID_4; i++) {
Servo_Lid.write(i);
delay(100);
}
delay(1000);
for (int i = 0; i < 10; i++) {
for (int j = SERVO_LID_1; j <= SERVO_LID_3; j++) {
Servo_Lid.write(j);
delay(6);
}
delay(100);
}
Servo_Lid.write(SERVO_LID_5);
delay(100);
Servo_Arm.write(MAX_SERVO_ARM_S);
delay(650);
Servo_Arm.write(MIN_SERVO_ARM);
delay(400);
Servo_Lid.write(SERVO_LID_1);
action++;
break;
case 11:
Serial.println("Action 11"); //
for (int i = SERVO_LID_1; i <= SERVO_LID_5; i++) {
Servo_Lid.write(i);
delay(6);
}
Servo_Arm.write(MAX_SERVO_ARM_S);
delay(2000);
for (int i = 0; i < 3; i++) {
Servo_Arm.write(SERVO_ARM_2);
delay(200);
Servo_Arm.write(SERVO_ARM_3);
delay(200);
}
delay(1500);
Servo_Arm.write(MIN_SERVO_ARM);
delay(400);
Servo_Lid.write(SERVO_LID_1);
delay(1500);
for (int i = SERVO_LID_1; i <= SERVO_LID_5; i++) {
Servo_Lid.write(i);
delay(6);
}
Servo_Arm.write(MAX_SERVO_ARM_S);
delay(3000);
Servo_Arm.write(MIN_SERVO_ARM);
delay(400);
Servo_Lid.write(SERVO_LID_1);
action++;
break;
case 12:
Serial.println("Action 12"); //
for (int i = SERVO_LID_1; i <= SERVO_LID_5; i++) {
Servo_Lid.write(i);
delay(50);
}
delay(1000);
for (int i = MIN_SERVO_ARM; i <= MAX_SERVO_ARM_NS; i++) {
Servo_Arm.write(i);
delay(50);
}
delay(2000);
Servo_Arm.write(MAX_SERVO_ARM_S);
delay(700);
Servo_Arm.write(MIN_SERVO_ARM);
delay(400);
Servo_Lid.write(SERVO_LID_1);
action++;
break;
case 13:
Serial.println("Action 13"); //
for (int i = SERVO_LID_1; i <= SERVO_LID_5; i++) {
Servo_Lid.write(i);
delay(6);
}
delay(2000);
for (int i = MIN_SERVO_ARM; i >= SERVO_ARM_2; i--) { // orig 60
Servo_Arm.write(i);
delay(40);
}
for (int i = 0; i < 3; i++) {
Servo_Arm.write(SERVO_ARM_2);
delay(200);
Servo_Arm.write(SERVO_ARM_3);
delay(800);
}
Servo_Arm.write(MAX_SERVO_ARM_S);
delay(2000);
Servo_Arm.write(MIN_SERVO_ARM);
delay(500);
Servo_Lid.write(SERVO_LID_1);
action++;
break;
case 14:
Serial.println("Action 14"); //
for (int i = SERVO_LID_1; i <= SERVO_LID_4; i++) {
Servo_Lid.write(i);
delay(6);
}
delay(4000);
Servo_Lid.write(SERVO_LID_5);
Servo_Arm.write(MAX_SERVO_ARM_S);
delay(650);
Servo_Arm.write(MIN_SERVO_ARM);
delay(500);
Servo_Lid.write(SERVO_LID_3);
delay(4000);
Servo_Lid.write(SERVO_LID_1);
action++;
break;
case 15:
Serial.println("Action 15"); //
for (int i = SERVO_LID_1; i <= SERVO_LID_4; i++) {
Servo_Lid.write(i);
delay(6);
}
delay(1000);
for (int i = 0; i < 3; i++) {
for (int j = SERVO_LID_1; j <= SERVO_LID_4; j++) {
Servo_Lid.write(j);
delay(50);
}
for (int j = SERVO_LID_4; j >= SERVO_LID_1; j--) {
Servo_Lid.write(j);
delay(50);
}
}
for (int j = SERVO_LID_1; j <= SERVO_LID_5; j++) {
Servo_Lid.write(j);
delay(50);
}
for (int i = MIN_SERVO_ARM; i <= MAX_SERVO_ARM_NS; i++) {
Servo_Arm.write(i);
delay(40);
}
delay(1000);
Servo_Arm.write(MAX_SERVO_ARM_S);
delay(700);
Servo_Arm.write(MIN_SERVO_ARM);
delay(400);
Servo_Lid.write(SERVO_LID_1);
action = 16;
break;
case 16:
Serial.println("Action 16"); //
for (int i = SERVO_LID_1; i <= SERVO_LID_5; i++) {
Servo_Lid.write(i);
delay(6);
}
delay(1000);
playRtttlBlocking(PIN_SPEAKER, song2);
Servo_Arm.write(MAX_SERVO_ARM_S);
delay(700);
Servo_Arm.write(MIN_SERVO_ARM);
delay(400);
Servo_Lid.write(SERVO_LID_1);
delay(200);
digitalWrite(PIN_RELAY,LOW);
break;
}
}
}
// non blocking delay using millis(), returns true if time is up
bool non_blocking_delay(unsigned long milliseconds) {
static unsigned long nb_delay_prev_time = 0;
if(millis() >= nb_delay_prev_time + milliseconds) {
nb_delay_prev_time += milliseconds;
return true;
}
return false;
}