No-More-Candy-inator 3000
Ben Nguyen Tran
Ben Nguyen Tran
Front view of the No-More-Candy-inator 3000
To give context, I eat too much candy at home, so I wanted to make a device that could help limit the amount of candy I eat. This project increases the barrier to entry for candy by only dispensing candy when I solve a Rubiks cube within a certain amount of time. Once a person solves the Rubik's cube they would place it on the force sensor, which detects how long it has been since the Rubik's cube has been picked up, only flipping a servo to dispense candy out if they solved it within the time limit.
View of the platform for the force sensor and LCD ongoing without any object on it.
View of the platform for the force sensor and LCD ongoing with an object on it.
Bottom of the container, showing the servo while closed.
Bottom of the container, showing the servo while open.
Top down view of the inside of the container.
In this video, I show the behavior of the dispensing when an Rubiks cube (the charger cube is a placeholder for a Rubiks cube) is solved within the time limit of 10s. The LCD will say "Finished" with the time elapsed, and the servo would flip open while the cube is on.
In this video, I show the behavior of the dispensing when an Rubiks cube (the charger cube is a placeholder for a Rubiks cube) is solved longer than the time limit of 10s. The LCD will say "Too Slow!" with the time elapsed, and the servo would stay closed.
This is the behaves-like prototype where touching the force sensor would cause the servo to move regardless of time elapsed. The LCD screen would keep track of the time elapsed, but no logic is connected to it.
The first fusion model for the candy container. It did not have space for a lot of candy and also did not have space for the candy to be dispensed from because the hole at the bottom would be covered by the floor/surface it is resting on.
Adjusted fusion model to have a stand for the candy to have space to be dispensed.
Laser cut the adjusted fusion model and assembled the entire container together. It was also relatively hard to put together because there are so many joints to consider and making sure the correct faces are facing outward.
The entire container and arduino put together (before a platform for the force sensor was made). Since the servo is attached to the bottom, it tended to sag and fall off.
I wasn't quite satisfied with the project because I wanted to also make sure the Rubiks cube was solved when it is placed on the force sensor, but that task turned out to be more difficult than I had thought, so I decided to not include it. Instead, I just detected whether the side facing the color sensor is yellow because yellow tends to be the last face I solve. Changing this would require scoping my projects a bit better because detecting the Rubik's cube solve state is quite hard with just an Arduino. However, I did enjoy coding up the timing and getting it working, so I'll most likely get a behaves-like prototype finished early for future projects.
Also, I've learned that I'm quite bad at physical fabrication. It took me quite a few tries to even get a working fusion model, so I would probably need to get working on that even sooner. Once crituque on the physical aspect I got was to change the shape of the container to be spherical or at least slanted to guide the candy towards the dispensing hole better, which I agree would be better because currently, the candy would have to be positioned in a particular way for the device to work multiple times. Although I most likely won't be continuing this project, if I would, this would be one of the first things I would change. I would increase the size of the hole for the candy to drop through. I wished I has closely measured the sizes of candy more carefully because the wrappers would often get stuck on the hole. Another critique was to move the platform for the force sensor on the lid so that everything is one component, but I think that would be difficult given that it would make the force sensor harder to reach and be more unstable compared to leaving it on the surface next to the candy container.
One last improvement I would make if I continued the project would be making a better platform for the force sensor so that it can more accurately detect forces on it. Maybe I would make it in the shape of a hollow cube so the Rubik's cube could fit closely.
No-More-Candy-inator 3000
Ben Nguyen Tran
The force sensor would take in any force and determine whether the time between presses are less than or greater than the time limit.
If it is greater than the time limit, do not open the servo.
If it is within the time limit, check if the face is yellow-ish. If it is, open the servo, keep the servo closed otherwise.
Servo open and close are at 0 degrees and 180 degrees respectively.
PIN MAPPING:
item | pin | mode
--------------------------------
Force Sensor | A0 | INPUT
Servo | 3 | OUTPUT
Color Sensor | SDA | N/A
Color Sensor | SCL | N/A
I2C LCD | SDA | N/A
I2C LCD | SCL | N/A
#include <Wire.h>
#include <Servo.h>
#include <LiquidCrystal_I2C.h>
#include "Adafruit_APDS9960.h"
Servo motor;
LiquidCrystal_I2C screen(0x27, 16, 2);
Adafruit_APDS9960 apds;
// PINS
const int FSR_PIN = A0;
const int SERVO_PIN = 3;
// TIMING
const int TIME_LIMIT = 10; // time limit in seconds
unsigned long time_elapsed = 0; // time that has elapsed since rubiks cube start
// for lcd
unsigned long ONE_SECOND_WAIT = 1000;
unsigned long next_time_to_run = 0;
// OTHER
bool is_start = false; // flag to detect whether the solve has started
enum Status {
ONGOING,
DONE,
SLOW,
INIT
};
enum Status result_status = INIT; // result status to indicate ongoing, finished or slow
const int force_threshold = 50; // if a force > force_threshold, then do actions
void setup_screen() {
screen.init();
screen.backlight();
screen.home();
screen.print("Time:");
screen.setCursor(10, 0);
screen.print("s");
}
void setup() {
// pin setup
pinMode(FSR_PIN, INPUT);
motor.attach(SERVO_PIN);
// reset motor position
motor.write(0);
delay(500);
// timing setup
next_time_to_run = millis() + ONE_SECOND_WAIT;
// color sensor
if(!apds.begin()){
Serial.println("failed to initialize device! Please check your wiring.");
}
else Serial.println("Device initialized!");
apds.enableColor(true);
setup_screen();
Serial.begin(9600);
}
void update_screen(unsigned long time_elapsed, unsigned int result_status) {
// Update timer
if (time_elapsed < TIME_LIMIT) {
screen.setCursor(8, 0);
screen.print(0);
screen.setCursor(9, 0);
screen.print(time_elapsed);
} else {
screen.setCursor(8, 0);
screen.print(time_elapsed);
}
// Update type of finish
screen.setCursor(0, 1);
if (result_status == INIT) {
screen.print(" ");
} else if (result_status == ONGOING) {
screen.print("Ongoing...");
} else if (result_status == DONE) {
screen.print("Finished ");
} else if (result_status == SLOW) {
screen.print("Too slow! ");
}
}
void loop() {
// Step 1: Gather inputs
int force = analogRead(FSR_PIN);
Serial.println(force);
// create some variables to store the color data in
uint16_t r, g, b, c;
// Step 2: Do computations
if (is_start) {
if (force > force_threshold) { // if the solve has started, check for a force to imply finished solving
// checking for colors:
//wait for color data to be ready
while(!apds.colorDataReady()){
Serial.println("bruh");
delay(5);
}
apds.getColorData(&r, &g, &b, &c);
if (r > b && g > b) { // check for mostly yellow
if (time_elapsed < TIME_LIMIT) { // if solved within correct time
Serial.println("Finished!");
result_status = DONE;
} else { // if took too long to solve
Serial.println("Too long!");
result_status = SLOW;
}
is_start = false;
}
}
} else {
if (force <= force_threshold) {
is_start = true;
// reset values (time elapsed, result_status)
time_elapsed = 0;
result_status = ONGOING;
}
}
// Step 3: Drive outputs
if (result_status == DONE) {
motor.write(180);
delay(500); // delay to make sure we don't update due to fluctuations and let motor run
} else if (result_status == SLOW) {
motor.write(0);
delay(500); // delay to make sure we don't update due to fluctuations and let motor run
} else {
motor.write(0);
// no delay to keep millis() consistent
}
// Step 4: Update LCD
if (millis() >= next_time_to_run) {
if (is_start && force <= force_threshold) { // only update the time if we started a solve, but haven't placed it back
time_elapsed += 1;
}
update_screen(time_elapsed, result_status);
next_time_to_run = millis() + ONE_SECOND_WAIT;
}
delay(10);
}