Appendix: Code

Download Code

/*************************************************************************************
 * happyzap's Cooking Coach by paras (phs11@cornell.edu) and rudy (rac32@cornell.edu)*
 *************************************************************************************/
/* to port back to mega103L  (backwards to port to 8515)
 * change include lines  X
 * comment out DDRC       X
 * move portD (appliances to port E)    X
 * move command lines (PORTD) back to port D X
 * move pushbuttons (portA) back to port F and kill init for port A     X
 * increase table sizes (relative to TEXT_WIDTH)
 * enable sram                                                 X
 * change TIMSK to 0x11                                        X
 * change timer interrupt vector to 13                         X
 */
#include <mega103.h>
#include <delay.h> 
#include <stdio.h>   
#include <math.h>

/*************************************************************************************
 * Constants																		 *
 *************************************************************************************/

// Global contants                             
#define NUM_RECIPES 21  // max number of recipes                          
#define NUM_STEPS 450   // max number of steps in all of the recipes collectivly
#define TEXT_WIDTH 0x28 // width of screen's text block     
#define VERTICAL_LINES 25     
#define TEXT_MEMORY_ADDR 0x0000
#define GRAPHICS_MEMORY_ADDR 0x03E8           
#define MENU_LINE 0xBC

// LCD Control line Commands (PORTD)
#define	LCDreset        0x00    // Reset the Display
#define	LCDnop          0x47    // No operation
#define	CmdSetup        0x47    // Set A0 high
#define	CmdWrite        0x43    // Set WR low
#define	DataSetup       0x07    // Set A0 low
#define	DataWrite       0x03    // Set WR low
#define	StatusRead      0x05    // Set RD low, A0 low
#define	DataRead        0x45    // Set RD low, A0 high

// LCD Commands (PORTB)
#define	SystemSet	0x40	// Initialize system
#define	SleepIn		0x53	// Enter standby mode
#define	DispOFF		0x58	// Display Off
#define	DispON		0x59	// Display On
#define	Scroll		0x44	// Initialize Address & Regions
#define	CSRForm		0x5D	// Set cursor type
#define	CharAddr	0x5C	// Set address of character RAM
#define	CSRRight	0x4C	// Cursor direction = right
#define	CSRLeft		0x4D	// Cursor direction = left
#define	CSRUp		0x4E	// Cursor direction = up
#define	CSRDown		0x4F	// Cursor direction = down
#define	HorzScroll	0x5A	// Set horz scroll position
#define	Overlay		0x5B	// Set Display Format
#define	CSRW		0x46	// Set cursor address
#define	CSRR		0x47	// Read cursor address
#define	MWRITE		0x42	// Write to display memory
#define	MREAD		0x43	// Read from display memory

// screen state variables
#define num_screen_states 6                       
#define button_label_length 10
#define _mainmenu 0
#define _optionsmenu 1
#define _selectrecipesforshoppinglist 2
#define _selectrecipe 3
#define _shoppinglist 4
#define _cooking 5
unsigned char lastscreenstate = _cooking;
unsigned char screenstate = _mainmenu;

// pushbutton variables
flash unsigned char _capture = 0;
flash unsigned char _wait = 1;
unsigned char state = _capture;
#define reload 255-117;
#define reload2 0;//255-61;
#define _scrollup 0
#define _scrolldown 1
#define _selection0 2
#define _selection1 3
#define _selection2 4
#define _selection3 5
#define _panic 6        
#define _nothing 7
unsigned char button = _nothing;

/*************************************************************************************
 * Lower Memory Variables															 *
 *************************************************************************************/

// lower memory addresses (commonly used variables)                   
unsigned char r_char, i, j, k;       
unsigned int location;     
unsigned char line_highlighted = 0;
unsigned char topLineDisplayed = 0;
unsigned char currentLowestLine = 21;  
unsigned char timersUpdated = 1;
unsigned char startActionOnLine = 22;
unsigned char startTimer1OnLine = 22;
unsigned char startTimer2OnLine = 22;

// database indicies
unsigned char r_index_x = 0;
unsigned char r_index_y = 0;
unsigned char recipe_index = 0;
                    
unsigned char t1H, t1M, t1S, t2H, t2M, t2S;	// two timers, hours, minutes and seconds 

/*************************************************************************************
 * Flash Contants																	 *
 *************************************************************************************/          

// flash tables
flash unsigned char happy_zap[11]     = {"HAPPY ZAP!"};
flash unsigned char cooking_coach[14] = {"COOKING COACH"};
flash unsigned char choose_one[11]	  = {"Choose One"};
flash unsigned char main_menu[10]     = {"MAIN MENU"}; 
flash unsigned char options_menu[13]  = {"OPTIONS MENU"}; 
flash unsigned char build_shopping_list[33] = {"SELECT RECIPES FOR SHOPPING LIST"};
flash unsigned char shopping_list[14] = {"SHOPPING LIST"};
flash unsigned char select_recipe[14] = {"SELECT RECIPE"};
flash unsigned char control_oven[15] = {"X Control Oven"};
flash unsigned char control_stove[16] = {"X Control Stove"};
flash unsigned char control_toaster[18] = {"X Control Toaster"};
flash unsigned char control_coffee_maker[23] = {"X Control Coffee Maker"};
flash unsigned char start1[9] = {" Start 1"};
flash unsigned char start2[9] = {" Start 2"};
flash unsigned char timer1[8] = {"timer 1"};
flash unsigned char timer2[8] = {"timer 2"};
flash unsigned char nine_blanks[10] = {"         "};
flash unsigned char execute[10] = {"  EXECUTE"};
flash unsigned char topBounds[num_screen_states] = {5,3,3,3,3,3};
flash unsigned char botBounds[num_screen_states] = {7,6,21,21,21,21};
flash unsigned char slider[6] = { 0b00000000, 0b00000100, 0b00001100, 0b00011100, 0b00111100, 0b01111100 };
flash unsigned char choices[num_screen_states][4][button_label_length+1] = 
{
	{ {"   ENTER"},	// main menu
	  {" "},
	  {" "},
	  {" "} },
    { {" MAIN MENU"},// options
      {"    ON"},
      {"   OFF"},
      {" "} },
    { {" MAIN MENU"},//select stuff for shopping list
	  {"  SELECT"},
	  {" UNSELECT"},
	  {"  DISPLAY"} },
    { {" MAIN MENU"},// select recipes
	  {"  SELECT"},
	  {"  REFRESH"},
	  {"   DELETE"} },
	{ {" MAIN MENU"},// shopping list
	  {" REDO LIST"},						
	  {"  CHECK"},
	  {" UNCHECK"} },
    { {" MAIN MENU"},// cooking
	  {"   NEW"},
	  {" "},
      {" "} }
 };

/*************************************************************************************
 * High Memory Tables																 *
 *************************************************************************************/

// large database variables       
unsigned char pendingTimer1[16];
unsigned char pendingTimer2[16];
unsigned char pendingAction1[5];
unsigned char pendingAction2[5];
unsigned char pendingAction[5];
unsigned char selected[NUM_RECIPES];
unsigned char controls[4] = {1,1,1,1};
unsigned char command[5];
unsigned char recipe_names[NUM_RECIPES][TEXT_WIDTH-8];	// list of recipe names
unsigned char recipe_pointers[NUM_RECIPES];				// where to find recipes in the database
unsigned char recipes[NUM_STEPS][TEXT_WIDTH-8];           // the database

/*************************************************************************************
 * Prototypes																		 *
 *************************************************************************************/

// file transfer
interrupt[UART_RXC] void uart_rec(void);                           

// button handling
void scrollup(void);
void scrolldown(void);
void PANIC(void);
void selection0(void);
void selection1(void);
void selection2(void);
void selection3(void);
void captureState(void);
void waitState(void);
interrupt[TIM0_OVF] void time0_overflow(void);     

// timing
interrupt[13] void t1a_compare_match(void);

// init screens
void displayMainMenu(void);
void displayOptionsMenu(void);
	void addOptions(unsigned char me);
	void removeOptions(unsigned char me);
void displayListCreator(void); 
	void addSelection(unsigned char me);
	void removeSelection(unsigned char me);
void displayShoppingList(void);
	void addCheck(unsigned char me);
	void removeCheck(unsigned char me);
void displayRecipeList(void);
void showRecipe(void);
	void readyToStartAction(void);
	void action(void);
	void startPendingAction(void);
	void readyToStartTimer1(void);
	void readyToStartTimer2(void);
	void startTimer1(void);
	void startTimer2(void);
	void startTimer1PendingAction(void);
	void startTimer2PendingAction(void);
	
// screen modifications
void shiftStuffDown(void);
void shiftStuffUp(void);            
void deleteRecipe(unsigned char deleteMe);

// primative LCD functions
void printWord(unsigned char flash *word);
void printBlankLine(void);
void displayButtons(void);   
void updateTimers(void);
void displayTimerLabels(void);
void displayBorders(void);					
void highlight(void);
void unHighlight(unsigned char unhighlight);
void initLCD(void);
void clearLCD(void);
void WriteCMD(unsigned char CommandCode);
void WriteDATA(unsigned char CommandCode);
void DELAY1_2(void);

// main
void main(void);
                                                        
/*************************************************************************************
 * File Transfer																	 *
 *************************************************************************************/

// executes when a character is transfered in over the serial line
interrupt[UART_RXC] void uart_rec(void)                           
{
	r_char = UDR;                       
	if (r_char != '\r' && r_char != '\n' && r_char != '\0')
		recipes[r_index_y][r_index_x++] = r_char;
	else
	{   
		recipes[r_index_y][r_index_x++] = '\0';                   
		if (recipes[r_index_y][0] == 'B')	// start of a new recipe
		{
			for (i=0;i<r_index_x-2;i++)		// copy over the recipe name
				recipe_names[recipe_index][i] = recipes[r_index_y][i+2];
			recipe_names[recipe_index][r_index_x-1] = '\0';
			recipe_pointers[recipe_index] = r_index_y;  
			recipe_index++;
		}
		r_index_y++;	// go to next line
		r_index_x = 0;
	}
}

/*************************************************************************************
 * Timing																			 *
 *************************************************************************************/

// keeps track of timers 1 and 2 by changing their times on one second ticks and executes timer based actions 
interrupt[13] void t1a_compare_match(void)
{   
	if (t1S != 0 || t1M != 0 || t1H != 0)	// if time has hit zero, don't change
	{
		t1S--;
		if (t1S == 0 && t1M == 0 && t1H == 0)	// if we just hit zero on this itteration
			startTimer1PendingAction();
		else if (t1S > 59)
		{
			t1S = 59; t1M--;
			if (t1M > 59)
			{
				t1M = 59;	t1H--; 
				if (t1H > 24) t1H = 0;
			}
		}                       
		timersUpdated = 1;
	}
	
	if (t2S != 0 || t2M != 0 || t2H != 0)	// if time has hit zero, don't change
	{         
		t2S--;                   
		if (t1S == 0 && t1M == 0 && t1H == 0)	// if we just hit zero on this itteration
			startTimer2PendingAction();
		else if (t2S > 59)
		{
			t2S = 59; t2M--;
			if (t2M > 59)
			{ 
				t2M = 59;	t2H--; 
				if (t2H > 24) t2H = 0;
			}
		}                       
		timersUpdated = 1;
	} 
}  

/*************************************************************************************
 * Button Press Handling															 *
 *************************************************************************************/

void scrollup(void) 
{
	printf("scrolling up");
	if (line_highlighted-1 >= topBounds[screenstate])
	{
		unHighlight(line_highlighted--);
		highlight();
	}
	else if (screenstate == _cooking && topLineDisplayed > 0)
		shiftStuffDown();
}

void scrolldown(void) 
{
	printf("scrolling down");
	if (line_highlighted+1 <= botBounds[screenstate] && line_highlighted+1 <= currentLowestLine)
	{
		unHighlight(line_highlighted++);
		highlight();
	}
	if (screenstate == _cooking)
	{   //printf("action line %d t1 line %d t2 line %d",startActionOnLine, startTimer1OnLine, startTimer2OnLine);                                                 
		if (line_highlighted == startActionOnLine)
			readyToStartAction();
		else if (line_highlighted == startTimer1OnLine)
			readyToStartTimer1();
		else if (line_highlighted == startTimer2OnLine)
			readyToStartTimer2();     
		else if (line_highlighted == 21)
			shiftStuffUp();
	}
}

void PANIC(void) 
{
	printf("PANIC - shutting everything off and killing automatic controls"); 
	UCR = 0x00;
	PORTE = 0x00;
	UCR = 0b10011000;
	controls[0] = 0; controls[1] = 0; controls[2] = 0; controls[3] = 0; 
}

void selection0(void) 
{
	printf("sel0");
	if (screenstate == _mainmenu)
		screenstate = line_highlighted - 4;
	else 
		screenstate = _mainmenu;
}

void selection1(void) 
{
	printf("sel1");
	if (screenstate == _selectrecipe)
		screenstate = _cooking;
	else if (screenstate == _cooking)
		screenstate = _selectrecipe;
	else if (screenstate == _selectrecipesforshoppinglist)
		addSelection(line_highlighted);
	else if (screenstate == _shoppinglist)
		state = _selectrecipesforshoppinglist;
	else if (screenstate == _optionsmenu)
		addOptions(line_highlighted);
}

void selection2(void) 
{
	printf("sel2");
	if (screenstate == _selectrecipe)
		displayRecipeList();
	else if (screenstate == _selectrecipesforshoppinglist)
		removeSelection(line_highlighted);
	else if (screenstate == _shoppinglist)
		addCheck(line_highlighted);  
	else if (screenstate == _optionsmenu)
		removeOptions(line_highlighted);
	else if (screenstate == _cooking)    
	{
		if (line_highlighted == startActionOnLine)
			startPendingAction();
		else if (line_highlighted == startTimer1OnLine)
			startTimer1();
		else printf("\r\nno longer on action or timer1 line");
	}
}

void selection3(void) 
{
	printf("sel3");             
	if (screenstate == _selectrecipe)
	{
		deleteRecipe(line_highlighted - 3);
		displayRecipeList();
	}
	else if (screenstate == _selectrecipesforshoppinglist)
		screenstate = _shoppinglist;
	else if (screenstate == _shoppinglist)
		removeCheck(line_highlighted);
	else if (screenstate == _cooking)
		startTimer2();
}

void captureState(void)
{                        
	state = _wait;
	if (PINF.0 == 0)
		button = _scrollup;
    else if (PINF.1 == 0)
		button = _scrolldown;
    else if (PINF.2 == 0)
		button = _selection0;
    else if (PINF.3 == 0)
		button = _selection1;
    else if (PINF.4 == 0)
		button = _selection2;
    else if (PINF.5 == 0)
		button = _selection3;
    else if (PINF.6 == 0)
		button = _panic;
    else                
    {
    	button = _nothing;
    	state = _capture;
    }
}

// waits until no buttons are pushed and goes back to capture state
void waitState(void)
{
	if (PINF == 0xff)       // if buttons have been released
	{
		if (button == _scrollup)
			scrollup();
		else if (button == _scrolldown)
			scrolldown();
		else if (button == _selection0)
			selection0();
		else if (button == _selection1)
			selection1();
		else if (button == _selection2)
			selection2();
		else if (button == _selection3)
			selection3();
		else if (button == _panic)
			PANIC();
		button = _nothing;
		state = _capture;
	}
}

// interrupt jump table...  should be envoked every 30mS or so 
interrupt[TIM0_OVF] void time0_overflow(void)
{
	TCNT0 = reload; 
  	if (state == _capture)
		captureState();
	else if (state == _wait)
		waitState();
}                                                                


/*************************************************************************************
 * Screen Initializations															 *
 *************************************************************************************/

// shows the mainmenu to the user and lets him/her choose which menu to see next 
void displayMainMenu(void)
{                       
	location = TEXT_MEMORY_ADDR;
	clearLCD();

	WriteCMD(CSRW);       	// print happyzap!
	WriteDATA(0x0C);
    WriteDATA(0x00);	            
	printWord(happy_zap); 
	                  
	location = TEXT_WIDTH;	// print main menu
	WriteCMD(CSRW);
	WriteDATA(location+0x0C);
	WriteDATA(0x00);
    printWord(main_menu);	
    
    location += TEXT_WIDTH + TEXT_WIDTH;  // print choices 
	WriteCMD(CSRW);
	WriteDATA(location);
	WriteDATA(location>>8);
	printWord(choose_one);

	location += TEXT_WIDTH + TEXT_WIDTH;
	WriteCMD(CSRW);
	WriteDATA(location);
	WriteDATA(location>>8);	
	printWord(options_menu);

	location += TEXT_WIDTH;
	WriteCMD(CSRW);
	WriteDATA(location);
	WriteDATA(location>>8);	
	printWord(shopping_list);

	location += TEXT_WIDTH;
	WriteCMD(CSRW);
	WriteDATA(location);
	WriteDATA(location>>8);	
	printWord(select_recipe);
	
	displayBorders();
	line_highlighted = topBounds[_mainmenu];
	currentLowestLine = botBounds[_mainmenu];
	highlight();
	displayButtons(); 
	displayTimerLabels();       
}

// lets the user choose which appliances to have controlled by the cooking coach
void displayOptionsMenu(void)
{
	location = TEXT_MEMORY_ADDR + TEXT_WIDTH;
	clearLCD();
						
	WriteCMD(CSRW);     		// prints options menu
	WriteDATA(location+0x0C);
	WriteDATA(0x00);
	WriteCMD(MWRITE);
	printWord(options_menu);
	
	location += TEXT_WIDTH<<1;	// skip two lines
	WriteCMD(CSRW);
	WriteDATA(location);
	WriteDATA(location>>8);

	WriteCMD(MWRITE);          // equip to control
	printWord(control_oven);
	location += TEXT_WIDTH;
	WriteCMD(CSRW);
	WriteDATA(location);
	WriteDATA(location>>8);
	printWord(control_stove);
	location += TEXT_WIDTH;
	WriteCMD(CSRW);
	WriteDATA(location);
	WriteDATA(location>>8);
	printWord(control_toaster);
	location += TEXT_WIDTH;
	WriteCMD(CSRW);
	WriteDATA(location);
	WriteDATA(location>>8);
	printWord(control_coffee_maker);   
	
	displayBorders();  
    displayTimerLabels();      
	line_highlighted = topBounds[_optionsmenu];    
	currentLowestLine = botBounds[_optionsmenu];
	displayButtons(); 
	highlight();   
}

// enables control of an appliance and places an X next to it
void addOptions(unsigned char me)
{            
	controls[me-3] = 0;
	location = TEXT_MEMORY_ADDR + (TEXT_WIDTH * me);
	WriteCMD(CSRW);
	WriteDATA(location);
	WriteDATA(location>>8);
	WriteCMD(MWRITE);
	WriteDATA('X');
	WriteDATA(' ');
}

// disables control of an appliance and removes the X next to it
void removeOptions(unsigned char me)
{            
	controls[me-3] = 0;
	location = TEXT_MEMORY_ADDR + (TEXT_WIDTH * me);
	WriteCMD(CSRW);
	WriteDATA(location);
	WriteDATA(location>>8);
	WriteCMD(MWRITE);
	WriteDATA(' ');
	WriteDATA(' ');
}

// shows a list of recipes to choose from to generate a shopping list
void displayListCreator(void)
{
	location = TEXT_MEMORY_ADDR;
	clearLCD();

	location = TEXT_WIDTH;           // prints heading
	WriteCMD(CSRW);
	WriteDATA(location+0x05);
	WriteDATA(0x00);
    printWord(build_shopping_list);

	for (i=0;i<NUM_RECIPES;i++)
		selected[i] = 0;

	location += TEXT_WIDTH;
    for (i=0;i<recipe_index;i++)      // prints out the recipe index with two spaces inserted before each name
	{   
		location += TEXT_WIDTH;
		WriteCMD(CSRW);
		WriteDATA(location);
		WriteDATA(location>>8); 
		j = 0;
		WriteCMD(MWRITE); 
		WriteDATA(' '); WriteDATA(' ');		
		while (recipe_names[i][j] != '\0')
			WriteDATA(recipe_names[i][j++]);			
	}
   
	displayBorders();     
	line_highlighted = topBounds[_selectrecipesforshoppinglist];
	currentLowestLine = topBounds[_selectrecipesforshoppinglist] + recipe_index - 1;
	highlight();   
	displayButtons();   
	displayTimerLabels(); 
}

// places and -> to indicate that a recipe will be used in the shopping list
void addSelection(unsigned char me)
{ 
	selected[me-3] = 1;
	location = TEXT_MEMORY_ADDR + (TEXT_WIDTH * me);
	WriteCMD(CSRW);
	WriteDATA(location);
	WriteDATA(location>>8);
	WriteCMD(MWRITE);
	WriteDATA('-');
	WriteDATA('>');
}

// removes the -> to indicate that a recipe will not be included in the shopping list
void removeSelection(unsigned char me)
{            
	selected[me-3] = 0;
	location = TEXT_MEMORY_ADDR + (TEXT_WIDTH * me);
	WriteCMD(CSRW);
	WriteDATA(location);
	WriteDATA(location>>8);
	WriteCMD(MWRITE);
	WriteDATA(' ');
	WriteDATA(' ');
}

// parses the recipe file for the recipes included in the shopping list and prints them to screen
void displayShoppingList(void)
{                            
	unsigned char numEs = 0;        // number of recipes traversed
	unsigned char numLines = 0;
	location = TEXT_MEMORY_ADDR;
	clearLCD();
						
	WriteCMD(CSRW);                 // prints title
	WriteDATA(location+0x0C);
	WriteDATA(0x00);
	i = 0;
	WriteCMD(MWRITE);
	printWord(shopping_list);
	    
	location += TEXT_WIDTH<<1;
    for (i=0;i<r_index_y;i++)
	{
		if (recipes[i][0] == 'I' && selected[numEs] == 1)
		{            
			numLines++;
			location += TEXT_WIDTH;
			WriteCMD(CSRW);
			WriteDATA(location);
			WriteDATA(location>>8); 
			j = 2;
			WriteCMD(MWRITE);
			WriteDATA(' ');
			WriteDATA(' ');
			while (recipes[i][j] != '\0')
			{
/*				if (j == 29 || j == 58 || j == 87)
				{
					location += TEXT_WIDTH;
					WriteCMD(CSRW);
					WriteDATA(location);
					WriteDATA(location>>8);
					WriteCMD(MWRITE);
				}*/
				WriteDATA(recipes[i][j++]);			
			}
		}
		if (recipes[i][0] == 'E')
			numEs++;
	}
                    
	displayBorders();     
//	unHighlight(line_highlighted-3);
	line_highlighted = topBounds[_shoppinglist];
	currentLowestLine = topBounds[_shoppinglist] + numLines - 1;
	highlight(); 	
	displayButtons(); 
	displayTimerLabels();   
}

// adds an X next to each ingredient as the user gets them
void addCheck(unsigned char me)  
{
	location = TEXT_MEMORY_ADDR + (TEXT_WIDTH * me);
	WriteCMD(CSRW);
	WriteDATA(location);
	WriteDATA(location>>8);
	WriteCMD(MWRITE);
	WriteDATA('X');
	WriteDATA(' ');
}                                                         

// lets the user undo a checked off ingredient
void removeCheck(unsigned char me)
{
	location = TEXT_MEMORY_ADDR + (TEXT_WIDTH * me);
	WriteCMD(CSRW);
	WriteDATA(location);
	WriteDATA(location>>8);
	WriteCMD(MWRITE);
	WriteDATA(' ');
	WriteDATA(' ');
}

// displays the list of recipes so that the user can choose one to execute
void displayRecipeList(void)
{                       
	location = TEXT_MEMORY_ADDR;
	clearLCD();

	location = TEXT_WIDTH;          // prints title
	WriteCMD(CSRW);
	WriteDATA(location+0x0C);
	WriteDATA(0x00);
    printWord(select_recipe);

	location += TEXT_WIDTH;          
    for (i=0;i<recipe_index;i++)     // write out the name of each recipe
	{   
		location += TEXT_WIDTH;
		WriteCMD(CSRW);
		WriteDATA(location);
		WriteDATA(location>>8); 
		j = 0;
		WriteCMD(MWRITE);
		while (recipe_names[i][j] != '\0')
			WriteDATA(recipe_names[i][j++]);			
	}
   
	displayBorders();       
	line_highlighted = topBounds[_selectrecipe];
	currentLowestLine = topBounds[_selectrecipe] + recipe_index - 1;
	highlight();    
	displayButtons(); 
	displayTimerLabels(); 
}

// the meat of the project, this displays a choosen recipe, executes the actions as the user hits the
// designated line and pushes a button.  it also starts timers to drive buzzers and do other actions
void showRecipe(void)                         
{                                            
	unsigned char end;      
	unsigned char numLines = 0;
	unsigned int locallocation;
	startActionOnLine = 22;
	startTimer1OnLine = 22;
	startTimer2OnLine = 22;
	end = recipe_pointers[line_highlighted-2];
	if (end == 0)
		end = r_index_y;
	
	locallocation = TEXT_MEMORY_ADDR + TEXT_WIDTH;
	clearLCD();
						
	WriteCMD(CSRW);                  // show name of the recipe
	WriteDATA(locallocation+0x0C);
	WriteDATA(0x00);
	i = 0;
	WriteCMD(MWRITE);
	while (recipe_names[line_highlighted-3][i] != '\0')
		WriteDATA(recipe_names[line_highlighted-3][i++]);
    
	locallocation += TEXT_WIDTH;
    for (i=recipe_pointers[line_highlighted-3];i<end;i++)	// for each line in the recipe
	{
		if (recipes[i][0] == 'I' || recipes[i][0] == 'S')  	// display ingredients or steps
		{
			numLines++;
			locallocation += TEXT_WIDTH;
			WriteCMD(CSRW);
			WriteDATA(locallocation);
			WriteDATA(locallocation>>8); 
			j = 2;
			WriteCMD(MWRITE);
			while (recipes[i][j] != '\0')
			{
				if (j == 29 || j == 58 || j == 87)
				{
					locallocation += TEXT_WIDTH;
					WriteCMD(CSRW);
					WriteDATA(locallocation);
					WriteDATA(locallocation>>8);
					WriteCMD(MWRITE);
				}
				WriteDATA(recipes[i][j++]);			
			}
		}
		else if (recipes[i][0] == 'A' && controls[recipes[i][2]-'0'] == 1)	// executes a simple action
		{      
			startActionOnLine = topBounds[_cooking] + numLines - 1;
			for (k=0;k<5;k++)
				pendingAction[k] = recipes[i][k];
			readyToStartAction();
		}
		else if (recipes[i][0] == 'T')       	// prepares to start a timer
		{   
            if (recipes[i][1] == '1') 
            {                    
            	startTimer1OnLine = topBounds[_cooking] + numLines - 1;
            	j = 0;
	            while (recipes[i][j] != '\0')
	            {
	            	pendingTimer1[j] = recipes[i][j];
	            	j++;
	            }
			}
            else if (recipes[i][1] == '2')                             
            {                                                          
               	startTimer2OnLine = topBounds[_cooking] + numLines - 1;
            	j = 0;
	            while (recipes[i][j] != '\0')
	            {
	            	pendingTimer2[j] = recipes[i][j];
	            	j++;
	            }
			}
		}
	}
	displayBorders();       
//	unHighlight(line_highlighted-3);
	line_highlighted = topBounds[_cooking];
	currentLowestLine = topBounds[_cooking] + numLines - 1;
	highlight(); 	
	displayButtons(); 
	displayTimerLabels(); 
}

// performs an action (controls an appliance)
void action()
{
	UCR = 0x00;
	if(command[0] == 'A' && controls[command[2]-'0'] == 1)
		PORTE = slider[command[4]-'0'];
	UCR = 0b10011000;  
                                    
	location = TEXT_MEMORY_ADDR + (TEXT_WIDTH * 24) + 20;
	WriteCMD(CSRW);
	WriteDATA(location);			// removes the button
	WriteDATA(location>>8);
  	printWord(nine_blanks);
}

// creates a button to execute an action
void readyToStartAction()
{
	location = TEXT_MEMORY_ADDR + (TEXT_WIDTH * 24) + 20;
	WriteCMD(CSRW);
	WriteDATA(location);
	WriteDATA(location>>8);   
	printWord(execute);
}

// once the user pushes the button, load the action and execute it
void startPendingAction(void)
{
	for (k=0;k<5;k++)
		command[k] = pendingAction[k];
	action();
}

// creates a button to start timer1
void readyToStartTimer1(void)
{
	location = TEXT_MEMORY_ADDR + (TEXT_WIDTH * 24) + 20;
	WriteCMD(CSRW);
	WriteDATA(location);
	WriteDATA(location>>8);
	printWord(start1);
}                             

// creates a button to start timer2
void readyToStartTimer2(void)
{
	location = TEXT_MEMORY_ADDR + (TEXT_WIDTH * 24) + 30;
	WriteCMD(CSRW);
	WriteDATA(location);
	WriteDATA(location>>8);
	printWord(start2);
}                             

// starts timer1 when the user pushes the appropriate button
void startTimer1(void)
{
	if (pendingTimer1[0] == 'T' && pendingTimer1[1] == '1')	// makes sure we are looking at a timer thing and not an action
	{
		t1H = (pendingTimer1[3]-'0')*10 + (pendingTimer1[4]-'0');
		t1M = (pendingTimer1[6]-'0')*10 + (pendingTimer1[7]-'0');
		t1S = (pendingTimer1[9]-'0')*10 + (pendingTimer1[10]-'0');
		if (pendingTimer1[12] == 'A')
			for (j=0;j<5;j++)
				pendingAction1[j] = pendingTimer1[j+12];
	}                                                    
	else printf("Bad timer number %d", pendingTimer1[1]);
	pendingTimer1[1] = '\0'; 
	location = TEXT_MEMORY_ADDR + (TEXT_WIDTH * 24) + 20;
	WriteCMD(CSRW);
	WriteDATA(location);		// removes the button
	WriteDATA(location>>8);
	printWord(nine_blanks);
}

// starts timer2 when the user pushes the appropriate button
void startTimer2(void)
{
	if (pendingTimer2[1] == '2')
	{                      
		t2H = (pendingTimer2[3]-'0')*10 + (pendingTimer2[4]-'0');
		t2M = (pendingTimer2[6]-'0')*10 + (pendingTimer2[7]-'0');
		t2S = (pendingTimer2[9]-'0')*10 + (pendingTimer2[10]-'0');
		if (pendingTimer2[12] == 'A')
			for (j=0;j<5;j++)
				pendingAction2[j] = pendingTimer2[j+12];
	}
	else printf("Bad timer number %d", pendingTimer2[1]);
	pendingTimer2[1] = '\0';

	location = TEXT_MEMORY_ADDR + (TEXT_WIDTH * 24) + 30;
	WriteCMD(CSRW);				// removes the button
	WriteDATA(location);
	WriteDATA(location>>8);
	printWord(nine_blanks);
}

// start the action which executes when timer1 expires
void startTimer1PendingAction()
{
	for (k=0;k<5;k++)
		command[k] = pendingAction1[k];
	action();
}

// starts the action which executes when timer2 expires
void startTimer2PendingAction()
{
	for (k=0;k<5;k++)
		command[k] = pendingAction2[k];
	action();
}

/*************************************************************************************
 * Screen Modifications																 *
 *************************************************************************************/

void shiftStuffDown(void)
{	
}

void shiftStuffUp(void)
{
}

// expunges a recipe from the main databse
void deleteRecipe(unsigned char deleteMe)
{
	unsigned char deleteFrom, deleteTo, difference;
	deleteFrom = recipe_pointers[deleteMe];
	deleteTo = recipe_pointers[deleteMe+1];
	if (deleteTo == 0)
		deleteTo = r_index_y;
	difference = deleteTo - deleteFrom;
	// moves all recipes up
	for (i=deleteFrom;i<r_index_y;i++)
		for (j=0;j<TEXT_WIDTH;j++)
			recipes[i][j] = recipes[i+difference][j];

	// moves up each recipe name
	for (i=deleteMe;i<recipe_index;i++)
    {          
    	for (j=0;j<TEXT_WIDTH-1;j++)
		 	recipe_names[i][j] = recipe_names[i+1][j];
        recipe_pointers[i] = recipe_pointers[i+1];
	}
	for (j=0;j<TEXT_WIDTH;j++)
		recipe_names[NUM_RECIPES][j] = 0;
	recipe_pointers[NUM_RECIPES] = 0;

	r_index_y -= difference;
	recipe_index--;
}

/*************************************************************************************
 * Primative LCD Functions															 *
 *************************************************************************************/

// prints a word to the LCD
void printWord(unsigned char flash *word)
{   
	unsigned char offset = 0;
	WriteCMD(MWRITE);
	while (*(word+offset) != '\0')
	{
		WriteDATA(*(word+offset));
		offset++;
	}
}
                           
// prints a blank line to the LCD
void printBlankLine(void)
{   
	unsigned char w;
	WriteCMD(MWRITE);
	for (w=0;w<TEXT_WIDTH;w++)
		WriteDATA(' ');
}
                                  
// puts the labels for the timer on the LCD                                          
void displayTimerLabels(void)
{
	location = TEXT_MEMORY_ADDR + (TEXT_WIDTH * 7) + 32;
	WriteCMD(CSRW);
	WriteDATA(location);
	WriteDATA(location>>8);
	
	printWord(timer1);     
	
	location = TEXT_MEMORY_ADDR + (TEXT_WIDTH * 15) + 32;
	WriteCMD(CSRW);
	WriteDATA(location);
	WriteDATA(location>>8);                      
	
	printWord(timer2);	
}

// when the timer ticks we update the timer on the screen
void updateTimers(void)
{
	unsigned char bit1;
	unsigned char bit0;
	
	location = TEXT_MEMORY_ADDR + (TEXT_WIDTH * 8) + 32;
	WriteCMD(CSRW);
	WriteDATA(location);
	WriteDATA(location>>8);
	
	WriteCMD(MWRITE);
	bit1 = floor(t1H/10);
	bit0 = t1H - (bit1*10);
	WriteDATA(bit1+'0');
	WriteDATA(bit0+'0');
	WriteDATA(':');

	bit1 = floor(t1M/10);
	bit0 = t1M - (bit1*10);
	WriteDATA(bit1+'0');
	WriteDATA(bit0+'0');
	WriteDATA(':');

	bit1 = floor(t1S/10);
	bit0 = t1S - (bit1*10);
	WriteDATA(bit1+'0');
	WriteDATA(bit0+'0');
	
	location = TEXT_MEMORY_ADDR + (TEXT_WIDTH * 16) + 32;
	WriteCMD(CSRW);
	WriteDATA(location);
	WriteDATA(location>>8);                      
	
	WriteCMD(MWRITE);
	bit1 = floor(t2H/10);
	bit0 = t2H - (bit1*10);
	WriteDATA(bit1+'0');
	WriteDATA(bit0+'0');
	WriteDATA(':');

	bit1 = floor(t2M/10);
	bit0 = t2M - (bit1*10);
	WriteDATA(bit1+'0');
	WriteDATA(bit0+'0');
	WriteDATA(':');

	bit1 = floor(t2S/10);
	bit0 = t2S - (bit1*10);
	WriteDATA(bit1+'0');
	WriteDATA(bit0+'0');
}

// looks up the appropriate buttons to display for each screen state and puts them on screen
void displayButtons(void)
{   
	unsigned char r,s;
	location = TEXT_MEMORY_ADDR + (TEXT_WIDTH * 24);
	// first clear out current line
	WriteCMD(CSRW);
	WriteDATA(location);
	WriteDATA(location>>8);	                 
	 	
	WriteCMD(CSRW);
	WriteDATA(location);
	WriteDATA(location>>8);

	for (r=0;r<4;r++)
	{                
		WriteCMD(MWRITE);
		s = 0;
		while (choices[screenstate][r][s] != '\0')
			WriteDATA(choices[screenstate][r][s++]); 
		
		location += button_label_length;
		WriteCMD(CSRW);
		WriteDATA(location);
		WriteDATA(location>>8);
	} 	
}                             

// displays the lines dividing each window
void displayBorders(void)
{                        
 	location = GRAPHICS_MEMORY_ADDR + (TEXT_WIDTH * 16);    // horizontal line, 2 spaces down
 	WriteCMD(CSRW);
 	WriteDATA(location);
 	WriteDATA(location>>8);
 	WriteCMD(MWRITE);
	for (i=0;i<(TEXT_WIDTH<<1);i++)
		WriteDATA(0xFF);
		
	location = GRAPHICS_MEMORY_ADDR + (TEXT_WIDTH * MENU_LINE);// horizontal line across bottom
 	WriteCMD(CSRW);
 	WriteDATA(location);
 	WriteDATA(location>>8);
 	WriteCMD(MWRITE);
	for (i=0;i<(TEXT_WIDTH<<1);i++)
		WriteDATA(0xFF);
 	
 	location = GRAPHICS_MEMORY_ADDR + (TEXT_WIDTH * 18) + 30;
	for (i=18;i<MENU_LINE-2;i++)
	{
		location += TEXT_WIDTH;
		WriteCMD(CSRW);
		WriteDATA(location);
		WriteDATA(location>>8);
		WriteCMD(MWRITE);                                                      
		
		WriteDATA(0x0C); 		
 	}
 		
 	location = GRAPHICS_MEMORY_ADDR + (TEXT_WIDTH * MENU_LINE);
	for (i=MENU_LINE;i<201;i++)
	{		
		WriteCMD(CSRW);
		WriteDATA(location);
		WriteDATA(location>>8);
		WriteCMD(MWRITE);
		WriteDATA(0x0C);
		
		location += 10;
		WriteCMD(CSRW);
		WriteDATA(location);
		WriteDATA(location>>8);
		WriteCMD(MWRITE);
		WriteDATA(0x8C);
		
		location += 10;
		WriteCMD(CSRW);
		WriteDATA(location);
		WriteDATA(location>>8);
		WriteCMD(MWRITE);
		WriteDATA(0x8C);                                       

		location += 10;
		WriteCMD(CSRW);
		WriteDATA(location);
		WriteDATA(location>>8);
		WriteCMD(MWRITE);
		WriteDATA(0x8C);
		
		location += 10;
	}	
	
	location = GRAPHICS_MEMORY_ADDR + (TEXT_WIDTH * 190) + 39;
	for (i=190;i<201;i++)
	{		
		location += TEXT_WIDTH;
		WriteCMD(CSRW);
		WriteDATA(location);
		WriteDATA(location>>8);
		WriteCMD(MWRITE);
		WriteDATA(0x01);
	 } 	                
}

// highlights line_highlighted by using an XOR operation between layers
void highlight(void)
{
	location = GRAPHICS_MEMORY_ADDR + ( (line_highlighted * TEXT_WIDTH)<<3);
	location -= TEXT_WIDTH;
	WriteCMD(CSRW);
	WriteDATA(location);
	WriteDATA(location>>8);
	
	for (i=0;i<9;i++)
	{
		WriteCMD(CSRW);
		WriteDATA(location);
		WriteDATA(location>>8);
		
		WriteCMD(MWRITE);
		for (j=0;j<TEXT_WIDTH-10;j++)
			WriteDATA(0xff);
		location += TEXT_WIDTH;
	}
} 	

// unhighlights the line unhighlight
void unHighlight(unsigned char unhighlight)
{
	location = GRAPHICS_MEMORY_ADDR + ( (unhighlight * TEXT_WIDTH)<<3);
	location -= TEXT_WIDTH;
	WriteCMD(CSRW);
	WriteDATA(location);
	WriteDATA(location>>8);
	
	for (i=0;i<9;i++)
	{
		WriteCMD(CSRW);
		WriteDATA(location);
		WriteDATA(location>>8);
		
		WriteCMD(MWRITE);
		for (j=0;j<TEXT_WIDTH-10;j++)
			WriteDATA(0x00);
		location += TEXT_WIDTH;
	}
} 	

// LCD initialization commands, see datasheet for details
void initLCD(void)
{
  //initialize the LCD
  DDRD = 0xFF;	
  DDRB = 0xFF;
  PORTD = LCDnop;
  TCCR0 = 0x02;

  DELAY1_2();
  DELAY1_2();
  DELAY1_2();
  DELAY1_2();
  PORTD = LCDreset;
	
  DELAY1_2();
  DELAY1_2();
  DELAY1_2();
    
  PORTD = LCDnop;

  DELAY1_2();
  DELAY1_2();
  DELAY1_2();
  DELAY1_2();
          
  WriteCMD(SystemSet);
  WriteDATA(0x30);
  WriteDATA(0x87);
  WriteDATA(0x07);
  WriteDATA(0x27);
  WriteDATA(0x2F);
  WriteDATA(0xC7);
  WriteDATA(0x28);
  WriteDATA(0x00);

  WriteCMD(Overlay);
  WriteDATA(0x01);

  WriteCMD(Scroll);
  WriteDATA(0x00);
  WriteDATA(0x00);
  WriteDATA(0xC8);
  WriteDATA(0xE8);
  WriteDATA(0x03);
  WriteDATA(0xC8);
		
  WriteCMD(CSRForm);
  WriteDATA(0x04);
  WriteDATA(0x86);
	
  WriteCMD(CSRRight);
		
  WriteCMD(HorzScroll);
  WriteDATA(0x00);
  
  WriteCMD(DispON);
  WriteDATA(0x14);
	
  clearLCD();
  
  WriteCMD(CSRW);
  WriteDATA(0x0F);
  WriteDATA(0x00);

}

void WriteCMD(unsigned char CommandCode) 
{
  PORTB = CommandCode;
  PORTD = CmdSetup;
  PORTD = CmdWrite;
  PORTD = CmdSetup;
  return;
}

void WriteDATA(unsigned char CommandCode) 
{
  PORTB = CommandCode;
  PORTD = DataSetup;
  PORTD = DataWrite;
  PORTD = DataSetup;
  return;
}

void clearLCD(void) 
{
  unsigned short i;

  WriteCMD(CSRRight);

  WriteCMD(CSRW);
  WriteDATA(0x00);
  WriteDATA(0x00);

  WriteCMD(MWRITE);

  for (i=0; i<1000; i++)
    WriteDATA(0x20);    // write " " to character memory
    
  WriteCMD(CSRW);
  WriteDATA(0xE8);
  WriteDATA(0x03);

  WriteCMD(MWRITE);

  for (i=0; i<8000; i++)
    WriteDATA(0x00);    // erase graphics memory
 
  return;
} // end clearLCD

void DELAY1_2(void) 
{
  unsigned char curr;
  
  TCNT0 = 0x00;
  do
    curr = TCNT0;
  while(curr < 250);  // 250 * 2us = 1/2 ms delay
  return;
} // end DELAY1_2
         
/*************************************************************************************
 * Testing																			 *
 *************************************************************************************/

void testUART(void)
{
	for (i=0;i<r_index_y;i++)
	{
		for (j=0;j<TEXT_WIDTH;j++)
		{   
			if (recipes[i][j] == '\0')
				break;
			putchar(recipes[i][j]);			
		}
	printf("\r\n");
 	}
 	for (i=0;i<recipe_index;i++)
	{
		for (j=0;j<TEXT_WIDTH;j++)
		{   
			if (recipe_names[i][j] == '\0')
				break;
			putchar(recipe_names[i][j]);			
		}          
		printf("\r\n");
		printf("%d",recipe_pointers[i]);
		printf("\r\n");
 	}                                         
	printf("\r\n"); 
}

/*************************************************************************************
 * Main																				 *
 *************************************************************************************/

void main(void)
{                     
	MCUCR = 0x80;			// enable external ram     
	i = 0; j = 0; t1S = 0; t1M = 0; t1H = 0; t2S = 0; t2M = 0; t2H = 0;
			// ***** kill when switching to port F
	DDRE = 0xFF;

	// UART stuff
	UCR = 0b10011000;
	UBRR = 25;                             
	printf("\r\nready\r\n");
    initLCD();
    
  	// setup timer1 to interrupt every second for food timings
  	TCCR1B = 0b00001101;	// sets timer1A to prescale /1024 and clear on campare match
  	TCNT1H = 0x00;			// clear timer1
  	TCNT1L = 0x00;
  	OCR1AH = 0x0f; 			// count up to 3906.25
  	OCR1AL = 0x42;

	// setup timer0 to interrupt every 30ms or so to deal with button presses
	TCCR0 = 0x05;			// prescaler = /128
  	TCNT0 = reload;                   
  	
  	TIMSK = 0x11;			// enable timer1A compare interrupt and timer0 overflow
    #asm
    sei
    #endasm

  	while (1)
  	{
  		if ( screenstate != lastscreenstate )
  		{   //testUART();       
  			lastscreenstate = screenstate;
  			if (screenstate == _mainmenu)
  				displayMainMenu();
  			else if (screenstate == _optionsmenu)
  				displayOptionsMenu();
  			else if (screenstate == _selectrecipesforshoppinglist)
  				displayListCreator();
  			else if (screenstate == _shoppinglist)
  				displayShoppingList();
  			else if (screenstate == _selectrecipe)
  				displayRecipeList();
  			else if (screenstate == _cooking)
  				showRecipe();
  			else printf("Unknown Screen State");
  		}
  	   	if (timersUpdated == 1)
  		{
  			timersUpdated = 0;
  			updateTimers();
  		}    
  	}
}