Disappearing Card Holder

by kyle_mc16 in Circuits > Arduino

187 Views, 0 Favorites, 0 Comments

Disappearing Card Holder

Useless machine

The Disappearing Card Holder, a box that looks simply like a model tag dispenser, yet will make it almost impossible for you to accomplish the simple task of retrieving a tag from it. The box is intended at act as a pseudo-living entity with the sole purpose of complicating and frustrating students looking to tag their models.

The project's concept centers around a spring-piston mechanism that when depressed will withdraw its stash of tags into the box preventing someone from taking one. To complicate the process, a network of sensors and LEDs are connected to the box that (at first glace) seem to interact with the core mechanism yet in reality do not prevent the platform from retracting (adding to the user's frustration).

Supplies

IMG_6529.png
IMG_6530.png

Components:

  1. x2 200Ω Resistors
  2. x2 10KΩ Resistors
  3. x1 Red LED
  4. x1 Green LED
  5. x2 Photoresistors
  6. x1 Servo Motor
  7. x1 Ultrasonic Sensor
  8. x2 Arduino Nano R4 (This project replaced one Nano with an Arduino Uno due to lack of materials)
  9. x3 Breadboards
  10. Jumper Wires

3D Printed Elements:

  1. Piston Platform
  2. Plastic Spring
  3. Piston Mount
  4. Motor Mount
  5. Motor Lever Attachment

Materials:

  1. Cardboard
  2. Foam (Different Sizes/Types as support)
  3. Cards, Tags, etc... (Dispensed Object the machine tries to keep)

Tools:

  1. Hot Glue Gun
  2. X-Acto Knife & Ruler

Concept & Prototyping

Screenshot 2026-03-02 191735.png
IMG_8340.png

Concept

A device that retracts its supply of studio model tags when it senses someone trying to grab one. It then complicates the process with unnecessary sensors, lights and motion twitches.

Sensor-Actuator Logic

  1. When the Ultrasonic Sensor detects something nearby, the motor will press the spring (causing the platform to drop) . When nothing is nearby, the motor will release (allowing the platform to rise).
  2. If something remains nearby for long periods of time, the motor will twitch the spring to create small jitters (making it hard to insert a hand into the slot and remove a card).
  3. If nothing is detected for extended periods of time, the motor will apply pressure in patterns to the spring (creating small 'creature-like' animations).
  4. Photoresistor sensors on the sides of the box activate LEDs when covered. If both Photoresistors are covered properly at the same time they will cause a small up-down animation from the spring-motor system (this does not affect the Ultrasonic sensor and is used to merely add to the user's frustration).

Prototyping

We began by creating a prototype of our Spring-Piston system using LEGO and 3D Printed parts in order to test the position of the motor's lever arm, as well as how the spring would react. Through this, we refined our design, adding a second piston (without a spring) and extending the piston mount.

Assembly of Base and Spring Platform

IMG_8356.jpg
IMG_8347.png
IMG_8353.png

In order to make the spring compress sufficiently, we created a mount for both the motor and the piston-spring system. Both of these were glued to a base with the non-spring cylinder encased in a sleeve that allowed it to move freely vertically but not horizontally.

We created the base to be 15cm by 30cm with the piston mount positioned in the center to allow ample space for wiring and other components to be added later.

Through testing we found the optimal positioning of the motor arm (3D printed) is for it to rest slightly above the piston platform when set to 95° and was attached using one of the motor's custom brackets.

Assembly of Walls and Misleading Sensor Wiring

Screenshot 2026-03-02 195334.png
IMG_8361.jpg
IMG_8360.jpg

Next, we began adding walls (16cm high) as well as positioning a breadboard inside the box to control the wires for the Misleading Sensors. Beginning with the front facing wall, we added two holes for LEDs, as well as one hole in the center of each side wall to hold the Photoresistors.

After inserting the two Photoresistors and LEDs, wiring follows the diagram above with emphasis placed on the wires that extend outside the diagram and notated as being attached to the main breadboard (these will be shown later, for now excess wire should be pulled to the back of the box).

Placing the final back wall, we pulled the wires for the Servo Motor (as well as all wires intended to connect from the Misleading Sensors breadboard to the main breadboard) through a small hole created in the bottom of the wall.

Throughout this process, we found the most difficulty in assuring that the jumper cables remained attached to the LEDs and Photoresistors as they would often slip off when wires were accidentally moved (electrical tape would be recommended as a solution for future builds).

Wiring Main Circuit

Screenshot 2026-03-02 200732.jpg
IMG_8367.jpg

With the walls of the box complete (and all wires for the Servo Motor and Misleading Sensors pulled outside the back of the box) wiring the main breadboard (attached to the Main Arduino Nano R4) follows the diagram above with emphasis placed on the wires running from the main board to the Motor Connectors and the Misleading Sensors Breadboard.

With wiring complete, the code linked below was uploaded to the Arduino Nano R4. Rotation values for the motor were set through calibration to between 75° and 95° though in future builds, this may require additional calibration according to fabrication precision.

Code for the Main System can be copied or downloaded below:

/* Main Arduino Code

Used to control LEDs, Photoresisor Sensors, Servo Motor
and to process signals received from the Ultrasonic sensor.

*/

#include <Servo.h>

// Define Sensor and Actuator Pins
const int AphotoPIN = A0;
const int BphotoPIN = A1;
const int RledPIN = 10;
const int GledPIN = 11;
const int AservoPIN = 5;
const int dataPIN = 12;

// Servo Motor Setup
Servo servoA;
int pos = 0;

// Sensor Data from Arduino Controlling Ultrasonic Sensor
int sensor_output = 0;
int DATA = 0;

// Variables for Misleading Sensors
int sensorValueA = 0; // Photoresistor Values
int sensorValueB = 0;
int dummyA = 0; // Target Value of Photoresistor
int dummyB = 0;
int dummyAhigh = 0; // Bounds on Acceptable Target Values
int dummyAlow = 0;
int dummyBhigh = 0;
int dummyBlow = 0;
boolean dummyAtest = false; // Test for Correct Photoresistor Value
boolean dummyBtest = false;
int seed = 0; // Random Sensor Variation

// Variables for Passive Animations
boolean passive_animation = false;
int passive_TIMER = 0;
int passive_SELECT = 0;

// Variables for when Motion is Detected
boolean motion_detected = false;
int trigger_length = 0;
boolean motion_active = false;


void setup() {
// Randomizer for Misleading Sensors and Passive Animations:
seed = analogRead(A2);
randomSeed(seed);
// Define Input/Output Pins:
pinMode(RledPIN, OUTPUT);
pinMode(GledPIN, OUTPUT);
pinMode(dataPIN, INPUT);
servoA.attach(AservoPIN);
// Move Motor to Starting Position:
servoA.write(95);
// Define Misleading Sensors Values:
dummyA = random(30, 70);
dummyB = random(30, 70);
dummyAlow = dummyA - 10;
dummyAhigh = dummyA + 10;
dummyBlow = dummyB - 10;
dummyBhigh = dummyB + 10;
// Serial.begin (9600); (For Troubleshooting)
// 'Warm Up' Delay for Proper Operation Confirmation:
delay(2500);
}

void loop() {
// Randomizer for Passive Animations:
seed = analogRead(A2);
randomSeed(seed);
// Check if a Passive Animation should be played (and which one):
if (passive_animation == true) {
if (passive_SELECT == 1) {
passive_1();
} else if (passive_SELECT == 2) {
passive_2();
}
}
/* When no motion is present, start a count down to a
Passive Animation Activation, after 15 seconds of no
motion, play a random Passive Animation: */
if (motion_detected == false) {
passive_TIMER += 1;
if (passive_TIMER == 300) {
passive_SELECT = random(1, 3);
passive_animation = true;
passive_TIMER = 0;
}
}
/* When Motion is detected: run Active Animation, start a
count up on how long motion is continuously detected and
reset the Passive Animation Timer: */
if (motion_detected == true) {
sensor_triggered();
trigger_length += 1;
passive_TIMER = 0;
/* Play small Animation if motion is continuously detected
for 10 seconds: */
if (trigger_length >= 200) {
servoA.write(75);
delay(500);
servoA.write(80);
trigger_length = 0;
}
// When motion is no longer detected, reset motor position:
} else if (motion_active == true) {
servoA.write(95);
motion_active = false;
}
/* Check Output from Arduino running Ultrasonic Sensor for a
signal indicating motion: */
sensor_output = digitalRead(dataPIN);
if(sensor_output == HIGH) {
motion_detected = true;
} else {
motion_detected = false;
}
/* Run Misleading Sensors Animation if no Motion is detected
and both sensors are inside their range: */
dummy_sensors();
if (motion_detected == false && passive_animation == false) {
if(dummyAtest == true && dummyBtest == true) {
servoA.write(75);
delay(750);
servoA.write(95);
delay(750);
servoA.write(75);
delay(750);
servoA.write(95);
delay(750);
}
}
delay(50); // Delay for Tick Speed (20 Ticks/Second)
}

// Activate Motor (Push Spring Down) when motion detected:
void sensor_triggered() {
servoA.write(75);
passive_TIMER = 0;
motion_active = true;
}

// Run Misleading Sensors:
void dummy_sensors() {
// Check and Remap (0-100) the Photoresistor Sensor Values:
sensorValueA = analogRead(AphotoPIN);
sensorValueB = analogRead(BphotoPIN);
sensorValueA = map(sensorValueA, 0, 700, 0, 100);
sensorValueB = map(sensorValueB, 0, 700, 0, 100);
constrain(sensorValueA, 0, 100);
constrain(sensorValueB, 0, 100);
/* Check if the Photoresistor Sensor Values are within the
desired range (turn LED on if that one is): */
if (sensorValueA >= dummyAlow && sensorValueA <= dummyAhigh) {
digitalWrite(RledPIN, HIGH);
dummyAtest = true;
} else {
digitalWrite(RledPIN, LOW);
dummyBtest = false;
}
if (sensorValueB >= dummyAlow && sensorValueB <= dummyBhigh) {
digitalWrite(GledPIN, HIGH);
dummyBtest = true;
} else {
digitalWrite(GledPIN, LOW);
dummyBtest = false;
}
}

// First Passive Animation (Drop, Wait, Lift Back Up):
void passive_1() {
if (passive_animation == true) {
for (int pos = 95; pos >= 75; pos -= 1) { // Push Lever Down
servoA.write(pos);
delay(20);
}
delay(1500); // Wait
for (int pos = 75; pos <= 95; pos += 1) { // Lift Lever
servoA.write(pos);
delay(20);
}
// Reset Passive Animation
passive_animation = false;
passive_SELECT = 0;
}
}

/* Second Passive Animation (Quick Drop, Wait, Slow Lift
Halfway, Wait, Lift Other Half) */
void passive_2() {
if (passive_animation == true) {
for (int pos = 95; pos >= 75; pos -= 1) { // Quick Push Lever Down
servoA.write(pos);
}
delay(2500); // Wait
for (int pos = 75; pos <= 85; pos += 1) { // Slowly Lift Lever Halfway
servoA.write(pos);
delay(50);
}
delay(2500); // Wait
for (int pos = 85; pos <= 95; pos += 1) { // Lift Lever Remain Distance
servoA.write(pos);
delay(20);
}
passive_animation = false;
passive_SELECT = 0;
}
}

Installing and Wiring Ultrasonic Sensor

Screenshot 2026-03-02 201429.jpg
IMG_6561.jpg
IMG_8364.jpg

With the Main breadboard and microcontroller wired, the Ultrasonic Sensor was the last step in the process. By creating a 'stair-like' ledge for the sensor and its breadboard to sit on, the sensor could be wired to the second microcontroller.

A second microcontroller was chosen in order to enhance processing time for both systems with a simple relay wire running between the two for communication. In this build, an Arduino Uno was chosen as the second microcontroller due to a lack of a second Arduino Nano R4, however this is not necessary and a second Nano would have also sufficed.

Wiring the Ultrasonic Sensor follows the diagram above, again particular emphasis was placed on the wires running between this breadboard and the main breadboard (notated in the diagram). Finally, code for the Ultrasonic Sensor (linked below) was uploaded to the microcontroller and troubleshooted.

Code for the Ultrasonic Sensor can be copied or downloaded below:

/* Arduino Controlling Ultrasonic Sensor

Utilizing a second Arduino for the sensor pulses
allows each controller to focus on processing its tasks
more efficiently (the nanosecond delays in the ultrasonic
sensor code do no interfere with operations of the main code).

*/

// Sensor and Connector Pins
const int trigPIN = 10;
const int echoPIN = 5;
const int dataPIN = 8;

// Variables Used in Distance Calculation
float dur = 0;
float dist = 0;

void setup() {
// Sensor and Connector Pin Setup
pinMode(trigPIN, OUTPUT);
pinMode(echoPIN, INPUT);
pinMode(dataPIN, OUTPUT);
// Serial.begin(9600); (For Troubleshooting)
}

void loop() {
digitalWrite(trigPIN, LOW); // Reset the Sensors
delayMicroseconds(2);
digitalWrite(trigPIN, HIGH); // Send Ultrasonic Signal
delayMicroseconds(10);
digitalWrite(trigPIN, LOW); // Stop Ultrasonic Signal
dur = pulseIn(echoPIN, HIGH); // Log Time Till Signal Returns
dist = (dur*.0343)/2; // Compute Distance from Time
/* Send signal to Main Arduino if something
is detected within 30cm: */
if (dist <= 30 && dist >= 1) {
digitalWrite(dataPIN, HIGH);
} else {
digitalWrite(dataPIN, LOW);
}
delay(100);
// Serial.println(dist); (For Troubleshooting)
}

Final Assembly

IMG_6564.jpg
ARC385 [Converted].png
IMG_6569.jpg
Screenshot 2026-03-02 221034.png

With the box's fabrication and wiring now finished, final checks of all the wiring (according to the full wiring diagram linked above) could be completed. In its finished state, the box retracts its cards whenever something enters within 30cm of it, as well as performing passive animations when no one is around or when the Misleading Sensors are triggered.

Reflection and Conclusion

Throughout this project, we encountered various challenges that shaped how our project was completed. In this process of analysis, troubleshooting and implementation, we feel that we have created a unique machine that, while 'useless' nevertheless creates interest and enjoyment (as well as quite a bit of frustration).

Obstacles:

Machine's Use: We originally designed the machine as a tissue box holder that would retract, though due to the non-rigid nature of the material (causing it to get caught beneath the lid when retracted down) we decided to switch to model tags that would provide a more rigid/stable object.

Use of Two Motors: Originally we planned to use two motors (a spring on each of two pistons with a motor controlling each side). Due to the 5v voltage limit of the Arduino Nano R4 we could only connect one Servo Motor without using external power. Because of this, the decision was made to utilize a single motor on one side, leaving the other cylinder in a sleeve to restrict its lateral movement.

Processing Power of a Single Arduino: During TinkerCAD simulations it was discovered that a single microcontroller struggled to process the Ultrasonic Sensor's data along with the main system code (due to the use of nanosecond delays between sending and receiving signals). As a solution, a second microcontroller was added to control only the Ultrasonic Sensor and relay its results to the main microcontroller using binary data.

Wiring Layout: While constructing the machine, we encountered many obstacles in wiring the various components due to the large nature of the machine. By splitting the wiring across three breadboards we were able to achieve three main wiring points which would then be related back to a single controller thereby simplifying the wiring process.

Lever Arm Rotation: In calibrating the motor arm's rotation we encountered various problems in over/under rotating the arm as well as in rotating it too slowly causing the motor to jam due to built up friction and elastic energy from the spring.

Reflection:

This project gave us the opportunity to experiment with sensors as a way of creating an interaction between a user an an object. Our research into sensor calibration (both for Photoresistors and Ultrasonic Sensors) will prove invaluable in future projects with increased complexity.

The project's exploration of creative ways of translating rotational movement into linear movement was also extremely interesting and yielded unconventional results centered around randomness that we found extremely compelling.

Finally, through this project we were able to explore ways of creating small movement and environmental interactions (through passive animations) that allowed us to learn how motion can be used as a storytelling tool to create something that feels alive through simple motors.


We found this project to be an extremely compelling way of testing our ability to realize a concept using simple electronics and mechanical systems. We feel that this project has given us the opportunity to expand our knowledge of what is possible using physical computing beyond its traditional uses.