Snackbot

John Mains, Leonard Tsai, and Percy Yeung

Description

Summary

Snackbot is a bluetooth controlled snack dispensing machine. If a user wants a quick serving of their favorite snack, Snackbot will fulfill that request. First, the user connects to Snackbot via bluetooth using the Bluefruit LE app on their smart phone. The user then selects UART as the communication. Snackbot is capable of dispensing three different kinds of snacks. To make a request, the user first types in the snack number (1, 2, or 3) and then a letter for the amount (S for small, L for large). The user then sends the request to Snackbot. Snackbot will then dispense the requested snack into a container found at it's base. Once done, the user can remove the container and enjoy their favorite snack.

Operation

Snackbot's initial state is waiting for a request to come through bluetooth. During this phase, the LCD screen is set to show that the Snackbot is waiting for a request. The Mbed is constantly polling the bluetooth module over serial communication to see if a data for a request arrives. Once the data arrives, the Snackbot decodes the data and stores the information in a struct. This struct contains data on the snack type and serving size. This data is then sent to be processes in the process request function. Here the corresponding servo is opened to allow the snack to flow. As the snack flows into the container, an ultrasonic sensor is measuring the height to determine if the proper serving size has been reached. This data is sent back to the Mbed where the software determines if it should continue keeping the valve open, or close the valve and finish the dispensing process. During this phase, a progress bar is displayed on the LCD to show how close Snackbot is to finishing the request. Once the proper serving size has been reached, the servo is closed and LCD panels tells the user that Snackbot is finished fulfilling the request. Snackbot then returns to it's initial state where it can receive another request to dispense more snacks.

Components

  • Mbed Microcontroller
  • Ultrasonic Sensor (HC-SR04)
  • uLCD-144-G2
  • 3 Servos
  • Adafruit Bluefruit LE UART

Wiring

MBEDBluetooth
p13TXO
p14RXI
VoutVin
GndGnd
GndCTS
MBEDuLCD
5V5V
GndGnd
p28RX
p27TX
p30Reset
MBEDUltrasonic
5V5V
GndGnd
p6Trig
p7Echo
MBEDServo1
p21Signal
12VVDD
GndGnd
MBEDServo2
p22Signal
12VVDD
GndGnd
MBEDServo3
p23Signal
12VVDD
GndGnd

Program

Snackbot Code

#include "mbed.h"
#include "uLCD_4DGL.h"
#include "Servo.h"
#include "ultrasonic.h"

RawSerial  pc(USBTX, USBRX);
RawSerial  dev(p13,p14);
DigitalOut led1(LED1);
DigitalOut led4(LED4);
Servo   m_FeedServo1(p21);
Servo   m_FeedServo2(p22);
Servo   m_FeedServo3(p23);
uLCD_4DGL uLCD(p28,p27,p30);
int m_Distance;

 void dist(int distance)
{
    //put code here to execute when the distance has changed
    m_Distance = distance;
    uLCD.locate(0,0);
    uLCD.printf("%d", m_Distance);
}

ultrasonic mu(p6, p7, .1, 1, &dist); 

typedef enum 
{
    SIZE_LARGE,
    SIZE_SMALL
} SnackSize;

struct SnackRequest
{
    SnackSize mSnackSize;
    int mSnackType;
};

SnackRequest mSnackRequest;
char mSnackNum = 0;
char mSnackSize = 0;
Timer m_Timer;
int m_NumDots = 1;
int m_BaseContainerHeight = 300;
int m_LowHeight = 285;
int m_HeighHeight = 265;

void MoveServo(SnackRequest pSnackRequest)
{
    switch(pSnackRequest.mSnackType)
    {
        case 1: m_FeedServo1 = 1.0f; break;
        case 2: m_FeedServo2 = 1.0f; break;
        case 3: m_FeedServo3 = 1.0f; break;
    }
    wait(0.3);
    switch(pSnackRequest.mSnackType)
    {
        case 1: m_FeedServo1 = 0.5f; break;
        case 2: m_FeedServo2 = 0.6f; break;
        case 3: m_FeedServo3 = 0.6f; break;
    }
    wait(1);
}

float MeasureDistance()
{
    int count = 0;
    int average = 0;
    for(int i = 0; i < 20; i++)
    {
        mu.checkDistance();
        if(m_Distance > 150)
        {
            average += m_Distance;
            count++;
        }
    }
    return ((float)average / (float)count);
}

bool ProcessSnackRequest(SnackRequest pSnackRequest)
{
    float PctFull;
    float ChosenHeight;
    float AverageDistance;
    
    if(pSnackRequest.mSnackSize == SIZE_SMALL)
    {
        ChosenHeight = m_LowHeight;
    }
    else
    {
        ChosenHeight = m_HeighHeight;
    } 
    
    uLCD.cls();
    uLCD.filled_rectangle(10, 100, 120, 120, 0xFFFFFF);
    uLCD.filled_rectangle(12, 102, 118, 118, 0x000000);
    uLCD.locate(4,5);
    uLCD.printf("Filling...");
    mu.checkDistance();
    while(1)
    {
        AverageDistance = MeasureDistance();
        PctFull =  1 - (((float)AverageDistance - ChosenHeight) / (m_BaseContainerHeight - ChosenHeight));
        pc.printf("Percent Full: %f", PctFull);
        if(PctFull >= 0 && PctFull <= 1) uLCD.filled_rectangle(12, 102, (int)(12 + PctFull * 108), 118, 0x00FF00);
        MoveServo(pSnackRequest);
        if(PctFull >= 1) break;
    }
    
    uLCD.cls();
    uLCD.locate(7,6);
    uLCD.printf("DONE!");
    wait(2);
    
    return true;
}

void DisplayMain()
{
    uLCD.cls();
    uLCD.locate(3,1);
    uLCD.printf("Snackbot v1.0\n");
    uLCD.locate(6,5);
    uLCD.printf("Waiting");
    uLCD.locate(8,6);
    uLCD.printf("for");
    uLCD.locate(6,7);
    uLCD.printf("Request");
}

int main()
{
    dev.baud(9600);
    m_FeedServo1 = 0.5f;
    m_FeedServo2 = 0.6f;
    m_FeedServo3 = 0.6f;
    DisplayMain();
    m_Timer.start();
    mu.startUpdates();
    uLCD.baudrate(3000000);
    
    while(1) 
    {
        mu.checkDistance();
        if(dev.readable())
        {
            mSnackNum = dev.getc();
            mSnackSize = dev.getc();
            while(dev.readable())
            {
                //Clear bluetooth buffer
                dev.getc();
            }
        }
        
        if(mSnackNum != 0 && mSnackSize != 0)
        {   
            if(mSnackNum == '1')
                mSnackRequest.mSnackType = 1;
            else if(mSnackNum == '2')
                mSnackRequest.mSnackType = 2;
            else if(mSnackNum == '3')
                mSnackRequest.mSnackType = 3;
            
            if(mSnackSize == 'S')
                mSnackRequest.mSnackSize = SIZE_SMALL;
            else if(mSnackSize == 'L')
                mSnackRequest.mSnackSize = SIZE_LARGE;
            
            ProcessSnackRequest(mSnackRequest);
            
            mSnackNum = 0;
            mSnackSize = 0;
            DisplayMain();
        }
        
        if(m_Timer.read() > 1)
        {
            m_Timer.reset();
            if(m_NumDots > 3)
            { 
                uLCD.locate(7,8);
                m_NumDots = 1;
                uLCD.printf("         ");
            }
            uLCD.locate(6,8);
            switch(m_NumDots)
            {
                case 1:
                    uLCD.printf(".");
                    break;
                case 2:
                    uLCD.printf(".  .");
                    break;
                case 3:
                    uLCD.printf(".  .  .");
                    break;
            }
            m_NumDots++;
        }
    }
}

Pictures

http://i.imgur.com/cvJsm9L.jpg http://i.imgur.com/QhRxULe.jpg http://i.imgur.com/GGL0psf.jpg http://i.imgur.com/2mbz9Tu.jpg http://i.imgur.com/Jz7k3FX.jpg http://i.imgur.com/5Z6pKAJ.jpg

Inventor Renderings

http://i.imgur.com/U2sLtVW.jpg http://i.imgur.com/oFwjbjx.jpg

Block Diagram

http://i.imgur.com/5IS4w5S.png

Problems Faced and Future Work

Ultrasonic Sensor

The ultrasonic sensor we used provided very unreliable readings. Just the shape of the snacks, their arrangement in the container, and residual snacks flying out of the tube when dispensing caused noise that made our measurements inaccurate. We tried some methods to alleviate the noise. First we only measured after the dispense was finished. We also took an average of 20 measurements to smooth out noise. A better fix to this problem that we did not get to would be to change the sensor to a load cell in which we can measure the weight, or use a more accurate distance sensor that doesn't get affected as much by the shape of the snacks.

Bottleneck Blockage

Sometimes our bottle feed system would clog. This was caused by the narrow neck in the soda bottles we used. This sometimes caused problems as the flow of snacks would stop, and Snackbot would be unable to know that this happened. Therefore it would stay stuck in it's dispensing loop. An easy way to fix this problem would have been to use bottles with larger necks like a Gatorade bottle.


Please log in to post comments.