Microcontroller projects

The not completely useless box

last updated: 2020-12-29

Introduction

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:

The hardware

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³.

The circuit

As we have a minimal circuit I used a piece of prototyping PCB to solder the whole thing:

circuit

PCB front PCB back

PCB + servos

3D prints

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).

useless_box

Servo 1 Servo 2 arm jew_box

The software

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

Downloads