This class teaches students how to design and fabricate mechanisms that utilize Arduino/small electrical components. This project was intended to allow students to interview a client with a disability and then with their help find an opportunity to improve their day-to-day life with a product. You can see how our interview went here.

What We Built:

Our project is designed to allow our client Allie to type a price into a keypad and then have the amount of money they need to be dispensed to them as well as the amount of change left over displayed. This cash dispenser can be attached to our client’s wheelchair. There is also a reload mechanism that allows our client to reload the machine with money. Our specific device was designed to dispense $10 and $20 bills. 

A general view of our machine. It is a pink box with two sets of money rollers inside. Each roller compose of two tubes covered and connected by a black fabric with two motors attached to the right side.

Infrared Sensors are depicted on the left and Infrared Detectors are shown on the right. These pair of sensors get blocked by the bills dispensed by the module and once the bill stops blocking the infrared light, the Arduino adds one to the amount of bill dispensed.

An LCD display is shown to the left with “price: $” printed on it, a keypad installed in a pink box is shown to the right with the numbers 0 to 9, a star key, and a pound sign arranged onto it in a 3 by 4 matrix. When a key is pressed on the keypad, the display shows the updated number and when done, the user presses the pound sign to enter the price needed to be dispensed. The star key is used to delete unwanted numbers.

A red push button is placed to the right of the money roller. When this button is pressed, both yellow motors attached to the rollers will begin driving (the top one drives faster than the bottom to ensure the fabric doesn’t create slack between the rollers) to reload the money.

A bill is shown being dispensed by the money roller module. The module is made with two tubes and a black fabric that wraps around both of them. Bills are stored between the rolled-up fabric and motors are driven to dispense and reload the bills.

A video that demonstrates the entire process of dispensing money from the view of the user.

A video that demonstrates the entire process of dispensing money from the view of the shop clerks.

Narrative Sketch:

When Allie goes out shopping and is about to pay for her groceries, she would turn to the Money Dispenser sitting on her desk on top of the wheelchair. She types in the price of the item using the keypad and the motors run and dispense the correct number of 10- and 20-dollar bills. The display screen reads the change Allie would get back. When Allie gets home, she and her helper would unlock the cabinet in the front, and then press the red button which says reload 10s and reload the 20s to reload the machine for purchases in the future.

How we got here:

Prototype:

This prototype was designed to help answer the question: how to most effectively dispense only one bill at a time?

Our first prototype was based on how a printer dispenses paper and utilized multiple roller mechanisms that would dispense a bill from a stack. Basically a spring would push up a stack of bills to a certain height and then a rolling cylinder with grippy material would push the bill forward. We then moved on to our second prototype which is the design that we ended up using with two rollers and fabric pulled between them. This design is essentially a piece of fabric taped to two roller and as its winded, bills are inserted between the folds of the fabric. The fabric is then unwound to dispense the bills.

Our original bill-dispensing prototype

 

The first prototype required us to make a spring which we did by wrapping wire around a clamp.

 

First rolling mechanism with only two motors and one set of rollers.

 

Our first prototype of the bill dispensing mechanism was not able to consistently roll out a single bill at a time.

 

Prototype of the new bill dispensing mechanism without fabric attached

Testing the new bill-dispensing mechanism

 

Assembling our prototype for our final design

Most of our prototyping process was spent figuring out if it was possible for us to create a money dispenser, specifically one that would only dispense one bill at a time. We first modeled our mechanism after a printer that utilized a set of rollers that would only allow one bill to pass through at a time. However, we found that this mechanism required high precision tolerance between the rollers which was difficult for us to consistently achieve. We then switched to our final mechanism which instead of storing the bills in a stack where they could stick together, layered the bills separately inside a roll of fabric.

We received feedback that our original mechanism was flawed because we did not have a way to detect if the correct number of bills had been dispensed, which we solved by integrating IR sensors into the design. However, we weren’t able to react to the feedback to make the mechanism smaller due to how much testing that would require and the time limit we were under. The biggest (good) surprise we encountered was how smoothly our second mechanism worked after having so much difficulty with tweaking the first one.

Process:

a photograph of a table with various parts for constructing the bill rollers. There are a couple of toilet paper tubes, rectangular pieces of acrylic with two holes in each, a hot glue gun, and a couple clear acrylic rings.

Constructing the bill rollers using laser-cut and 3D-printed parts

Two fully-constructed bill rollers. These rollers are made from toilet paper tubes wrapped with black nylon fabric, and have gray 3D-printed connectors on the end to pair with the toy gear box motors.

Fully constructed bill rollers using standardized 3D-printed and laser-cut parts.

a white board table with a grid partially filled in with numbers corresponding to keys on a keypad. There is also a laptop attached to a microcontroller and keypad where I am tapping keys to make sure the right number is appearing on the computer.

Figuring out the row and column pin-mapping for the keypad.

Wiring the IR receiver and transducer on a breadboard for initial readings

 

This image includes our hardware testing set-up. There is a long piece of cardboard with many electrical components and wires such as motors, a keypad, a microcontroller, and IR sensors.

Integrating multiple pieces of hardware into a circuit with the Arduino Mega.

A view of the back panel of our box. The LCD display has a hole cut out for it on the upper left side, there is a hole for the keypad wires on the lower left side, and there is an opening for the power cord on the lower right side.

Constructing the back panel of the box

The biggest change in our design moving forward from our prototype presentation was switching our bill dispensing mechanism to a simpler two-roller setup. We found a mechanism online that used two rollers driven by motors to reload and dispense bills from a spool of fabric. This mechanism gave us more control over how many bills were released at once. It was also more appropriate for Allie’s needs since she told us that she gets about 60 dollars each week in the form of 3 twenty-dollar bills, so she didn’t necessarily need a device that could fit a large stack of bills. 

We also had to make a decision about how our device could track the number of dispensed bills from each compartment. Initially, we planned to time how long it would take to dispense a single bill, but that would require the bills to be evenly spaced out when loaded into the rollers. Instead, we chose to use an IR transmitter and receiver that detects a decrease in the IR signal as a bill exits the money rollers. Similarly, we had to find a method to determine when a given compartment was out of bills. At first we planned to use an RGB color sensor to detect a color change when a roller reached a white piece of tape at the end of the fabric. However, we ended up using an IR sensor so that we would only have to work with one signal value rather than the three values that an RGB sensor outputs. 

Our biggest breakthrough was figuring out how to prevent fabric slack from bunching up between the two rollers when running the motors. We found the best way to maintain good tension in the fabric roll was to drive the bottom roller faster than the top roller while dispensing and drive the top roller faster while reloading. This allowed us to smoothly and consistently dispense/reload bills. 

One mistake we made was waiting too long to start arranging our pieces of hardware into the outer shell of the box. We spent a lot of time fine-tuning the IR sensor thresholds outside the final cash box. This took up a lot of time, and we had to redo the IR signal thresholds in our code anyway because the box provided a much darker environment. If we had put our entire device into the box earlier on, we would have still been able to fine-tune the performance of our hardware and would have had more time for wire management inside the final form.

Gantt Chart Schedule:

Gantt chart timeline from prototype critique until final presentation.

Our team followed the sequence of tasks on the Gantt chart pretty closely. However, we underestimated how long it would take us to complete certain tasks. There ended up being a lot more hardware than initially expected, so the process of wiring, writing code, and introducing each component into the circuit took longer than expected. Rather than finishing the “implementing circuit” phase by 4/24, we didn’t finish integrating all the electronics and the roller mechanisms until 4/29. This only gave us 3 days to put all of our hardware into the outer box.

Conclusions and Lessons Learned:

This project has been a difficult but rewarding experience. We have run into a lot of problems during every stage of the process, including revamping the entire mechanism after the prototype review, but we were able to put together the mechanism in the end. The box itself during the final review still had a lot of room for improvement. As pointed out by comments such as “the fabrication is flimsy with a less-than-ideal UI”, “a bit big”, and “[Allie] can’t see what comes out”, the fabrication was a last-minute process and had flaws such as its size and fragility. We also planned on making a locked cabinet in the front but couldn’t get around to installing it. Additionally, the box orients itself away from Allie which prevents her from seeing the money being dispensed. A great solution that our critic brought up was adding a “swivel” to the bottom of the device so that it can “turn towards Allie” first, then when Allie makes sure it is the right amount, she can turn the machine towards the shop staff. Moreover, the problem of size was also discussed heavily in the critique. One critic suggested using “bill recognition” and some forms of servo motor that directs the money out or back into the loop so that we can use one module for all kinds of bills instead of two modules for two types of bills. This will not only increase the utility of the machine but also downsize it to a reasonable and usable form. Another critic pointed out is the messy “inner components” that should be organized which might reduce the size of the box further. However, despite the shortcomings, our project still got some praise for tackling such a difficult problem, dealing with single sheets of paper. Some critics said that the “motor system works well” and contains “clever paper handling” which we are happy to see.

Working with Allie has been a phenomenal experience as she was extremely helpful during our creation process. Allie shared us some hobbies with us and even some of the posters she made in her free time. We were able to learn to incorporate ergonomic designs into the project that caters to Allie’s specific needs, and through that, we learned more about disability beyond the project as we realized that disability is not just simply a medical definition but also a social problem. Some items and facilities are simply not designed for those with different needs and people are handicapped by the products and that is an external problem instead of an internal one. In the future, we will ask more questions working with people with disabilities and have more meetings with them to get a better sense of the user’s experience.

Working on Zoom certainly brought up some difficulties during the interview as Allie had some issues with her microphone, but overall, we as a team were able to get together in person for the most part and work in the Physical Computing lab together without any issues.

Photo with Allie at the final critique.

Technical Details:

Cash dispenser electrical schematic

Cash dispenser block diagram

Code:

/* Money Box Code - Andy Jiang, Cassie Rausch, Lynn Rushkin
 *  
 * How it works:
 *  Use the keypad to enter the price you would like to pay:
      '#' = enter (E)
      '*' = delete (D)
      type in your price as dollars and cents (i.e. 1600 --> $16.00, 4567 --> $45.67)
      if you mess up a digit then click '*' to delete the last digit
      press # when you have your price entered
   The code calculates how many 10 and 20 dollar bills need to be dispensed. As the device dispenses the bills, "dispensing..." will show up on LCD display. The IR transducer/receiver pair on the $10 and $20 compartments will detect how many bills have been dispensed and once the correct amount has been paid the motors of the compartments will stop running. The change amount is calculated based on how much was paid and the entered price. This amount will pop up on the LCD display. If one bill compartment is empty(detected by IR sensor)then the number of bills needed will be recalculated and dispensed from the compartment that still has bills. 

   Sources:
   1. Keypad code from Terrance Ou's "Math Buddy" code (https://courses.ideate.cmu.edu/60-223/s2022/work/math-buddy/)
   2. LCD display code from https://courses.ideate.cmu.edu/60-223/s2022/tutorials/I2C-lcd

  //----------------pin mapping ----------------------
  Pin mapping:
    Arduino Mega pin | type   | description
    -----------------|--------|-------------
    A1                 input     IR receiver 
                                 (detect dispensed $20 bills)
    A2                 input     IR receiver 
                                 (detect dispensed $10 bills)
    A4                 input     IR sensor 
                                 (detects empty $10 bill compartment) 
    A5                 input     IR sensor 
                                 (detects empty $20 bill compartment) 
    2                  output    toy gear box motor 1 
                                 (top roller of $10 bill compartment)
    3                  output    toy gear box motor 1 
                                 (top roller of $10 bill compartment) 
    5                  output    toy gear box motor 2 
                                 (bottom roller of $10 bill compartment)
    6                  output    toy gear box motor 2 
                                 (bottom roller of $10 bill compartment)
    9                  output    toy gear box motor 3 
                                 (top roller of $20 bill compartment)
    10                 output    toy gear box motor 3 
                                 (top roller of $20 bill compartment)
    11                 output    toy gear box motor 4 
                                 (bottom roller of $20 bill compartment)
    12                 output    toy gear box motor 4 
                                 (bottom roller of $20 bill compartment)
    25                 input     button 1 (for reloading $20 bills) 
    27                 input     button 2(for reloading $10 bills)
    39                 input     keypad (COL 2)
    41                 input     keypad (ROW 4)
    43                 input     keypad (COL 3)
    45                 input     keypad (ROW 1)
    47                 input     keypad (COL 1)
    49                 input     keypad (ROW 2)
    51                 input     keypad (ROW 3)
    SDA                          LCD display
    SCL                          LCD diaplay

   Note: toy gear box motors are connected to Arduino Mega through a dual motor driver
*/

//--------------initialize LCD----------------------
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C screen(0x27, 16, 2);

//---------------------initialize keypad---------------------
#include <Keypad.h>
const byte ROWS = 4; // four rows
const byte COLS = 3; // three columns

char keys[ROWS][COLS] = {
  {'1', '2', '3'},
  {'4', '5', '6'},
  {'7', '8', '9'},
  {'D', '0', 'E'}
};


byte rowPins[ROWS] = {45, 49, 51, 41}; // connect to the row pinouts of the keypad
byte colPins[COLS] = {47, 39, 43};    // connect to the column pinouts of the keypad


Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );

//----------- initialize motors ----------------------------------
//pins for 20s top
int motorControlPin1 = 12;
int motorControlPin2 = 11;

//pins for 20s bottom
int motorControlPin3 = 2;
int motorControlPin4 = 3;

//pins for 10s top
int motorControlPin5 = 5;
int motorControlPin6 = 6;

//pins for 10s bottom
int motorControlPin7 = 9;
int motorControlPin8 = 10;

//---------------initialize IR receivers for bill detection ------------------
const int IRPin_20s = A1;
const int IRPin_10s = A2;
unsigned long LastIRTime;

int IRVal_20s; //reading of the sensor (0 to 1023)
bool currentIR_20s = 0; //is the IR sensor covered currently? (1 = yes, 0 = no)
bool prevIR_20s = 0; //was the IR sensor covered previously? (1 = yes, 0 = no)

int IRVal_10s;
bool currentIR_10s = 0;
bool prevIR_10s = 0;

int dispensedTwenties = 0; //counts how many bills have been dispensed from 20s compartment
int dispensedTens = 0;

//-------------initialize IR sensor variables for empty compartment detection -------
const int tenIR_empty = A4;
const int twenIR_empty = A5;
int tenIR, twenIR;
bool EmptyModule_20s = 0; //is the $20 compartment empty? (1 = yes, 0 = no)
bool EmptyModule_10s = 0;

// -----------initialize reload buttons ------------------------
const int tens_button = 27;
const int twens_button = 25;

int twens_buttonVal; //on or off values of the button reading
int tens_buttonVal;

//----------- initialize bill calculating variables --------------
char key; //keypad reading
String price = "";
float finalPrice = 0;
int billsTwenties = 0; //number of $20 bills we need to dispense
int billsTens = 0; //number of $10 bills we need to dispense
float change = 0;

// -----------calibrate these values -----------------------------

//motor speed dispense
const int twenTopSpeed_dispense = 100;
const int twenBotSpeed_dispense = 140;

const int tenTopSpeed_dispense = 100;
const int tenBotSpeed_dispense = 140;

//motor speed reloading
const int twenTopSpeed_reload = 150;
const int twenBotSpeed_reload = 80;

const int tenTopSpeed_reload = 150;
const int tenBotSpeed_reload = 60;

/*IR bill dispense threshold - any IR reading below these values means
a bill is passing */
const int twenBills_threshold = 50;
const int tenBills_threshold = 60;

/*IR empty module threshold - any IR reading above these values means 
the bill compartment has been emptied.*/
const int empty_threshold20s = 200;
const int empty_threshold10s = 180;

//------------setup and main loop---------------------------------

void setup() {
  Serial.begin(9600);

  // setup LCD
  screen.init();
  screen.backlight();
  screen.home();

  pinMode(motorControlPin1, OUTPUT);
  pinMode(motorControlPin2, OUTPUT);

  pinMode(motorControlPin3, OUTPUT);
  pinMode(motorControlPin4, OUTPUT);

  pinMode(motorControlPin5, OUTPUT);
  pinMode(motorControlPin6, OUTPUT);

  pinMode(motorControlPin7, OUTPUT);
  pinMode(motorControlPin8, OUTPUT);

  pinMode(tenIR_empty, INPUT);
  pinMode(twenIR_empty, INPUT);
  pinMode(IRPin_20s, INPUT);
  pinMode(IRPin_10s, INPUT);
  pinMode(twens_button, INPUT);
  pinMode(tens_button, INPUT);
}

void loop() {
  enteredPrice();
  if (key == 'E') {
    delay(1000);
    calculateBills();
    dispenseBills();
    stop10s_motor();
    stop20s_motor();
    delay(1000);
    calculateChange();
    resetVariables();
  }
 
  checkReload();
}

//------------functions -------------------------------------
void clearLastInput() {
  int lastIdx = price.length() - 1;
  screen.setCursor(lastIdx + 8, 0);
  screen.print(" ");
}

void enteredPrice() { //displays the price typed into the keypad on LCD
  screen.home();
  screen.print("price: $");
  key = keypad.getKey();
  if (key != NO_KEY
      && key != 'D' && key != 'E'
      && price.length() < 20) {
    price += key;
    screen.setCursor(8, 0);
    screen.print(price);
  }
  if (key == 'D') {
    clearLastInput();
    price.remove(price.length() - 1); //removes the last digit from the price string
  }

  else if (key == 'E') {
    finalPrice = price.toFloat() / 100;
    screen.setCursor(8, 0);
    screen.print(finalPrice);
    price = "";
  }
}

void calculateBills() { //calculates the number of 10 and 20 dollar bills to ideally dispense
  billsTwenties = finalPrice / 20; //integer divide to get how many twenties we need to dispense
  float remainder = finalPrice - 20 * billsTwenties;
  if (remainder > 10) {
    billsTwenties += 1;
  }
  else if (remainder > 0 ) {
    billsTens = 1;
  }

  /*Serial.print("bills twenties:");
  Serial.println(billsTwenties);
  Serial.print("bills Tens:");
  Serial.println(billsTens);*/
}

void run20s_motor() { //drives the top and bottom motors of the $20 compartment to dispense
  //do an initial acceleration (at highest speed) to overcome internal gear resistance, 
  //and then drive the motors at a reasonable speed
  
  if (digitalRead(motorControlPin1) == 0 && digitalRead(motorControlPin4) == 0) {
    analogWrite(motorControlPin1, 255);
    analogWrite(motorControlPin4, 255);
    delay(300);
  }

  analogWrite(motorControlPin1, twenTopSpeed_dispense);
  analogWrite(motorControlPin2, 0);

  analogWrite(motorControlPin3, 0);
  analogWrite(motorControlPin4, twenBotSpeed_dispense);

}
void stop20s_motor() {
  analogWrite(motorControlPin1, 0);
  analogWrite(motorControlPin2, 0);

  analogWrite(motorControlPin3, 0);
  analogWrite(motorControlPin4, 0);
}
void run10s_motor() {
  if (digitalRead(motorControlPin6) == 0 && digitalRead(motorControlPin8) == 0) {
    analogWrite(motorControlPin6, 255);
    analogWrite(motorControlPin8, 255);
    delay(300);
  }
  analogWrite(motorControlPin5, 0);
  analogWrite(motorControlPin6, tenTopSpeed_dispense);

  analogWrite(motorControlPin7, 0);
  analogWrite(motorControlPin8, tenBotSpeed_dispense);

}

void stop10s_motor() {
  analogWrite(motorControlPin5, 0);
  analogWrite(motorControlPin6, 0);

  analogWrite(motorControlPin7, 0);
  analogWrite(motorControlPin8, 0);
}

void checkIR() { //updates boolean values of whether a bill is passing
  //check IR readings in $20 compartment
  IRVal_20s = analogRead(IRPin_20s);
  if (IRVal_20s < twenBills_threshold) {
    currentIR_20s = 1;
  }
  else {
    currentIR_20s = 0;
  }

  //check IR in $10 compartment
  IRVal_10s = analogRead(IRPin_10s);
  if (IRVal_10s < tenBills_threshold) {
    currentIR_10s = 1;
  }
  else {
    currentIR_10s = 0;
  }
}

void count_billsdispensed() { /*uses the change in currentIR_10s value from 1 --> 0 
  to determine that a bill has passed*/
  if (millis() - LastIRTime >= 400) {
    LastIRTime = millis();
    checkIR();
    if (currentIR_20s == 0 && prevIR_20s == 1) {
      dispensedTwenties += 1;
    }
    prevIR_20s = currentIR_20s;
    if (currentIR_10s == 0 && prevIR_10s == 1) {
      dispensedTens += 1;
    }
    prevIR_10s = currentIR_10s;
    /*
      Serial.print("dispensed tens: ");
      Serial.println(dispensedTens);
    */
  }
}

void moduleStatus() { //check if either bill compartment has been emptied
  tenIR = analogRead(tenIR_empty);
  twenIR = analogRead(twenIR_empty);
  //Serial.println(tenIR); // push the most recent value to the computer
  if (tenIR >= empty_threshold10s) {
    EmptyModule_10s = 1;
  }
  if (twenIR >= empty_threshold20s) {
    EmptyModule_20s = 1;
  }
}

void dispenseBills() {
  screen.clear();
  screen.home();
  screen.print("dispensing...");
  
  //the following while loop will continue to run as long as there are bills needed to be dispensed to pay the entered price and at least one bill compartment hasn't been emptied
  while ((billsTwenties > dispensedTwenties or billsTens > dispensedTens) && (EmptyModule_20s == 0 or EmptyModule_10s == 0)) {
    if (billsTwenties > dispensedTwenties and EmptyModule_20s == 0) {
      run20s_motor();
    }
    if (billsTwenties > dispensedTwenties and EmptyModule_20s == 1) {
      stop20s_motor();
      billsTens += 2 * (billsTwenties - dispensedTwenties);
      billsTwenties = dispensedTwenties;
    }
    if (billsTwenties == dispensedTwenties) {
      stop20s_motor();
    }
    if (billsTens > dispensedTens and EmptyModule_10s == 0) {
      run10s_motor();
    }
    if (billsTens > dispensedTens and EmptyModule_10s == 1) {
      stop10s_motor();
      billsTwenties += ((billsTens - dispensedTens) / 2) + 1 ;
      billsTens = dispensedTens;
    }
    if (billsTens == dispensedTens) {
      stop10s_motor();
    }
    //check whether the 10s or 20s modules are empty
    moduleStatus();

    //check how many 10s and 20s have been dispensed
    count_billsdispensed();
  }
}

void calculateChange() {
  change = (dispensedTwenties * 20 + dispensedTens * 10) - finalPrice;
  screen.clear();
  screen.setCursor(0, 0);
  screen.print("change: $");
  screen.setCursor(9, 0);
  screen.print(change);
  delay(3000);
  screen.clear();
}

void checkReload() { //checks if buttons are being pressed. If so, then motors of the bill compartment that correspond to the button will be driven in the opposite direction to reload money into the rollers. 
  twens_buttonVal = digitalRead(twens_button);
  tens_buttonVal = digitalRead(tens_button);
  if (twens_buttonVal == 1) {
    if (digitalRead(motorControlPin2) == 0 && digitalRead(motorControlPin3) == 0) {
      analogWrite(motorControlPin2, 255);
      analogWrite(motorControlPin3, 255);
      delay(300);
    }

    analogWrite(motorControlPin1, 0);
    analogWrite(motorControlPin2, twenTopSpeed_reload);

    analogWrite(motorControlPin3, twenBotSpeed_reload);
    analogWrite(motorControlPin4, 0);

  }

  if (twens_buttonVal == 0) {
    stop20s_motor();
  }

  if (tens_buttonVal == 1) {
    //10
    if (digitalRead(motorControlPin5) == 0 && digitalRead(motorControlPin7) == 0) {
      analogWrite(motorControlPin5, 255);
      analogWrite(motorControlPin7, 200);
      delay(300);
    }

    analogWrite(motorControlPin5, tenTopSpeed_reload);
    analogWrite(motorControlPin6, 0);

    analogWrite(motorControlPin7, tenBotSpeed_reload);
    analogWrite(motorControlPin8, 0);
  }
  
  if (tens_buttonVal == 0) {
    stop10s_motor();
  }
}

void resetVariables() {
  key;
  price = "";
  finalPrice = 0;
  billsTwenties = 0;
  billsTens = 0;
  change = 0;
  dispensedTwenties = 0; //counts how many bills have been dispensed from 20s compartment
  dispensedTens = 0;
  EmptyModule_20s = 0;
  EmptyModule_10s = 0;
}

Design Files

Money box case zip

Money dispenser zip

Keypad box