Introduction: Automatic Espresso Loader

Problem
For the personal semester project, I chose to create a machine that will load and compact the coffee grounds holder in an espresso machine. To load the machine manually, first you remove the holder (which we will call the “pot”).

Afterwards, the coffee grounds are scooped in until the pot is filled to the appropriate height (either 2 or 4 shots of espresso). The grounds are then compacted using a flat surface. The loaded pot is then secured in the espresso machine in order to begin the process.

The aim of the machine is to automate the process, while adding the flexibility of selection from three different coffee flavors.

Requirements
The machine will need three hoppers to hold the three different flavors of coffee grounds. Each of these hoppers will be controlled by some type of valve. The machine will also include some type of mechanism to compact the grounds afterwards. To take the selection, some type of user interface will also be provided, and a microcontroller to run the show.

Step 1: The Machine

The Design
The machine I created revolves around the Parallax Propeller MSR1 board. The board includes handy features to interface with accessories (i.e. Servos, LCD, etc) that use the standard 3-pin connection (power, ground, signal). It also includes an on-board H-bridge to control external DC motors. And of course, it comes with voltage regulation.




To use the machine, the pot is first secured in the pot holder. The clip is used to secure the pot in the circle on the base (made of hot glue). After the pot is secured, the machine can be turned on using the red power button underneath the keypad. The screen will then prompt the user for the flavor (1-3) and the number of espresso shots (2 or 4). The user then presses OK, which starts the dispensing sequence.
The machine first moves the chute into position on top of the pot, and then the appropriate valve is opened depending on the flavor selected. The valve is held open for a pre-determined amount of time, depending on whether the user selected 2 or 4 shots. After the pot is full, the chute is moved out of the way of the compacter. The compacter then comes down onto the pot, compacting the coffee grounds. The sequence is then complete, and the user takes out the pot to be used in the espresso machine. Refer to the detailed subsystems below for more information.

Step 2: Hoppers

Hoppers
The hoppers, made from plastic liquid bottles, hold the three flavors of coffee. The flavors are actuated by the three servo motors on the bottom, which rotate a valve to control the flow of coffee grounds.

Step 3: Compactor

Compactor
The compactor is a repurposed ink cartridge carrier from a dead inkjet printer. It has a circular head on the bottom to compact the grounds. The compactor is driven by a DC motor on the bottom, providing power through a belt system. There is a limit switch on the top to tell the microcontroller when the compacter has reached the upper limit.

Step 4: UI/Microcontroller

User Interface / Microcontroller
The user interface consists of a custom build keypad with the flavors, # of shots, OK and Cancel buttons. The microcontroller communicates with the user using the 2-line LCD screen above the keypad. Power to the system is actuated by the red push button below the keypad. Finally, the show is run by the Parallax Propeller board shown below. The board, servos, and Dc motor are all powered by the 11.1v 3200 mAh Li-Po battery.

Step 5: Software/Control

Software / Control
As the mentioned above, the system is controlled by the Parallax Propeller MSR1 board, running a custom SPIN program.

The software’s algorithm can be broken down in a linear fashion. There was no need to have simultaneous routines running, and so only one cog was utilizes for simplicity’s sake. The program first defines constants, which mostly include the pins for the servos, pushbuttons, and DC motor. LCD information and clock frequency are also set.

Next, three objects are called: Servo32v5, the serial terminal, and the debug_lcd. The servo object is used for easy servo control (no need to fuss with timing pulses). The lcd object is used to initialize and write to the LCD screen. Finally, the serial terminal is used for diagnostic purposes, and has no role in the final product.

Next, the two variables that the user chooses are defined: the flavor and the shot size.

Finally, the main program can begin. The servos are first set to the closed position, then the LCD displays a welcome message.

Afterwards, the flavor subroutine is called, which then prompts the user for the flavor. After the appropriate key press is detected, the shot subroutine is called, which prompts the user for the shot size. After the appropriate key press is detected, the program waits for the OK button to be pressed. After the OK is detected, the dispense subroutine is called. The dispense subroutine then sets the chute to the dispensing position, opens the appropriate valve for the appropriate time, then closes the valve. Afterwards, the compact subroutine is called, which moves the chute out of the way of the compacter, and calls the compacter twice. Afterwards, a “done” message is displayed, and the routine is finished.

Step 6: Problems/Praises

Problems/ Praises
The main problem encountered while building this machine is intermittent interference with the servos. The most likely suspect is noise from the wires used to extend the servo wires; however, every attempt to mitigate this noise didn’t work. Otherwise, the machine works flawlessly and is awesome. Several people commented on how cool it was, and that they would totally buy one.

Step 7: SPIN Code

SPIN Code

CON
Flavor1 = 0
Flavor2 = 1
Flavor3 = 2
Shot2 = 3
Shot4 = 4
Start = 5
Cancel = 6

Servo1 = 8
Servo2 = 21
Servo3 = 18
Servo4 = 11

MotorPos = 24
MotorNeg = 25

Limiter = 16

LCD_Pin = 15
LCD_Baud = 19_200
LCD_Lines = 2

_clkmode = xtal1 + pll16x
_xinfreq = 5_000_000

OBJ
servo : "Servo32v5.spin"
pst : "Parallax Serial Terminal"
lcd : "debug_lcd"

VAR
byte Flavor
byte Shot

PUB main
servo.start
servo.set(Servo1, 1350)
servo.set(Servo2, 570)
servo.set(Servo3, 980)
servo.set(Servo4, 1000)

LCD.init(LCD_Pin, LCD_Baud, LCD_Lines)
LCD.cursor(0)
LCD.cls
LCD.gotoxy(0,0)
LCD.cls
LCD.str(string("Espresso Magic",13, "By Roy Eid",13))
waitcnt(clkfreq*2 + cnt)

dira[MotorPos]~~
dira[MotorNeg]~~



Flavor := 0
Shot := 0

flava

PUB shat
repeat
if ina[Shot2]==1
LCD.gotoxy(9, 1)
LCD.str(string("2"))
Shot := 1
commander

if ina[Shot4]==1
LCD.gotoxy(9, 1)
LCD.str(string("4"))
Shot := 2
commander

if ina[Cancel]==1
LCD.cls
LCD.str(string("Cancelled by",13, "User"))
waitcnt(clkfreq + cnt)
flava

PUB flava
LCD.cls
LCD.str(string("Flavor :",13,"Shots :"))
repeat
if ina[Flavor1]==1
LCD.gotoxy(9, 0)
LCD.str(string("1"))
Flavor := 1
shat

if ina[Flavor2]==1
LCD.gotoxy(9, 0)
LCD.str(string("2"))
Flavor :=2
shat

if ina[Flavor3]==1
LCD.gotoxy(9, 0)
LCD.str(string("3"))
Flavor :=3
shat

PUB commander
repeat
if ina[Start]==1
LCD.cls
LCD.str(string("Dispensing..."))
dispense

if ina[Cancel]==1
LCD.cls
LCD.str(string("Cancelled by",13,"User"))
waitcnt(clkfreq + cnt)
flava

PUB dispense

servo.set(Servo4, 1600)
waitcnt(clkfreq*1 + cnt)

case Flavor
1:
servo.set(Servo1, 1800)
if Shot ==1
waitcnt(clkfreq*2 + cnt)
servo.set(Servo1, 1350)
waitcnt(clkfreq*1 + cnt)
compact

if Shot ==2
waitcnt(clkfreq*4 + cnt)
servo.set(Servo1, 1350)
waitcnt(clkfreq*1 + cnt)
compact
2:
servo.set(Servo2, 1000)
if Shot ==1
waitcnt(clkfreq*2 + cnt)
servo.set(Servo2, 570)
waitcnt(clkfreq*1 + cnt)
compact

if Shot ==2
waitcnt(clkfreq*4 + cnt)
servo.set(Servo2, 570)
waitcnt(clkfreq*1 + cnt)
compact
3:
servo.set(Servo3, 550)
if Shot ==1
waitcnt(clkfreq*2 + cnt)
servo.set(Servo3, 980)
waitcnt(clkfreq*1 + cnt)
compact

if Shot ==2
waitcnt(clkfreq*4 + cnt)
servo.set(Servo3, 980)
waitcnt(clkfreq*1 + cnt)
compact


PUB compact
servo.set(Servo4, 1000)
waitcnt(clkfreq*2 + cnt)
repeat 2
if ina[Limiter]==0
up
if ina[Limiter]==1
down
up

LCD.cls
LCD.str(string("done!"))
waitcnt(clkfreq*2 + cnt)
flava

PUB up

repeat
LCD.gotoxy(0,0)
LCD.str(string("Compacting..."))
outa[MotorNeg] :=1
outa[MotorPos] :=0
if ina[Limiter]==1
outa[MotorNeg] :=0
outa[MotorPos] :=0
quit

PUB down

LCD.cls
LCD.str(string("Compacting..."))
repeat 100
outa[MotorPos] :=1
outa[MotorNeg] :=0
waitcnt(clkfreq/500 + cnt)
outa[MotorNeg] :=0
outa[MotorPos] :=0
waitcnt(clkfreq/500 + cnt)

Microcontroller Contest

Participated in the
Microcontroller Contest

Make It Move Challenge

Participated in the
Make It Move Challenge