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;
}