Smart Cabinet

Yanrui Wang (yw2226) and Mengde Wu (mw934)

Friday, December 13, 2019

We designed a smart cabinet to store and grab toy cars automatically.

Introduction

We designed a Smart Cabinet to store and grab toy cars automatically through pressing a 'store' button and a 'grabbing' button.

High Level Design

Our idea of final projet derived from a real problem in the world. It's very crowded in the big cities like New York City. When you drive in New York City, it's very crowded and very difficult to find an available parking space. How to solve this problem?

If we're able to design and build an intelligent parking lot underground, drivers only need to drive in specified location and check in. The car would be automatically stored underground. When drivers are back, they only need to check out before they go, the car would be shown up on the ground. Then all the problems are addressed.

Based on this idea, we designed and built a Smart Cabinet to store and grab toy cars as a prototype to mimic the intelligent parking lot as we imagined. We designed two cells structure to store toy cars inside it. We designed a platform to put toy car on it, then press the 'store' button, and the system will store the item automatically in a cell. After Smart Cabinet successfully stores an toy car, it will give a code on the TFT screen, which you need to remember in order to pick up toy car in the future. If you want to take the toy car from Smart Cabinet, you need to input the 'picking up' code and the system will automatically take the toy car out for you.

Design

Hardware Design

The main structure of Smart Cabinet is built by acrylic. We have many figures drawn in AutoCAD in order to do laser cutting.

Figure 1: All laser cutting design.

Figure1 Note: All laser cutting design is in 2D. We used 6mm acrylic to cut every side and assemble them together. The bulges and hollows structure are aimed to attach each side of acrylic firmly.

On figure 1, we shown all laser cutting figures we drawn in the final project. In the following, we only shown bottom side, top side and front side specifically since they're most important. In addition, the other sides of Smart Cabinet has similar design with those three main parts.

Figure 2: Bottom Side of Smart Cabinet.

Figure2 Note: On bottom side, we designed 6 legs for Smart Cabinet. The top middle circle is to to be attached with the central stepper motor. The holes on its left and right is to be attached with 3D print material finally connect with two rods.

Figure 3: Top Side of Smart Cabinet.

Figure3 Note: On top side, we designed three holes to be attached with one lead screw and two rods. The bottom rectangle structure is to hold PIC32 and board of buttons-design.

Figure 4: Front Side of Smart Cabinet.

Figure4 Note: The middle two sigle holes are to be attached with two different motors in each cell structure. The right and left side structure are used to be attached with two rods.

Figure 5: Wood Design for Wood Box.

Figure5 Note: This figure shows the laser cut file for building the wood box. This wood box consists of five parts. We glued these five parts together using wood glue. Then we just used wood screws to fix this wood box on a part printed by 3D-printer. Since this wood box does not have a bottom, it can be used to pull the item from the platform or push the item onto the platform.

Figure 6: Platform design of Smart Cabinet.

Figure6 Note: 3D print design of central platform.

Figure 7: Side Box Structure of Smart Cabinet.

Figure7 Note: 3D print design of side box structure.

Figure 8: stepper motor driver circuit.

Figure8 Note: As is shown in Figure8 is the circuit of stepper motor driver module DRV8824. In this project, we connect DIR pin to GPIO Y0, STEP pin to GPIO Y1. There is another kind of stepper motor driver in our project, which needs four input pins. For this kind of driver, we connect one to GPIOZ0-3 and one to GPIOZ4-7.

Figure 9: button design.

Figure9 Note: This is the circuit for one single button. We connected five buttons to the big board and used GPIOY2-6.

Software Design

The entire software design consisted of four threads. The overall purpose of each of these components is as follows:

Timer thread: draw the user interface on TFT screen and keep updating status of system at fixed 10 fps

Command thread: preset what the system needs to do (set the status of motors: which motor and the direction of rotation of that motor) given different commands and keep checking what command has been given

Key thread: Keep checking which key has been pressed and released and give the corresponding command

Motor thread: Keep checking the status of motors, which is set in the command thread, and make the motors run according to the status

Stepper Motor Control

There are two kinds of stepper motor in our project. Since they use different kinds of stepper motor driver, the codes to drive them are different.

For the stepper motors using ULN2003 driver board, we need to directly send pulse wave to the four phases of the motor. The code is shown below.

writePE(GPIOZ, 0x01);
PT_YIELD_TIME_msec(2);
writePE(GPIOZ, 0x02);
PT_YIELD_TIME_msec(2);
writePE(GPIOZ, 0x04);
PT_YIELD_TIME_msec(2);
writePE(GPIOZ, 0x08);
PT_YIELD_TIME_msec(2);

For the stepper motor using DRV8825 Stepper Motor Driver Module, the code is totally different. It only needs two I/O pins, one pin for direction and one pin for steps. If the direction pin is set HIGH, the motor would rotate counter-clockwisely. If the direction pin set LOW, the motor would rotate clockwisely. In this way, we can write the driving code like this.

If we want it to rotate counter-clockwisely, the code is:

clearBits(GPIOY, 0x01);

setBits(GPIOY, 0x02);
PT_YIELD_TIME_msec(5);
clearBits(GPIOY, 0x02);
PT_YIELD_TIME_msec(5);

If we want it to rotate clockwisely, the code is:

setBits(GPIOY, 0x01);

setBits(GPIOY, 0x02);
PT_YIELD_TIME_msec(5);
clearBits(GPIOY, 0x02);
PT_YIELD_TIME_msec(5);

Open Loop Control

In the Command thread, we implemented an open loop control to the system. If we want to control one motor, we just need to set two things, Motor_Num and Motor_Status. Motor_Num corresponds to which motor we want to control. Motor_Status corresponds to whether we want it to move in or move out or stay idle.

For example:

Motor_Num = MOTOR3;
Motor_Status = MOVE_IN;
for (i=0;i<26;i++) PT_YIELD_TIME_msec(1000);
Motor_Num = IDLE;
Motor_Status = IDLE;

Button Control

We designed five buttons for 'storing', 'picking up' and 'input' purpose.

When 'storing' button is pressed, the system would check whether there is an empty cell. If not, it wil display 'Sorry, it's full' on the TFE screen. If there is an empty cell, the system would store the toy cars inside it and generate a random 3-digit code for picking up purpose in the future

When 'grabbing' button is pressed, the user uses left and right button to select right number in each units and press 'Enter' button, once 3-digit number matches with the 'picking up 'code in the system, the toy cars would be taken out automatically.

Results

Figure 10: TFT screen and Buttons design of Smart Cabinet.

Figure10 Note: This figure shows a TFT screen which used to display system status and for 'input' purpose. We also successfully designed five buttons. Two of them are used to 'storing' and 'picking up' purpose, the other three buttons are used to input 'picking up code'.

Figure 11: Side View of Smart Cabinet.

Figure11 Note: We successfully designed the strongly mechanical structure of the Smart Cabinet. Figure 11 is a side view of our structure. There are two motors on the side in orde to slide wood box then slide toy cars into cell or out of cell. The while squres are 3D print materials to attach rods and acrylic structure.

Figure 12: Front View of Smart Cabinet.

Figure12 Note: Figure 12 shows the front view of the Smart Cabinet. On the left, we designed a platform which could be moved vertically. On the right, we designed two cells which are able to store toy cars inside them.

The Smart Cabinet is portable. Our mechanical design is strong and firm. In addition, our control is stable to store and pick up toy cars automatically and successfully.

Conclusion

Currently, the smart cabinet is using open loop control without sensor and is tested to work properly. As shown in the demo video, when we put a toy car at the specific place on the platform and press the "store" button, the Smart Cabinet would finish the storing sequece and successfully store it in one cell. It also works well with grabing. However, we found that the motors might sometimes stuck at some place. This would endanger the whole systems, since open loop control requires that the motors run properly all the time.

Future Work

For further improvement, we would consider adding ultrasonic sensor to switch from open loop control to close loop control, which will make the system much more stable and safer. The ultrasonic sensor will be used to make sure that the platform and the boxes move to certain place. We can even add a PID controller to it.

Work Distribution

Team work: hardware design, software design, final report

Yanrui Wang: whole laser cutting structure design for acrylic, button control, circuit design

Mengde Wu: 3D print, wood box design, stepper motor control, open loop control

Project Parts

Part Name Price (USD)
Big board 10
TFT LCD 10
MicroStickII 1
DRV8825 Stepper Motor Driver Module 2
Small stepper motor and driver 2.8 x 2 = 5.6
Stepper Motor Bipolar 13.99
Aluminum Shaft Coupling Rigid Coupler 2.25 x 3 = 6.75
HICTOP Linear Motion Ball Bearing 2.75 x 6 = 16.5
Hardened Chrome Linear Motion Rods 12.5 x 2 = 25
lead screw 9.99 x 2 = 19.98
total 110.82

Appendix

Appendix A

The group approves this report for inclusion on the course website.

The group approves the video for inclusion on the course youtube channel.

Code Appendix

              
/*
 * File:        TFT, keypad, DAC, LED, PORT EXPANDER test
 *              With serial interface to PuTTY console
 * Author:      Bruce Land
 * For use with Sean Carroll's Big Board
 * http://people.ece.cornell.edu/land/courses/ece4760/PIC32/target_board.html
 * Target PIC:  PIC32MX250F128B
 */

////////////////////////////////////
// clock AND protoThreads configure!
// You MUST check this file!.................................
//  TEST OLD CODE WITH NEW THREADS
//#include "config_1_2_3.h"
#include "config_1_3_2.h"
// threading library
//#include "pt_cornell_1_2_3.h"
#include "pt_cornell_1_3_2.h"
// yup, the expander
#include "port_expander_brl4.h"

////////////////////////////////////
// graphics libraries
// SPI channel 1 connections to TFT
#include "tft_master.h"
#include "tft_gfx.h"
// need for rand function
#include 
// need for sin function
#include 
#include 
////////////////////////////////////

// lock out timer interrupt during spi comm to port expander
// This is necessary if you use the SPI2 channel in an ISR
#define start_spi2_critical_section INTEnable(INT_T2, 0);
#define end_spi2_critical_section INTEnable(INT_T2, 1);

////////////////////////////////////
// some precise, fixed, short delays
// to use for extending pulse durations on the keypad
// if behavior is erratic
#define NOP asm("nop");
// 1/2 microsec
#define wait20 NOP;NOP;NOP;NOP;NOP;NOP;NOP;NOP;NOP;NOP;NOP;NOP;NOP;NOP;NOP;NOP;NOP;NOP;NOP;NOP;
// one microsec
#define wait40 wait20;wait20;
////////////////////////////////////

// string buffer
char buffer[60];

// === thread structures ============================================
// thread control structs
static struct pt pt_timer, pt_motor, pt_command, pt_key;

// system 1 second interval tick
int sys_time_seconds ;

volatile int pick_up_code[2] = {0, 0};
volatile int code_matching = 0;
volatile int enter_num = 0;
volatile int code_display_pos_x;

// motor status
#define IDLE 0
#define MOVE_IN 1
#define MOVE_OUT 2
volatile int Motor_Status = IDLE;

// current motor
#define IDLE 0
#define MOTOR1 1 //motor for third layer
#define MOTOR2 2//motor for second layer
#define MOTOR3 3 //motor for platform
volatile int Motor_Num = IDLE;

// current cabinet status
#define EMPTY 0
#define FULL 1
#define STORING 2
#define TAKING 3
volatile int Cell1_Status = EMPTY;
volatile int Cell2_Status = EMPTY;

// system status
#define IDLE 0
#define Store_To_Cell_One 1
#define Store_To_Cell_Two 2
#define Take_From_Cell_One 3
#define Take_From_Cell_Two 4
#define TEST 5
volatile int System_Status = IDLE;

// remaining time of current process
volatile int remaining_time;

volatile int time;

#define int2Accum(a) ((_Accum)(a))

// === print a line on TFT =====================================================
// print a line on the TFT
// string buffer
char buffer[60];
void printLine(int line_number, char* print_buffer, short text_color, short back_color){
    // line number 0 to 31 
    /// !!! assumes tft_setRotation(0);
    // print_buffer is the string to print
    int v_pos;
    v_pos = line_number * 10 ;
    // erase the pixels
    tft_fillRoundRect(0, v_pos, 239, 8, 1, back_color);// x,y,w,h,radius,color
    tft_setTextColor(text_color); 
    tft_setCursor(0, v_pos);
    tft_setTextSize(1);
    tft_writeString(print_buffer);
}

void printLine2(int line_number, char* print_buffer, short text_color, short back_color){
    // line number 0 to 31 
    /// !!! assumes tft_setRotation(0);
    // print_buffer is the string to print
    int v_pos;
    v_pos = line_number * 20 ;
    // erase the pixels
    tft_fillRoundRect(0, v_pos, 239, 16, 1, back_color);// x,y,w,h,radius,color
    tft_setTextColor(text_color); 
    tft_setCursor(0, v_pos);
    tft_setTextSize(2);
    tft_writeString(print_buffer);
}

// === Timer Thread =================================================
static PT_THREAD (protothread_timer(struct pt *pt))
{
    static int begin_time;
    PT_BEGIN(pt);
    // set up LED to blink
    mPORTASetBits(BIT_0 );	//Clear bits to ensure light is off.
    mPORTASetPinsDigitalOut(BIT_0 );    //Set port as output
     
    sprintf(buffer,"Smart Cabinet System");
    printLine2(1, buffer, ILI9340_WHITE, ILI9340_BLACK);

    tft_drawRoundRect(10,50,220,160,5,ILI9340_WHITE);
    tft_drawLine(10,130,230,130,ILI9340_WHITE);
    
    sprintf(buffer,"CELL1");
    tft_fillRoundRect(30, 70, 100, 20, 1, ILI9340_BLACK);// x,y,w,h,radius,color
    tft_setTextColor(ILI9340_WHITE); 
    tft_setCursor(30, 70);
    tft_setTextSize(2);
    tft_writeString(buffer);
    
    sprintf(buffer,"CELL2");
    tft_fillRoundRect(30, 150, 100, 20, 1, ILI9340_BLACK);// x,y,w,h,radius,color
    tft_setTextColor(ILI9340_WHITE); 
    tft_setCursor(30, 150);
    tft_setTextSize(2);
    tft_writeString(buffer);
    
    sprintf(buffer,"System Status:");
    printLine2(11, buffer, ILI9340_WHITE, ILI9340_BLACK);

    
     while(1) {
        // yield time 1 second
        begin_time = PT_GET_TIME();
        
        // toggle the LED on the big board
        mPORTAToggleBits(BIT_0);

        // draw the status of cell 1 and 2
        if (Cell1_Status == EMPTY){
            sprintf(buffer,"EMPTY");
        }
        else if (Cell1_Status == FULL){
            sprintf(buffer,"FULL");
        }
        else if (Cell1_Status == STORING){
            sprintf(buffer,"STORING");
        }
        else if (Cell1_Status == TAKING){
            sprintf(buffer,"TAKING");
        }
        tft_fillRoundRect(70, 90, 100, 20, 1, ILI9340_BLACK);// x,y,w,h,radius,color
        tft_setTextColor(ILI9340_WHITE); 
        tft_setCursor(70, 90);
        tft_setTextSize(2);
        tft_writeString(buffer);
        
        if (Cell2_Status == EMPTY){
            sprintf(buffer,"EMPTY");
        }
        else if (Cell2_Status == FULL){
            sprintf(buffer,"FULL");
        }
        else if (Cell2_Status == STORING){
            sprintf(buffer,"STORING");
        }
        else if (Cell2_Status == TAKING){
            sprintf(buffer,"TAKING");
        }
        tft_fillRoundRect(70, 170, 100, 20, 1, ILI9340_BLACK);// x,y,w,h,radius,color
        tft_setTextColor(ILI9340_WHITE); 
        tft_setCursor(70, 170);
        tft_setTextSize(2);
        tft_writeString(buffer);
        
        // draw current system status
        if (System_Status == IDLE){
            sprintf(buffer,"IDLE");
        }
        else if (System_Status == Store_To_Cell_One | System_Status == Store_To_Cell_Two){
            sprintf(buffer,"Storing");
        }
        else if (System_Status == Take_From_Cell_One | System_Status == Take_From_Cell_Two){
            sprintf(buffer,"Taking");
        }
        else if (System_Status == TEST){
            sprintf(buffer,"Testing");
        }
        tft_fillRoundRect(70, 240, 100, 20, 1, ILI9340_BLACK);// x,y,w,h,radius,color
        tft_setTextColor(ILI9340_WHITE); 
        tft_setCursor(70, 240);
        tft_setTextSize(2);
        tft_writeString(buffer);
        
        
        PT_YIELD_TIME_msec(100 - (PT_GET_TIME() - begin_time));
        // NEVER exit while
      } // END WHILE(1)
  PT_END(pt);
} // timer thread


// === Command Thread =================================================
static PT_THREAD (protothread_command(struct pt *pt))
{
    PT_BEGIN(pt);
    static int i;
    while(1) {
        if (System_Status == TEST){
            Motor_Num = MOTOR3;
            Motor_Status = MOVE_OUT;
            for (i=0;i<54;i++) PT_YIELD_TIME_msec(1000);
            System_Status = IDLE;
            Motor_Num = IDLE;
            Motor_Status = IDLE;
        }
        if (System_Status == Store_To_Cell_One){
            Cell1_Status = STORING;
            Motor_Num = MOTOR3;
            Motor_Status = MOVE_IN;
            for (i=0;i<26+8;i++) PT_YIELD_TIME_msec(1000);
            Motor_Num = MOTOR1;
            Motor_Status = MOVE_OUT;
            for (i=0;i<60;i++) PT_YIELD_TIME_msec(1000);
            Motor_Num = MOTOR3;
            Motor_Status = MOVE_OUT;
            for (i=0;i<8;i++) PT_YIELD_TIME_msec(1000);
            Motor_Num = MOTOR1;
            Motor_Status = MOVE_IN;
            for (i=0;i<60;i++) PT_YIELD_TIME_msec(1000);
            Motor_Num = MOTOR3;
            Motor_Status = MOVE_OUT;
            for (i=0;i<26;i++) PT_YIELD_TIME_msec(1000);
            
            System_Status = IDLE;
            Motor_Num = IDLE;
            Motor_Status = IDLE;
            Cell1_Status = FULL;
        }
        if (System_Status == Store_To_Cell_Two){
            Cell2_Status = STORING;
            Motor_Num = MOTOR3;
            Motor_Status = MOVE_IN;
            for (i=0;i<26+28+8;i++) PT_YIELD_TIME_msec(1000);
            Motor_Num = MOTOR2;
            Motor_Status = MOVE_OUT;
            for (i=0;i<60;i++) PT_YIELD_TIME_msec(1000);
            Motor_Num = MOTOR3;
            Motor_Status = MOVE_OUT;
            for (i=0;i<8;i++) PT_YIELD_TIME_msec(1000);
            Motor_Num = MOTOR2;
            Motor_Status = MOVE_IN;
            for (i=0;i<60;i++) PT_YIELD_TIME_msec(1000);
            Motor_Num = MOTOR3;
            Motor_Status = MOVE_OUT;
            for (i=0;i<54;i++) PT_YIELD_TIME_msec(1000);
            
            System_Status = IDLE;
            Motor_Num = IDLE;
            Motor_Status = IDLE;
            Cell2_Status = FULL;
        }
        if (System_Status == Take_From_Cell_One){
            Cell1_Status = TAKING;
            Motor_Num = MOTOR3;
            Motor_Status = MOVE_IN;
            for (i=0;i<26;i++) PT_YIELD_TIME_msec(1000);
            Motor_Num = MOTOR1;
            Motor_Status = MOVE_OUT;
            for (i=0;i<60;i++) PT_YIELD_TIME_msec(1000);
            Motor_Num = MOTOR3;
            Motor_Status = MOVE_IN;
            for (i=0;i<8;i++) PT_YIELD_TIME_msec(1000);
            Motor_Num = MOTOR1;
            Motor_Status = MOVE_IN;
            for (i=0;i<60;i++) PT_YIELD_TIME_msec(1000);
            Motor_Num = MOTOR3;
            Motor_Status = MOVE_OUT;
            for (i=0;i<26+8;i++) PT_YIELD_TIME_msec(1000);
            
            System_Status = IDLE;
            Motor_Num = IDLE;
            Motor_Status = IDLE;
            Cell1_Status = EMPTY;
        }
        if (System_Status == Take_From_Cell_Two){
            Cell2_Status = TAKING;
            Motor_Num = MOTOR3;
            Motor_Status = MOVE_IN;
            for (i=0;i<26+28;i++) PT_YIELD_TIME_msec(1000);
            Motor_Num = MOTOR2;
            Motor_Status = MOVE_OUT;
            for (i=0;i<60;i++) PT_YIELD_TIME_msec(1000);
            Motor_Num = MOTOR3;
            Motor_Status = MOVE_IN;
            for (i=0;i<8;i++) PT_YIELD_TIME_msec(1000);
            Motor_Num = MOTOR2;
            Motor_Status = MOVE_IN;
            for (i=0;i<60;i++) PT_YIELD_TIME_msec(1000);
            Motor_Num = MOTOR3;
            Motor_Status = MOVE_OUT;
            for (i=0;i<26+28+8;i++) PT_YIELD_TIME_msec(1000);
            
            System_Status = IDLE;
            Motor_Num = IDLE;
            Motor_Status = IDLE;
            Cell2_Status = EMPTY;
        }
        
        PT_YIELD_TIME_msec(1000);
      } // END WHILE(1)
  PT_END(pt);
} // timer thread

// === Motor Thread =============================================
static PT_THREAD (protothread_motor(struct pt *pt))
{
    PT_BEGIN(pt);
    
    while(1) {
        // yield time
        switch (Motor_Status)
        {
            case IDLE: 
                PT_YIELD_TIME_msec(10);
                break;
                
            case MOVE_IN:
                start_spi2_critical_section;
                clearBits(GPIOY, 0x01);
                end_spi2_critical_section ;
                while (Motor_Status == MOVE_IN){
                    switch (Motor_Num)
                    {
                        case IDLE:
                            PT_YIELD_TIME_msec(10);
                            break;
                        case MOTOR1:
                            writePE(GPIOZ, 0x01);
                            PT_YIELD_TIME_msec(2);
                            writePE(GPIOZ, 0x02);
                            PT_YIELD_TIME_msec(2);
                            writePE(GPIOZ, 0x04);
                            PT_YIELD_TIME_msec(2);
                            writePE(GPIOZ, 0x08);
                            PT_YIELD_TIME_msec(2);
                            break;
                        case MOTOR2:
                            writePE(GPIOZ, 0x10);
                            PT_YIELD_TIME_msec(2);
                            writePE(GPIOZ, 0x20);
                            PT_YIELD_TIME_msec(2);
                            writePE(GPIOZ, 0x40);
                            PT_YIELD_TIME_msec(2);
                            writePE(GPIOZ, 0x80);
                            PT_YIELD_TIME_msec(2);
                            break;
                        case MOTOR3:
                            setBits(GPIOY, 0x02);
                            PT_YIELD_TIME_msec(5);
                            clearBits(GPIOY, 0x02);
                            PT_YIELD_TIME_msec(5);
                            break;
                    }
                }
                break;
                
            case MOVE_OUT:
                setBits(GPIOY, 0x01);
                while (Motor_Status == MOVE_OUT){
                    switch (Motor_Num)
                    {
                        case IDLE:
                            PT_YIELD_TIME_msec(10);
                            break;
                        case MOTOR1:
                            writePE(GPIOZ, 0x08);
                            PT_YIELD_TIME_msec(2);
                            writePE(GPIOZ, 0x04);
                            PT_YIELD_TIME_msec(2);
                            writePE(GPIOZ, 0x02);
                            PT_YIELD_TIME_msec(2);
                            writePE(GPIOZ, 0x01);
                            PT_YIELD_TIME_msec(2);
                            break;
                        case MOTOR2:
                            writePE(GPIOZ, 0x80);
                            PT_YIELD_TIME_msec(2);
                            writePE(GPIOZ, 0x40);
                            PT_YIELD_TIME_msec(2);
                            writePE(GPIOZ, 0x20);
                            PT_YIELD_TIME_msec(2);
                            writePE(GPIOZ, 0x10);
                            PT_YIELD_TIME_msec(2);
                            break;
                        case MOTOR3:
                            setBits(GPIOY, 0x02);
                            PT_YIELD_TIME_msec(5);
                            clearBits(GPIOY, 0x02);
                            PT_YIELD_TIME_msec(5);
                            break;
                    }
                }
                break;
        }
                
        // NEVER exit while
    } // END WHILE(1)
    PT_END(pt);
} // Motor thread

static int seed = 0;
_Accum random_Num(_Accum lower_bound, _Accum upper_bound)
{
    int randomNum = rand() % 1000 + 1;
    
    return int2Accum(randomNum) / 1000 * (upper_bound - lower_bound) + lower_bound;
}


// === Key Thread =============================================
static PT_THREAD (protothread_key(struct pt *pt))
{
    PT_BEGIN(pt);
    static int key_value;

#define ENTER BIT_6
#define RIGHT BIT_5
#define DOWN BIT_4
#define UP BIT_3
#define LEFT BIT_2
    
      while(1) {
        // yield time
        PT_YIELD_TIME_msec(10);
        
        key_value  = readBits(GPIOY, ENTER);
        if(key_value==0) {
            while(1){
                PT_YIELD_TIME_msec(10);
                key_value  = readBits(GPIOY, ENTER);
                if (key_value != 0){
                    code_matching *= 10;
                    code_matching += enter_num;
                    code_display_pos_x += 15;
                    enter_num = 0;
                    
                    if(code_matching < 100)
                    {
                        sprintf(buffer,"%d", enter_num);

                        tft_fillRoundRect(code_display_pos_x, 260, 100, 20, 1, ILI9340_BLACK);// x,y,w,h,radius,color
                        tft_setTextColor(ILI9340_WHITE); 
                        tft_setCursor(code_display_pos_x, 260);
                        tft_setTextSize(2);
                        tft_writeString(buffer);
                        
                        sprintf(buffer,"Password: %d", code_matching);
                        printLine2(15, buffer, ILI9340_WHITE, ILI9340_BLACK);
                    }
                    else
                    {
                        if(code_matching == pick_up_code[0])
                        {
                            sprintf(buffer,"Correct, Waiting...");
                            printLine2(15, buffer, ILI9340_WHITE, ILI9340_BLACK);
                            
                            System_Status = Take_From_Cell_One;
                        }
                        else if (code_matching == pick_up_code[1])
                        {
                            sprintf(buffer,"Correct, Waiting...");
                            printLine2(15, buffer, ILI9340_WHITE, ILI9340_BLACK);
                                    
                            System_Status = Take_From_Cell_Two;
                        }
                        else
                        {
                            sprintf(buffer,"Sorry, Wrong Code");
                            printLine2(15, buffer, ILI9340_WHITE, ILI9340_BLACK);
                        }
                            
                    }
                    
                    break;
                }
            }
        }

        key_value  = readBits(GPIOY, UP);
        if(key_value==0) {
            while(1){
                PT_YIELD_TIME_msec(10);
                key_value  = readBits(GPIOY, UP);
                if (key_value != 0){
                    if(Cell1_Status == EMPTY)
                    {
                        pick_up_code[0] = random_Num(100, 999);
                        sprintf(buffer,"Pick Code: %d",pick_up_code[0]);
                        printLine2(13, buffer, ILI9340_WHITE, ILI9340_BLACK);
                        
                        System_Status = Store_To_Cell_One;
                    }
                    else if(Cell2_Status == EMPTY)
                    {
                        pick_up_code[1] = random_Num(100, 999);
                        sprintf(buffer,"Pick Code: %d",pick_up_code[1]);
                        printLine2(13, buffer, ILI9340_WHITE, ILI9340_BLACK);
                        
                        System_Status = Store_To_Cell_Two;
                    }
                    else
                    {
                        sprintf(buffer,"Sorry, it's full");
                        printLine2(13, buffer, ILI9340_WHITE, ILI9340_BLACK);
                    }
                    break;
                }
            }
        }
        
        key_value  = readBits(GPIOY, DOWN);
        if(key_value==0) {
            while(1){
                PT_YIELD_TIME_msec(10);
                key_value  = readBits(GPIOY, DOWN);
                if (key_value != 0){
                    enter_num = 0;
                    code_matching = 0;
                    code_display_pos_x = 140;
                    
                    sprintf(buffer,"Enter Code: %d", enter_num);
                    printLine2(13, buffer, ILI9340_WHITE, ILI9340_BLACK);
                    
                    
                    break;
                }
            }
        }
        
        key_value  = readBits(GPIOY, LEFT);
        if(key_value==0) {
            while(1){
                PT_YIELD_TIME_msec(10);
                key_value  = readBits(GPIOY, LEFT);
                if (key_value != 0){
                    enter_num--;
                    if(enter_num < 0)
                    {
                        enter_num += 10;
                    }
                    sprintf(buffer,"%d", enter_num);
                    
                    tft_fillRoundRect(code_display_pos_x, 260, 100, 20, 1, ILI9340_BLACK);// x,y,w,h,radius,color
                    tft_setTextColor(ILI9340_WHITE); 
                    tft_setCursor(code_display_pos_x, 260);
                    tft_setTextSize(2);
                    tft_writeString(buffer);
                    
                    //System_Status = TEST;
                    break;
                }
            }
        }
        
        key_value  = readBits(GPIOY, RIGHT);
        if(key_value==0) {
            while(1){
                PT_YIELD_TIME_msec(10);
                key_value  = readBits(GPIOY, RIGHT);
                if (key_value != 0){
                    enter_num++;
                    if(enter_num >= 10)
                    {
                        enter_num -= 10;
                    }
                    sprintf(buffer,"%d", enter_num);
                    
                    tft_fillRoundRect(code_display_pos_x, 260, 100, 20, 1, ILI9340_BLACK);// x,y,w,h,radius,color
                    tft_setTextColor(ILI9340_WHITE); 
                    tft_setCursor(code_display_pos_x, 260);
                    tft_setTextSize(2);
                    tft_writeString(buffer);
                    
                    break;
                }
            }
        }
        
     }

        // NEVER exit while
   // END WHILE(1)
    PT_END(pt);
} // key thread


// === Main  ======================================================
void main(void) {
  ANSELA = 0; ANSELB = 0; 
   
  // === config threads ==========
  // turns OFF UART support and debugger pin, unless defines are set
  PT_setup();

  // === setup system wide interrupts  ========
  INTEnableSystemMultiVectoredInt();
  
  // === set up i/o port pin ===============================
    mPORTBSetPinsDigitalOut(BIT_0 );    //Set port as output
    initPE();
    mPortZSetPinsOut(BIT_0 | BIT_1 | BIT_2 | BIT_3 | BIT_4 | BIT_5 | BIT_6 | BIT_7);
    mPortYSetPinsOut(BIT_0 | BIT_1);
    mPortYSetPinsIn(BIT_2 | BIT_3 | BIT_4 | BIT_5 | BIT_6);
    mPortYEnablePullUp(BIT_2 | BIT_3 | BIT_4 | BIT_5 | BIT_6);

  // init the threads
  PT_INIT(&pt_timer);
  PT_INIT(&pt_motor);
  PT_INIT(&pt_command);
  PT_INIT(&pt_key);
  

  // init the display
  // NOTE that this init assumes SPI channel 1 connections
  tft_init_hw();
  tft_begin();
  tft_fillScreen(ILI9340_BLACK);
  //240x320 vertical display
  tft_setRotation(0); // Use tft_setRotation(1) for 320x240

  // seed random color
  srand(1);
  
  // round-robin scheduler for threads
  while (1){
      PT_SCHEDULE(protothread_timer(&pt_timer));
      PT_SCHEDULE(protothread_motor(&pt_motor));
      PT_SCHEDULE(protothread_command(&pt_command));
      PT_SCHEDULE(protothread_key(&pt_key));  
      
      }
  } // main

// === end  ======================================================

              
              

Download is avaliable on the top of the web page

References

Thanks to Professor Bruce Land and TAs for giving us advice and help.