#include <avr/io.h>
#include <stdlib.h>
#include <util/delay.h>
#include <string.h>
#include <avr/pgmspace.h>

// The RFID Skull Project (or how to play HALO Multiplayer in real life)
// www.imakeprojects.com
// 
// theprojectmaker@imakeprojects.com
// June 2008 last worked on. Published to WWW on Jan 2009.



// ATMEGA8
//
// Written for use on ATMEL ATMEGA8 microcontroller.
// Uses default internal clock of 1MHz.
//
//
// Pins and Functions
// ------------------
// PD0	Pin 2 	= RS232 RX (TTL, inverted) from RFID reader SEROUT, through an inverter
// PD1	Pin 3 	= RS232 TX (TTL, inverted) to serial LCD, through an inverter
// PB0	Pin 14 	= /ENABLE line of RFID reader
// PD6	Pin 12	= BUZZER
// PB2 	Pin	16	= /BUTTON B (Right Eye)
// PB1 	Pin 15	= /BUTTON A (Left Eye)
//
//
// ATMEGA8 Physical Layout
// 
//										.---------.
//								/RESET	|1		28| PC5
// RFID inverted SEROUT output 	RX (PD0)|2		27| PC4
// To serial LCD input 	        TX (PD1)|3		26| PC3
// 								   	PD2 |4		25| PC2
//								    PD3	|5		24| PC1
// 								   	PD4	|6		23| PC0
// 									VCC	|7		22| AGND (to GND)
// 									GND	|8		21| AREF
// 								   	PB6	|9		20| AVCC (to +5)
// 								   	PB7	|10		19| SCK  (to ISP)
// 								   	PD5	|11		18| MISO (to ISP)
// 						To Buzzer 	PD6 |12		17| MOSI (to ISP)
// 					To LED driver  	PD7	|13		16| PB2 To /BUTTON B (Right Eye)
// 		To /ENABLE of RFID reader	PB0	|14		15| PB1 To /BUTTON A (Left Eye)
//										`---------'
//
// Inverter: 74HC14 Physical Layout
//
// 						.----------.
// in from TX (Pin3)	| 1		14 | VCC
// out to LCD			| 2		13 |
// in from RFID SEROUT	| 3		12 |
// (jumper to 5)		| 4		11 |
// (jumper from 4)		| 5		10 |
// out to RX (Pin2)		| 6		 9 |
// GND					| 7		 8 |
// 						`----------'
//
//
// Buttons:
// Normally open, pullups to +5V
//
// Buzzer:
// Piezo buzzer, +5V
//
// LED:
// High brightness plug-in driver on PD7.
// Digital output (to a transistor driver for LED)
//
// Serial LCD:
// Matrix Oribital LCD0821 (8x2)
// Modified from default RS232 to TTL
// Modified from default 19200 baud to 2400 baud




//////////////////////////////////////////////////////////////////////////////////////
//
// GAME OVERVIEW
// -------------
//
// 10 players max
// Standard "game" is 30 mins total. (1800 seconds)
//
//
// Each player has an RFID tag.  The admin has one also which is not used for play.
// 
// Players are assigned a random "nickname" upon first using their tag.  
// The player should learn this tag.
//
// OBJECTIVE:
// To be the player that held the skull the longest.
//
// Rules:
// - Upon grabbing the skull, activate it by depressing both eye-socket buttons.
// - You must then scan your RFID tag.  The skull will then count your time.
// - Hold the skull with both buttons down until you are shot, at which point the skull 
//   must be dropped.
// - Releasing the skull releases the buttons which stops your session time.
// 
// At the end of the game (time runs out, or admin scans his/her tag) the skull will 
// report stats and declare a winner.
//
//////////////////////////////////////////////////////////////////////////////////////


// CHANGE LOG
/*
	Version:  1.0

	June 18, 2008
		- Modifed code to allow players to press both socket switches, then 
			let go in order to shift their grip (if needed) to scan their tag,
			as long as they press both sockets again within 2 seconds.  Otherwise
			because of the physical location of the RFID reader it can require some 
			serious wrist-fu gymnastics to scan the skull over the tag without
			letting go of the eye socket switches.
*/





// *********************************
// Initializations, Defines, etc
// *********************************



///////////////////////////////////////////////////////////////////////////
// Macros
//
// Set Bit (PORT, BITNUM)
#define	sbi(var, mask)		((var) |= (uint8_t)(1<<mask))
//
// Clear Bit (PORT, BITNUM)
#define	cli(var, mask)		((var) &= (uint8_t)~(1<<mask))
//
// End Macros
///////////////////////////////////////////////////////////////////////////




///////////////////////////////////////////////////////////////////////////
// Defines
// 
// Tells the code where each pin lives.  If the pin mappings are moved, 
// These should be updated and the rest should take care of itself.
#define	BUZZER_PORT			PORTD
#define	BUZZER_PORT_DDR		DDRD
#define	BUZZER_BIT_NUMBER	6
//
#define	RFID_PORT				PORTB
#define	RFID_PORT_DDR			DDRB
#define	RFID_ENABLE_BIT_NUMBER	0
//
#define BUTTONS_PORT		PINB	// Buttons are inputs
#define BUTTONS_PORT_DDR	DDRB
#define BUTTON_A_BIT_NUMBER	1
#define BUTTON_B_BIT_NUMBER	2
//
// 
// These defines are for the game parameters
#define	MAX_PLAYERS			10		// Room for this many tags in memory.
// 1800 = 30 mins, 900 = 15 mins, 600 = 10 mins
#define	GAME_LENGTH			900 	// This value must be at least 100 seconds.

///////////////////////////////////////////////////////////////////////////




//////////////////////////////////////
// Global Stuff
//
char	RFID_latest_tag[13];	// Big enough for 12byte tag (incl start and stop bytes)

char	button_a_state;			// State tracking for buttons (0 when pressed)
char	button_b_state;			

char	game_ended;			// Game end flag.

char 	admin_tag[11];			// An admin tag, used as a start and stop signal.

char 	player_tag[MAX_PLAYERS][11];	// Variables to identify players
int		player_nick_index[MAX_PLAYERS];	// Array if indices to nickname for this player.
int		player_time[MAX_PLAYERS];		// Array index valid [0-9]
int		player_time_total[MAX_PLAYERS];		// Array index valid [0-9]
int		player_highscore[MAX_PLAYERS];		// Array index valid [0-9]
int		player_rank[MAX_PLAYERS];			// Player rank (calculated at end)
int		player_times_held[MAX_PLAYERS];
int		active_player_index;
	
int		highscore;			// The current high-score (in seconds)

int		time_passed;			// Total time passed (active or not)


// Sadly this is static and can't scale with MAX_PLAYERS
char	nicknames[10][8] = { "wiggles", "lefty", "boomer", "flexy", "saucy", "feeler", "whitey", "handy", "nasty", "crazy" };
int		next_available_nickname;

// End Global Stuff
//////////////////////////////////////



// Function Prototypes
//
static inline void setup_rs232(int baudrate);		// Called only once at startup.
// "static inline" tells the compiler to try to put it inline and therefore not bother
// with a lot of the setup/overhead of a typical function.
void 	beep(void);					// Emit a short beep on piezo.
void 	shortbeep(void);			// Emit a shorter beep on piezo.
void 	longbeep(void);				// Emit a longer beep on piezo.
void	errbeep(void);				// Error beep sound
void 	TXbyte(char data);			// Send one byte out the UART (rs232 serial).
char 	RXbyte(void);				// [Wait for and] receive one byte from the UART.
void 	serout(char *s);			// Send a string out the UART, one char at a time.
void 	serout_P(const char *s);	// Version for strings residing in PROG memory, not RAM
void 	RFID_enable(void);			// Activate the RFID reader so can read tags.
void 	RFID_disable(void);			// Deactivate the RFID reader (low power, no read tags).
int 	read_RFID(int wait_for_START_byte);	// Turn reader on and perform a tag read.
void 	lcd_clear(void);			// Clear the serial display.
void 	lcd_xy(char x, char y);		// Send the serial display's cursor somewhere.
//
// NOTES:
//   serout("This string consumes RAM even though it may never change.");
//     vs.
//   serout_P( PSTR("This string lives only in Program Memory, saving RAM!") );
//
//   - The 'PSTR' part is a macro provided by pgmspace.h.
//   - Saving RAM is important especially with a low-RAM part like the ATTINY2313
//     which has only 128 bytes of ram.  
//   - Compiler result of 53% RAM usage is probably enough that the stack smashes 
//     into the rest of the RAM and therefore corrupts data, so it's important here.
//
// End Prototypes


// *********************************
// Main
// *********************************


int main (void)
{
	////////////////////////////
	// Variable Declarations
	//
	int 	i,x,z;					// General purpose integers
	char	string[11];				// Misc string

	//
	// End variable declarations
	////////////////////////////
	

	
	//////////////////////////////
	// Initializations and Setup 
	//
	// Setup the serial comms

	setup_rs232(2400);		// Do all setup for 2400baud, 8N1
							// TTL inverted RS232 where Pin2=RX and Pin3=TX

	// Serial display setup
	//
	// Wait a few moments to show the splash screen
	_delay_ms(1000);
	TXbyte(0xfe); TXbyte('D');	// Auto Line wrapping off (C is on)
	TXbyte(0xfe); TXbyte('R');	// Auto scroll off (Q is on)
	TXbyte(0xfe); TXbyte('K');	// Underline Cursor off (on is 'J')
	TXbyte(0xfe); TXbyte('T');	// Blinking Cursor off (on is 'S')
	TXbyte(0xfe); TXbyte(0x99); TXbyte(56); // 0-255 brightness
	TXbyte(0xfe); TXbyte(0x50); TXbyte(127); // 0-255 contrast
	lcd_clear();				// Clear the display

	TXbyte(0xfe);	// Achtung!
	TXbyte('h');	// Init horizontal bar (i.e. load the bar custom chars into the memory)

	
	// Direction Registers for local operations
	// (I/O functions take care of their own setting of DDR, etc)
	//
	BUTTONS_PORT_DDR &= ~(0x01 << BUTTON_A_BIT_NUMBER);	// Input for thumb-button A
	BUTTONS_PORT_DDR &= ~(0x01 << BUTTON_B_BIT_NUMBER);	// Input for thumb-button B

	//
	// End initializations and Setups
	//////////////////////////////////

	

	/////////////////////////////////////////////////////////////////
	//
	// MAIN Functionality of code
	//
	// - Allow players to pre-register tags if desired (Helps "train" players to do
	//   it correctly and assigns nicknames)
	// - Or game can be initiated with an ADMIN tag scan.
	// - Once that's done, the game begins.
	//
	/////////////////////////////////////////////////////////////////

	serout_P( PSTR(" Jab an\n Eye!") );

	// Wait for either button to be pushed to wake up
	do
	{
		button_a_state = BUTTONS_PORT & (0x01<<BUTTON_A_BIT_NUMBER); // '0' when pushed
		button_b_state = BUTTONS_PORT & (0x01<<BUTTON_B_BIT_NUMBER); // '0' when pushed
		_delay_ms(10);
	}
	while( button_a_state && button_b_state );
	// Wait for buttons to be released
	do
	{
		button_a_state = BUTTONS_PORT & (0x01<<BUTTON_A_BIT_NUMBER); // '0' when pushed
		button_b_state = BUTTONS_PORT & (0x01<<BUTTON_B_BIT_NUMBER); // '0' when pushed
		_delay_ms(10);
	}
	while( (button_a_state==0) || (button_b_state==0) );


	do
	{
		lcd_clear();
		serout_P( PSTR("<Add Tag\n") );
		serout_P( PSTR("Game Go>") );

		do
		{
			button_a_state = BUTTONS_PORT & (0x01<<BUTTON_A_BIT_NUMBER); // '0' when pushed
			button_b_state = BUTTONS_PORT & (0x01<<BUTTON_B_BIT_NUMBER); // '0' when pushed
			_delay_ms(10);
		}
		while( button_a_state && button_b_state );



		if( button_a_state == 0)
		{
			/////////////////////////////////
			// Pre-register a player's tag
			/////////////////////////////////

			lcd_clear();
			serout_P( PSTR("Scan a\ntag now") );		
	
			longbeep();		// longbeep() is considered the "please scan tag" sound.
			x = read_RFID(0);
			while( x!=0 )		// Re-read on error.
			{
				//errbeep();	// BreepBreepBreep is "error, please re-scan"
				_delay_ms(250);
				x = read_RFID(1);	// arg '1' = "Try Harder" (basically)
			}
			// Successful read sound
			beep();beep();beep();	

			// Determine if the tag read is already registered or not.
			// Look through the list until one of the following conditions is true:
			// - We run out of list entries
			// - We get a match (x==0)
			for (x=1, i=0; (i<MAX_PLAYERS) && (x!=0); i++)
			{
				x = strcmp(player_tag[i], RFID_latest_tag);
				if( x == 0)
					active_player_index = i;
			}

			// If x==0 then we have a match and [active_player_index] is the 
			// index of the player whose tag is currently held.
			//
			// If no match, add the player's tag to the next available slot (available 
			// player_tag[i][] slots have a NULL in the first element) and point
			// active_player_index to the index of the new entry.
			//
			// Player info variables:
			//  player_tag[][]				- RFID tag for the player
			// 	player_nick_index[]			- Index to nicknames[] for this player
	
			if( x==0 )		// Player tag already exists
			{
				lcd_clear();
				serout(nicknames[player_nick_index[active_player_index]]);
				serout_P( PSTR("\nexists!") );
				_delay_ms(2500);
			}
			else
			{
				for(x=0,i=0; (i<MAX_PLAYERS) && (x==0); i++)
				{
					// Assign a vacant spot to this new player.
					if( player_tag[i][0] == '\0' )
					{
						strcpy(player_tag[i], RFID_latest_tag);		// Copy the RFID id
						active_player_index = i;
						// Assign next available nickname
						player_nick_index[active_player_index] = next_available_nickname; 
						// Increment the global number representing the next available nickname.
						next_available_nickname++;		
						x=1;	// "Stop" flag
						//
						lcd_clear();
						serout_P( PSTR("Welcome\n") );
						serout(nicknames[player_nick_index[active_player_index]]);
						_delay_ms(2500);
					}
				}

				if( x==0 )
				{
					lcd_clear();
					serout_P( PSTR("No more\nRoom! :(") );
					// MAX_PLAYERS reached!  Can't add the player.
					_delay_ms(2500);
					x=1;
					
				}
			}
		}
		else
		{
			_delay_ms(150);			// Prevent multiple button activations by 
									// this slight pause if there isn't anything to do.
		}
	}
	while( button_b_state != 0 );	// Keep looping back to the choice of starting 
									// a game or pre-reg a player until the user 
									// chooses "start a game".



	///////////////////////////////////////////////////////////////////////////////
	// Note:
	// Once a game is over and we want to play again without re-scanning tags:
	// To restart a game but leave scanned player tags assigned, 
	// we should re-enter code here after zeroing out the following 
	// variables:
	//
	//
	//game_ended;							// Game end flag.
	//player_time[MAX_PLAYERS];				// Array index valid [0-9]
	//player_time_total[MAX_PLAYERS];		// Array index valid [0-9]
	//player_highscore[MAX_PLAYERS];		// Array index valid [0-9]
	//player_rank[MAX_PLAYERS];				// Rank of player (calculated at end)
	//player_times_held[MAX_PLAYERS];		// Number of times skull held
	//highscore;							// The current high-score (in seconds)
	//time_passed;							// Total time passed (active or not)
	//
	restart:
	game_ended=0;
	highscore=0;
	time_passed=0;
	for(x=0; x<MAX_PLAYERS; x++)
	{
		player_time[x]=0;
		player_time_total[x]=0;
		player_highscore[x]=0;
		player_rank[x]=0;
		player_times_held[x]=0;
	}
	///////////////////////////////////////////////////////////////////////////////



	// End the "pre-register a player" part of things.  Init the game!
	lcd_clear();
	serout_P( PSTR("Scan\nADMIN...") );


	// Read RFID tag into 'admin_tag'
	//
	longbeep();		// longbeep() is considered the "please scan tag" sound.
	x = read_RFID(0);
	while( x!=0 )		// Re-read on error.
	{
		//errbeep();	// BreepBreepBreep is "error, please re-scan"
		_delay_ms(250);
		x = read_RFID(1);	// arg '1' = "Try harder" (basically)
	}
	// Successful read sound
	beep();beep();beep();	
	strcpy(admin_tag, RFID_latest_tag);


	
	// Display the Tag ID
	//
	lcd_clear();
	
	/*
	for(x=0; admin_tag[x]!='\0'; x++ )		// Print out the tag ID on the LCD
		TXbyte(admin_tag[x]);
	*/
	
	serout_P( PSTR("MISSION\n START!") );
	_delay_ms(2500);



	///////////////////////////////////////////
	// 
	// The game has now started.
	//
	// - Count down game time while idle, or while held by a player.
	// - When held by a player, count their time until released.
	// - End the game when time is out, or ADMIN tag is scanned.
	// 
	///////////////////////////////////////////


	while (game_ended == 0)
	{
		//////////////////////////////////////////////////////////////////////////////
		// Functional overview:
		//
		// While the game has not ended (total time < GAME_LENGTH, including
		// time skull is not actively held) and the admin_tag has not been 
		// seen again:
		//
		// 1. Wait for the two buttons in the skull eye sockets to be pushed and held.
		//    Count and update 'time_passed' while doing so.  Beep occasionally.
		// 2. Ask and wait for a tag.  
		//	  (Bug/Issue/Feature: 'time_passed' freezed while we are waiting.)
		// 2a. If tag = admin_tag, end the game right away.
		// 3. Determine if the tag is known.  If not, add it in a free spot.
		// 4. Inc the 'player_times_held[]'.
		// 5. Inc 'player_time[]' and 'time_passed'.  If > highscore then update highscore.
		// 	  Do until button(s) released, or 'time_passed' > GAME_LENGTH.
		//    
		// If high score obtained, notify the user with a bunch of beeps.
		// 
		// 6. If 'time_passed' < GAME_LENGTH then go back to #1.
		//////////////////////////////////////////////////////////////////////////////		
		
		lcd_clear();
		serout_P( PSTR("TimeLeft") );


		//////////////////////////////////////////////////////////////////////////////
		// Read buttons A and B into variables, then if they are not both '0'
		// Then delay a little (1 second), update 'time_passed' by one second, and 
		// look again.
		//////////////////////////////////////////////////////////////////////////////

		button_a_state = BUTTONS_PORT & (0x01<<BUTTON_A_BIT_NUMBER); // Read button A
		button_b_state = BUTTONS_PORT & (0x01<<BUTTON_B_BIT_NUMBER); // Read button B
	
		z=0;	// This is our '5 seconds' counter timer

		while( button_a_state || button_b_state )	// While either is a '1'
		{
			// Wait 1 second while waiting idle.
			// (Emit a 200ms beep every 5 seconds)
			_delay_ms(800);		// 800ms of 1 second
			//
			// What we do next depends on whether a 5 second block is up or not
			z++;
			if( z==5 )
			{
				z = 0;
				beep();			// A 200ms beep
			}
			else
			{
				_delay_ms(200);	// Or just wait the 200ms otherwise
			}

			time_passed++;		// Update time game has gone on.

			
			// Display a bar graph showing 'time_passed' versus GAME_LENGTH
			// When this bar empties, the game time is up.
			x = (GAME_LENGTH / 40);		// x is the scaler value (e.g. 200 / 40 = 5)
			TXbyte(0xfe);	// Achtung!
			TXbyte(0x7c);	// Bar graph mode on display
			TXbyte(0x01);	// Column
			TXbyte(0x02);	// Row
			TXbyte(0x00);	// Left-To-Right bar (0)
			// Next is 'length' which is in pixels (5*8 for 8x2 display = 40)
			// time_passed as a value between 0 and 40 (40 representing GAME_LENGTH)
			// is obtained by: (time_passed / the scalar) [scalar being the 'x' value.]
			// Subtract that from 40.
			TXbyte( 40 - (time_passed / x) ); 	// 0-40 bar length


			if( time_passed > GAME_LENGTH )
			{
				game_ended = 1;		// End game flag set if this has gone one too long.
				break;
			}
			
			// Now look at those buttons again.
			button_a_state = BUTTONS_PORT & (0x01<<BUTTON_A_BIT_NUMBER); // '0' when pushed
			button_b_state = BUTTONS_PORT & (0x01<<BUTTON_B_BIT_NUMBER); // '0' when pushed
		}	


		/////////////////////////////////////////////////////////////////////////////
		// If the game is over, break out and go to post-game operations.
		/////////////////////////////////////////////////////////////////////////////
		if (game_ended == 1)
			break;


		/////////////////////////////////////////////////////////////////////////////
		// Otherwise, read a tag from the person holding the skull.
		/////////////////////////////////////////////////////////////////////////////

		lcd_clear();
		serout_P( PSTR("Scan tag\n") );		
	
		
		longbeep();		// longbeep() is considered the "please scan tag" sound.
		x = read_RFID(0);
		while( x!=0 )		// Re-read on error.
		{
			//errbeep();	// BreepBreepBreep is "error, please re-scan"
			_delay_ms(250);
			x = read_RFID(1);
		}

		// Successful read sound
		beep();beep();beep();	


		// Check whether this is the admin tag.  If so, end the game right away.
		x = strncmp(admin_tag, RFID_latest_tag, 10);
		if( x==0 )	// A match
		{
			game_ended = 1;
			break;
		}

		// Otherwise, we have a player's tag - determine if it is known tag or not.
		// Look through the list until one of the following conditions is true:
		// - We run out of list entries
		// - We get a match (x==0)
		for (x=1, i=0; (i<MAX_PLAYERS) && (x!=0); i++)
		{
			x = strcmp(player_tag[i], RFID_latest_tag);
			if( x == 0)
				active_player_index = i;
		}

		// If x==0 then we have a match and [active_player_index] is the 
		// index of the player whose tag is currently held.
		//
		// If no match, add the player's tag to the next available slot (available 
		// player_tag[i][] slots have a NULL in the first element) and point
		// active_player_index to the index of the new entry.
		//
		// Player info variables:
		//  player_tag[][]				- RFID tag for the player
		// 	player_nick_index[]			- Index to nicknames[] for this player
		//  player_time[]				- Time the player has held the skull this session
		//	player_highscore[]			- Player's highest time
		//	player_rank[]				- Player's rank by score
		//  player_time_total[]			- Total time the player has held the skull
		//  player_times_held[]			- Number of times player has held the skull
		// Also:
		//  highscore
		
		
		/*
		if( x==0 )
		{
			serout_P( PSTR("Match\n") );
		}
		*/
		if( x!=0 )
		{
			//serout_P( PSTR("No Match.\n") );

			for(x=0,i=0; (i<MAX_PLAYERS) && (x==0); i++)
			{
				// Assign a vacant spot to this new player.
				if( player_tag[i][0] == '\0' )
				{
					strcpy(player_tag[i], RFID_latest_tag);		// Copy the RFID id
					active_player_index = i;
					// Assign next available nickname
					player_nick_index[active_player_index] = next_available_nickname; 
					// Increment the global number representing the next available nickname.
					next_available_nickname++;		
					x=1;	// "Stop" flag
				}
			}
			if( x==0 )
			{
				serout_P( PSTR("No Room\nfor you!") );
				// This should never happen.  Ends the game ungracefully.
				return 1;
			}
		}

		/////////////////////////////////////////////////////////////////
		// [active_player_index] is now a valid index that points to an 
		// identified player who is holding the skull.
		/////////////////////////////////////////////////////////////////
		lcd_clear();
		serout(nicknames[player_nick_index[active_player_index]]);
		//serout(player_tag[active_player_index]);


		// Now, it's possible that between grabbing the skull and poking the eyes,
		// the user had to let go of the eyes to shift their grip and scan their tag.
		// If that's the case, then warn them to poke the eyes again.  Give them 3 
		// seconds.
		//
		// Did they let go to scan their tag?  If so, tell them to poke eyes again.
		button_a_state = BUTTONS_PORT & (0x01<<BUTTON_A_BIT_NUMBER); // Read button A
		button_b_state = BUTTONS_PORT & (0x01<<BUTTON_B_BIT_NUMBER); // Read button B
		if( (button_a_state) || (button_b_state) )	// 1 = not pressed
		{
			_delay_ms(1000);		// Time to read the name before we erase it.
			lcd_clear();
			serout_P( PSTR("Restore\ngrip now") );
			_delay_ms(1000);
			lcd_clear();
			serout_P( PSTR("3\nsecs...") );
			_delay_ms(1000);
			lcd_clear();
			serout_P( PSTR("2\nsecs...") );
			_delay_ms(1000);
			lcd_clear();
			serout_P( PSTR("1\nsec...") );
			_delay_ms(1000);
			lcd_clear();		// Re-print what we started from a few lines higher...
			serout(nicknames[player_nick_index[active_player_index]]);
		}


		// +1 to Number of time player had held skull
		player_times_held[active_player_index]++;

		// Reset the time the player has held the skull for this session to 0.
		player_time[active_player_index] = 0;

		// We have now identified the player who is in possession of the skull.


		// Now start managing the time while a player is in possession of the skull.
		// Update time_passed just like before, except this time also increase
		// the player_time[i] as well.  In addition, update highscore too if
		// it's called for and notify the player.

		button_a_state = BUTTONS_PORT & (0x01<<BUTTON_A_BIT_NUMBER); // Read button A
		button_b_state = BUTTONS_PORT & (0x01<<BUTTON_B_BIT_NUMBER); // Read button B

		while( (button_a_state==0) && (button_b_state==0) )	// While both are (still) '0'
		{
			time_passed++;		// Update time game has gone on.
			player_time[active_player_index]++;	// Also update the player's time.
			player_time_total[active_player_index]++;	// And update the player's total
			// And update the player's personal highscore
			if( player_time[active_player_index] > player_highscore[active_player_index] )
				player_highscore[active_player_index] = player_time[active_player_index];


			// Determine if we need to update the highscore
			if( player_time[active_player_index] > highscore )
			{
				// Every second of new-high-score-ness = a beep to accompany
				// the timer.
				highscore = player_time[active_player_index];
				shortbeep();	// 100ms
				_delay_ms(900);	// 900ms		
			}
			else
			{
				// Normal "time passed"
				_delay_ms(1000);
			}


			// Display a bar graph showing 'time_passed' versus GAME_LENGTH
			// When this bar empties, the game time is up.
			x = (GAME_LENGTH / 40);		// x is the scaler value (e.g. 1600 / 40 = 40)
			TXbyte(0xfe);	// Achtung!
			TXbyte(0x7c);	// Bar graph mode on display
			TXbyte(0x01);	// Column
			TXbyte(0x02);	// Row
			TXbyte(0x00);	// Left-To-Right bar (1=bar to left)
			// time_passed as a value between 0 and 40 (40 representing GAME_LENGTH)
			// is obtained by: (time_passed / the scalar) [scalar being the 'x' value.]
			// Subtract that from 40.
			TXbyte( 40 - (time_passed / x) ); 	// 0-40 bar length


			if( time_passed > GAME_LENGTH )
			{
				game_ended = 1;		// End game flag set if this has gone one too long.
				break;
			}
			
			// Now look at those buttons again.
			button_a_state = BUTTONS_PORT & (0x01<<BUTTON_A_BIT_NUMBER); // '0' when pushed
			button_b_state = BUTTONS_PORT & (0x01<<BUTTON_B_BIT_NUMBER); // '0' when pushed
		}	

		// Player has let go of the skull.
		longbeep();


		/////////////////////////////////////////////////////////////////////////////
		// If the game is over, break out and go to post-game operations.
		/////////////////////////////////////////////////////////////////////////////
		if (game_ended == 1)
			break;

	}

	//////////////////////////////////////////////////////////
	//
	// Game is over.
	//
	// - Report stats, or
	// - Allow scanning of tags to remind players what their
	//   nickname was.
	//
	//////////////////////////////////////////////////////////
	

	// Calculate player ranks ( player_rank[] )
	// 
	// Method:
	// Assume everyone starts at [# of players] rank (i.e. last place)
	// For each person with a lower score than you, advance one rank.
	// (This means you must BEAT someone's time to be ranked ahead of them)
	// (Also this means we don't have to worry about ranking being affected
	// by comparing against ourselves in the loop.)
	//
	for( i=0, x=0; x<MAX_PLAYERS; x++ )		
		if( player_tag[x][0] != '\0' )
			i++;					// i is the total number of players

	for( x=0; x<i; x++ )			// For each player
	{
		player_rank[x] = i;			// Rank = # of players (i.e. last place)
		for( z=0; z<i; z++)			// Compare against all other player scores
		{
			if( player_highscore[x] > player_highscore[z] )
				player_rank[x]--;	// Up 1 rank each time we beat someone's score
		}
	}

	
	
	// Notify user, etc.
	lcd_clear();
	serout_P( PSTR("  Game\n  Over") );

	// End game sound
	errbeep();		
	longbeep();
	errbeep();
	longbeep();

	serout_P( PSTR(" >") );


	// Wait for either button push, then both buttons to be released
	do
	{
		button_a_state = BUTTONS_PORT & (0x01<<BUTTON_A_BIT_NUMBER); // '0' when pushed
		button_b_state = BUTTONS_PORT & (0x01<<BUTTON_B_BIT_NUMBER); // '0' when pushed
	}
	while( button_a_state && button_b_state );
	do
	{
		button_a_state = BUTTONS_PORT & (0x01<<BUTTON_A_BIT_NUMBER); // '0' when pushed
		button_b_state = BUTTONS_PORT & (0x01<<BUTTON_B_BIT_NUMBER); // '0' when pushed
	}
	while( (button_a_state==0) || (button_b_state==0) );



	// Report on each player, cycling through each one.
	// Or just query a tag and return the nickname.

	for(i=0; i<MAX_PLAYERS; )
	{
		
		// If we've reached the end of valid players (but not the end of valid indices)
		// then reset 'i' so we loop forever.
		//
		// Otherwise we will display the next score in the lineup of players.
		if( player_tag[i][0] == '\0' )
			i = 0;

		// One thing first -- if there is no winner (i.e. 'highscore' = 0)
		// then say so and stop here.
		if( highscore == 0 )
		{
			lcd_clear();
			serout_P( PSTR("GAMEOVER\nNo Win.") );
			return 1;
		}


		// Before scrolling the results, give a basic menu for choosing between
		// Results or Query (i.e. "what was my name?")
		// 
		// If "scores" selected, 
		// cycle through all player scores.

		if( i==0 )		// Only do this if we've completed (or just started) a cycle
		{				// of score reports
			do
			{
				lcd_clear();
				serout_P( PSTR("< Scores\n") );
				serout_P( PSTR("Tag ID >\n") );
				// Wait for either button to be pushed
				do
				{
					button_a_state = BUTTONS_PORT & (0x01<<BUTTON_A_BIT_NUMBER); // '0' when pushed
					button_b_state = BUTTONS_PORT & (0x01<<BUTTON_B_BIT_NUMBER); // '0' when pushed
				}
				while( button_a_state && button_b_state );

				//////////////////////////////////////////////////////////
				// If BOTH are pushed and held for at least 2 seconds
				// Then start another game with the same players.
				_delay_ms(50);
				button_a_state = BUTTONS_PORT & (0x01<<BUTTON_A_BIT_NUMBER); // '0' when pushed
				button_b_state = BUTTONS_PORT & (0x01<<BUTTON_B_BIT_NUMBER); // '0' when pushed
				if( (button_a_state==0) && (button_b_state==0) )
				{
					_delay_ms(2000);
					button_a_state = BUTTONS_PORT & (0x01<<BUTTON_A_BIT_NUMBER); // '0' when pushed
					button_b_state = BUTTONS_PORT & (0x01<<BUTTON_B_BIT_NUMBER); // '0' when pushed
					if( (button_a_state==0) && (button_b_state==0) )
					{
						beep();
						lcd_clear();
						serout( PSTR("Let go\n= replay") );
						// Wait until both buttons are released before restarting
						do
						{
							button_a_state = BUTTONS_PORT & (0x01<<BUTTON_A_BIT_NUMBER); // '0' when pushed
							button_b_state = BUTTONS_PORT & (0x01<<BUTTON_B_BIT_NUMBER); // '0' when pushed
						}
						while( (button_a_state==0) || (button_b_state==0) );

						goto restart;
					}
				}
				//
				//////////////////////////////////////////////////////////

				if( button_a_state )
				{
					lcd_clear();
					serout_P( PSTR("Please\nscan tag") );

					longbeep();		// longbeep() is considered the "please scan tag" sound.
					x = read_RFID(0);
					while( x!=0 )		// Re-read on error.
					{
						//errbeep();	// BreepBreepBreep is "error, please re-scan"
						_delay_ms(250);
						x = read_RFID(1);	// (1) = "Try Harder" (essentially)
					}

					// Successful read sound
					beep();beep();beep();	

					// Look through the list until we have a match
					active_player_index = (MAX_PLAYERS+1);
					for (x=1, z=0; (z<MAX_PLAYERS) && (x!=0); z++)
					{
						x = strcmp(player_tag[z], RFID_latest_tag);
						if( x == 0)
							active_player_index = z;
					}

					// Display name
					lcd_clear();
					//serout(RFID_latest_tag);
					serout_P( PSTR("You are\n") );
					if( active_player_index == (MAX_PLAYERS+1) )	// This is a flag value for "not found"
						serout_P( PSTR("No Match!") );
					else
						serout(nicknames[player_nick_index[active_player_index]]);
					
					_delay_ms(2000);	// Time to read it

					x = 1;		// Flag to return to this menu
				}
				else
				{
					x = 0;		// Flag to go onto a round of the scores
				}
			}
			while (x == 1);		// Use 'x' as a "query a tag" flag, until "scores" were chosen.
		}
		

		// Wait until both buttons are released before displaying the scores.
		do
		{
			button_a_state = BUTTONS_PORT & (0x01<<BUTTON_A_BIT_NUMBER); // '0' when pushed
			button_b_state = BUTTONS_PORT & (0x01<<BUTTON_B_BIT_NUMBER); // '0' when pushed
		}
		while( (button_a_state==0) || (button_b_state==0) );



		// Display something like this for each player
		//
		// Name: wiggles
		// Best Time: 61 sec
		// *************.........
		// High score was 88.
		//



		// Page 1
		lcd_clear();
		serout(nicknames[player_nick_index[i]]);
		TXbyte('\n');
		itoa( player_highscore[i], string, 10 );
		serout( string );
		serout_P( PSTR( "sec" ) );
		lcd_xy(8,2);
		TXbyte('>');


		// Wait for either button to be pushed
		// (ie "Press a button for more...")
		do
		{
			button_a_state = BUTTONS_PORT & (0x01<<BUTTON_A_BIT_NUMBER); // '0' when pushed
			button_b_state = BUTTONS_PORT & (0x01<<BUTTON_B_BIT_NUMBER); // '0' when pushed
		}
		while( button_a_state && button_b_state );
		// Wait for buttons to be released
		_delay_ms(15);
		do
		{
			button_a_state = BUTTONS_PORT & (0x01<<BUTTON_A_BIT_NUMBER); // '0' when pushed
			button_b_state = BUTTONS_PORT & (0x01<<BUTTON_B_BIT_NUMBER); // '0' when pushed
		}
		while( (button_a_state==0) || (button_b_state==0) );
		//
		// End "press a button for more..."		


		// Page 2

		// Display a bar graph showing player's longest time vs. highscore
		// If this bar is full, the player's IS the high score
		lcd_clear();
		if( highscore >= 40 )
			x = (highscore / 40);		// x is the scaler value (e.g. 300 / 40 = 6)
		else
			x = ( 40 / highscore );	// e.g. (40 / 24 = 2)
		TXbyte(0xfe);	// Achtung!
		TXbyte(0x7c);	// Bar graph mode on display
		TXbyte(0x01);	// Column
		TXbyte(0x01);	// Row
		TXbyte(0x00);	// Left-To-Right (0)
		// time_passed as a value between 0 and 40 (40 representing 'highscore')
		// is obtained by: (player_highscore / the scalar) [scalar being the 'x' value.]
		// Subtract that from 40.
		if( player_highscore[i] == highscore )
			TXbyte(40);	// Winner is not always 100% of graph due to rounding.
		else
			TXbyte( player_highscore[i] * x ); 	// 0-40 bar length
		//
		// Now display the commentary (high score congrats, or "your time/highscore")
		lcd_xy(1,2);
		if( player_highscore[i] == highscore )
		{
			serout_P( PSTR("WINNER!") );
		}
  		else
		{
			/*
			itoa( player_highscore[i], string, 10 );	// Print our highscore
			serout( string );
			TXbyte('/');
			itoa( highscore, string, 10 );				// Print the winner's score
			serout( string );
			*/

			itoa( player_rank[i], string, 10);			// Print our ranking
			TXbyte('#');
			serout( string );
			
			lcd_xy(8,2);
			TXbyte('>');
		}
		

		// Wait for either button to be pushed
		// (ie "Press a button for more...")
		do
		{
			button_a_state = BUTTONS_PORT & (0x01<<BUTTON_A_BIT_NUMBER); // '0' when pushed
			button_b_state = BUTTONS_PORT & (0x01<<BUTTON_B_BIT_NUMBER); // '0' when pushed
		}
		while( button_a_state && button_b_state );
		// Wait for buttons to be released
		_delay_ms(15);
		do
		{
			button_a_state = BUTTONS_PORT & (0x01<<BUTTON_A_BIT_NUMBER); // '0' when pushed
			button_b_state = BUTTONS_PORT & (0x01<<BUTTON_B_BIT_NUMBER); // '0' when pushed
		}
		while( (button_a_state==0) || (button_b_state==0) );
		//
		// End "press a button for more..."		


		// Page 3
		lcd_clear();
		serout_P( PSTR("Held:") );
		itoa( player_times_held[i], string, 10 );
		serout( string );
		TXbyte('\n');
		// Line 3
		serout_P( PSTR("Sum:") );
		itoa( player_time_total[i], string, 10 );
		serout( string );

		_delay_ms(50);
		lcd_xy(8,2);
		TXbyte('>');





		// Wait for either button to be pushed
		// (ie "Press a button for more...")
		do
		{
			button_a_state = BUTTONS_PORT & (0x01<<BUTTON_A_BIT_NUMBER); // '0' when pushed
			button_b_state = BUTTONS_PORT & (0x01<<BUTTON_B_BIT_NUMBER); // '0' when pushed
		}
		while( button_a_state && button_b_state );
		// Wait for buttons to be released
		_delay_ms(15);
		do
		{
			button_a_state = BUTTONS_PORT & (0x01<<BUTTON_A_BIT_NUMBER); // '0' when pushed
			button_b_state = BUTTONS_PORT & (0x01<<BUTTON_B_BIT_NUMBER); // '0' when pushed
		}
		while( (button_a_state==0) || (button_b_state==0) );
		//
		// End "press a button for more..."		




		i++;						// Move to the next player, but if we're at the end
									// then reset to 0 so we loop forever.
		if( i>=MAX_PLAYERS )
			i=0;
	}


	return 0;
}


// ***** FUNCTIONS ******** FUNCTIONS ******* FUNCTIONS ***** FUNCTIONS ***
// ***** FUNCTIONS ******** FUNCTIONS ******* FUNCTIONS ***** FUNCTIONS ***
// ***** FUNCTIONS ******** FUNCTIONS ******* FUNCTIONS ***** FUNCTIONS ***
// ***** FUNCTIONS ******** FUNCTIONS ******* FUNCTIONS ***** FUNCTIONS ***
// ***** FUNCTIONS ******** FUNCTIONS ******* FUNCTIONS ***** FUNCTIONS ***
// ***** FUNCTIONS ******** FUNCTIONS ******* FUNCTIONS ***** FUNCTIONS ***
// ***** FUNCTIONS ******** FUNCTIONS ******* FUNCTIONS ***** FUNCTIONS ***
// ***** FUNCTIONS ******** FUNCTIONS ******* FUNCTIONS ***** FUNCTIONS ***
// ***** FUNCTIONS ******** FUNCTIONS ******* FUNCTIONS ***** FUNCTIONS ***
// ***** FUNCTIONS ******** FUNCTIONS ******* FUNCTIONS ***** FUNCTIONS ***
// ***** FUNCTIONS ******** FUNCTIONS ******* FUNCTIONS ***** FUNCTIONS ***
// ***** FUNCTIONS ******** FUNCTIONS ******* FUNCTIONS ***** FUNCTIONS ***
// ***** FUNCTIONS ******** FUNCTIONS ******* FUNCTIONS ***** FUNCTIONS ***
// ***** FUNCTIONS ******** FUNCTIONS ******* FUNCTIONS ***** FUNCTIONS ***
// ***** FUNCTIONS ******** FUNCTIONS ******* FUNCTIONS ***** FUNCTIONS ***
// ***** FUNCTIONS ******** FUNCTIONS ******* FUNCTIONS ***** FUNCTIONS ***
// ***** FUNCTIONS ******** FUNCTIONS ******* FUNCTIONS ***** FUNCTIONS ***
// ***** FUNCTIONS ******** FUNCTIONS ******* FUNCTIONS ***** FUNCTIONS ***
// ***** FUNCTIONS ******** FUNCTIONS ******* FUNCTIONS ***** FUNCTIONS ***
// ***** FUNCTIONS ******** FUNCTIONS ******* FUNCTIONS ***** FUNCTIONS ***
// ***** FUNCTIONS ******** FUNCTIONS ******* FUNCTIONS ***** FUNCTIONS ***
// ***** FUNCTIONS ******** FUNCTIONS ******* FUNCTIONS ***** FUNCTIONS ***
// ***** FUNCTIONS ******** FUNCTIONS ******* FUNCTIONS ***** FUNCTIONS ***
// ***** FUNCTIONS ******** FUNCTIONS ******* FUNCTIONS ***** FUNCTIONS ***
// ***** FUNCTIONS ******** FUNCTIONS ******* FUNCTIONS ***** FUNCTIONS ***
// ***** FUNCTIONS ******** FUNCTIONS ******* FUNCTIONS ***** FUNCTIONS ***
// ***** FUNCTIONS ******** FUNCTIONS ******* FUNCTIONS ***** FUNCTIONS ***
// ***** FUNCTIONS ******** FUNCTIONS ******* FUNCTIONS ***** FUNCTIONS ***
// ***** FUNCTIONS ******** FUNCTIONS ******* FUNCTIONS ***** FUNCTIONS ***
// ***** FUNCTIONS ******** FUNCTIONS ******* FUNCTIONS ***** FUNCTIONS ***
// ***** FUNCTIONS ******** FUNCTIONS ******* FUNCTIONS ***** FUNCTIONS ***
// ***** FUNCTIONS ******** FUNCTIONS ******* FUNCTIONS ***** FUNCTIONS ***
// ***** FUNCTIONS ******** FUNCTIONS ******* FUNCTIONS ***** FUNCTIONS ***

// ************************************************************************
// Functions
// ************************************************************************



///////////////////////////////////////////////////////////////////////////
// Name: 	beep
// Purpose:	Beep!
//
// Inputs:
// Outputs: 
// Changes:
// Needs: 	
// Notes:	
// Bugs:	Crashes MCU and erases all EEPROM, also demolishes orphanage.
///////////////////////////////////////////////////////////////////////////
void beep(void)
{
	BUZZER_PORT_DDR |= (0x01 << BUZZER_BIT_NUMBER);	// Set as output
	BUZZER_PORT |= (0x01 << BUZZER_BIT_NUMBER);		// High
	_delay_ms(150);
	BUZZER_PORT &= ~(0x01 << BUZZER_BIT_NUMBER);	// Low
	_delay_ms(50);
	return;
}
///////////////////////////////////////////////////////////////////////////



///////////////////////////////////////////////////////////////////////////
// Name: 	shortbeep
// Purpose:	Bip!
//
// Inputs:
// Outputs: 
// Changes:
// Needs: 	
// Notes:	
// Bugs:	Crashes MCU and erases all EEPROM, also demolishes orphanage.
///////////////////////////////////////////////////////////////////////////
void shortbeep(void)
{
	BUZZER_PORT_DDR |= (0x01 << BUZZER_BIT_NUMBER);	// Set as output
	BUZZER_PORT |= (0x01 << BUZZER_BIT_NUMBER);		// High
	_delay_ms(75);
	BUZZER_PORT &= ~(0x01 << BUZZER_BIT_NUMBER);	// Low
	_delay_ms(25);
	return;
}
///////////////////////////////////////////////////////////////////////////



///////////////////////////////////////////////////////////////////////////
// Name: 	longbeep
// Purpose:	Beeeeeeeep!
//
// Inputs:
// Outputs: 
// Changes:
// Needs: 	
// Notes:	
// Bugs:	Erases all nearby credit cards and magnetic disks.
///////////////////////////////////////////////////////////////////////////
void longbeep(void)
{
	BUZZER_PORT_DDR |= (0x01 << BUZZER_BIT_NUMBER);	// Set as output
	BUZZER_PORT |= (0x01 << BUZZER_BIT_NUMBER);		// High
	_delay_ms(1000);
	BUZZER_PORT &= ~(0x01 << BUZZER_BIT_NUMBER);	// Low
	_delay_ms(50);
	return;
}
///////////////////////////////////////////////////////////////////////////




///////////////////////////////////////////////////////////////////////////
// Name: 	errbeep
// Purpose:	BreepBreepBreep
//
// Inputs:
// Outputs: 
// Changes:
// Needs: 	
// Notes:	
// Bugs:	
///////////////////////////////////////////////////////////////////////////
void errbeep(void)
{
	BUZZER_PORT_DDR |= (0x01 << BUZZER_BIT_NUMBER);	// Set as output

	BUZZER_PORT |= (0x01 << BUZZER_BIT_NUMBER);		// High
	_delay_ms(75);
	BUZZER_PORT &= ~(0x01 << BUZZER_BIT_NUMBER);	// Low
	_delay_ms(30);
	BUZZER_PORT |= (0x01 << BUZZER_BIT_NUMBER);		// High
	_delay_ms(75);
	BUZZER_PORT &= ~(0x01 << BUZZER_BIT_NUMBER);	// Low
	_delay_ms(30);
	BUZZER_PORT |= (0x01 << BUZZER_BIT_NUMBER);		// High
	_delay_ms(75);
	BUZZER_PORT &= ~(0x01 << BUZZER_BIT_NUMBER);	// Low
	_delay_ms(100);


	BUZZER_PORT |= (0x01 << BUZZER_BIT_NUMBER);		// High
	_delay_ms(75);
	BUZZER_PORT &= ~(0x01 << BUZZER_BIT_NUMBER);	// Low
	_delay_ms(30);
	BUZZER_PORT |= (0x01 << BUZZER_BIT_NUMBER);		// High
	_delay_ms(75);
	BUZZER_PORT &= ~(0x01 << BUZZER_BIT_NUMBER);	// Low
	_delay_ms(30);
	BUZZER_PORT |= (0x01 << BUZZER_BIT_NUMBER);		// High
	_delay_ms(75);
	BUZZER_PORT &= ~(0x01 << BUZZER_BIT_NUMBER);	// Low
	_delay_ms(100);


	BUZZER_PORT |= (0x01 << BUZZER_BIT_NUMBER);		// High
	_delay_ms(75);
	BUZZER_PORT &= ~(0x01 << BUZZER_BIT_NUMBER);	// Low
	_delay_ms(30);
	BUZZER_PORT |= (0x01 << BUZZER_BIT_NUMBER);		// High
	_delay_ms(75);
	BUZZER_PORT &= ~(0x01 << BUZZER_BIT_NUMBER);	// Low
	_delay_ms(30);
	BUZZER_PORT |= (0x01 << BUZZER_BIT_NUMBER);		// High
	_delay_ms(75);
	BUZZER_PORT &= ~(0x01 << BUZZER_BIT_NUMBER);	// Low
	_delay_ms(100);

	return;
}
///////////////////////////////////////////////////////////////////////////





///////////////////////////////////////////////////////////////////////////
// Name: 	setup_rs232
// Purpose:	initialize the TX and RX pins of the UART, set comms parameters
//
// Inputs:	Desired baudrate as a number
// Outputs: None
// Changes:
// Needs: 	AVR part must have a UART.
//
// Notes:	Only need to run once to init prior to using serial comms.
//			See datasheet for which physical pins are the TX and RX.
//			Comms are RS232 at TTL LEVELS, and INVERTED. (ie idle is 0, not 1)
//
// Bugs:	Re-running may trash buffered waiting data.
///////////////////////////////////////////////////////////////////////////
static inline void setup_rs232(int baudrate)
{
	// Turn on the transmission and reception circuitry
	UCSRB |= (1 << RXEN) | (1 << TXEN);   
	// Set to 8-N-1
	UCSRC = (1 << UCSZ1) | (1 << UCSZ0);
	
	// This is a computational method of setting baud rate
	// Can also set "magic numbers" from datasheet.

	// Load lower 8-bits of the baud rate value into the low byte of the UBRR register
	UBRRL = (((F_CPU / (baudrate * 16UL))) - 1); 
	//
	// Load upper 8-bits of the baud rate value into the high byte of the UBRR register
	UBRRH = ((((F_CPU / (baudrate * 16UL))) - 1) >> 8); 

	return;
}
///////////////////////////////////////////////////////////////////////////




///////////////////////////////////////////////////////////////////////////
// Name: 	TXbyte
// Purpose:	Hand off a character to the serial comms hardware so it is transmitted.
//
// Inputs:	A single character (e.g. 'a')
// Outputs: None
// Changes:
// Needs: 	UART must be set up by running setup_rs232().
// Notes:	
// Bugs:	None
///////////////////////////////////////////////////////////////////////////
void TXbyte(char data)
{
      // Do nothing until UDR is ready for more data to be written to it
	  while ((UCSRA & (1 << UDRE)) == 0) {}; 
      // Hand off the byte so it can be sent.
	  UDR = data; 
	  //_delay_ms(1);	// This seems to help a little
	  return;
}
///////////////////////////////////////////////////////////////////////////




///////////////////////////////////////////////////////////////////////////
// Name: 	RXbyte
// Purpose:	Receive the next character from the UART.
//
// Inputs:	
// Outputs: Returns a single character (e.g. 'a')
// Changes:
// Needs: 	UART must be set up by running setup_rs232().
// Notes:	This function will hang unless/until there is data available.
// Bugs:	None
///////////////////////////////////////////////////////////////////////////
char RXbyte(void)
{
	// Do nothing until data have been recieved and is ready to be read from UDR
	while ((UCSRA & (1 << RXC)) == 0) {}; 
	// Return the recieved byte.
	return UDR; 
}
///////////////////////////////////////////////////////////////////////////






///////////////////////////////////////////////////////////////////////////
// Name: 	serout
// Purpose:	Send a string out by RS232 serial connection
//
// Inputs:	s: string (char[] array) passed by reference
// Outputs: 
// Changes:
// Needs: 	UART must be set up by running setup_rs232().
//			Needs TXbyte() function.
//			string is assumed to be null terminated.
// Notes:	
// Bugs:	
///////////////////////////////////////////////////////////////////////////
void serout(char *s)
{
	int i;
	
	for (i=0; s[i]!='\0'; i++)
	{
		TXbyte(s[i]);
	}

	return;
}
///////////////////////////////////////////////////////////////////////////





///////////////////////////////////////////////////////////////////////////
// Name: 	serout_P
// Purpose:	Send a string living in PROGRAM memory (not RAM) 
// 			out by RS232 serial connection
//
// Inputs:	s: string (char[] array) living in PROG memory, passed by reference.
// Outputs: 
// Changes:
// Needs: 	UART must be set up by running setup_rs232().
//			Needs TXbyte() function.
//			Calls pgm_read_byte() in "avr/pgmspace.h"
//			String is assumed to be null terminated and living in PROG memory.
// Notes:	
// Bugs:	
///////////////////////////////////////////////////////////////////////////
void serout_P(const char *s)
{
	while( pgm_read_byte(s) != '\0' )
		TXbyte( pgm_read_byte(s++) );

	return;
}
///////////////////////////////////////////////////////////////////////////


///////////////////////////////////////////////////////////////////////////
// Name: 	RFID_enable
// Purpose:	Bring RFID /enable line LOW
//
// Inputs:
// Outputs: 
// Changes:	
// Needs: 	RFID #defines
// Notes:	
// Bugs:	
///////////////////////////////////////////////////////////////////////////
void RFID_enable(void)
{
	RFID_PORT_DDR |= (0x01 << RFID_ENABLE_BIT_NUMBER);	// Set as output
	RFID_PORT &= ~(0x01 << RFID_ENABLE_BIT_NUMBER);	// Low
	return;
}
///////////////////////////////////////////////////////////////////////////




///////////////////////////////////////////////////////////////////////////
// Name: 	RFID_disable
// Purpose:	Bring RFID /enable line HIGH
//
// Inputs:
// Outputs: 
// Changes:	
// Needs: 	RFID #defines
// Notes:	
// Bugs:	
///////////////////////////////////////////////////////////////////////////
void RFID_disable(void)
{
	RFID_PORT_DDR |= (0x01 << RFID_ENABLE_BIT_NUMBER);	// Set as output
	RFID_PORT |= (0x01 << RFID_ENABLE_BIT_NUMBER);		// High
	return;
}
///////////////////////////////////////////////////////////////////////////




///////////////////////////////////////////////////////////////////////////
// Name: 	read_RFID
// Purpose:	Enables the RFID reader and reads the next tag's ID
//
// Inputs:	0 if normal read, 1 if we should wait in a loop for a START byte.
// Outputs: 1 on invalid packet.  0 on success.
//
// Changes: Modifies global variable RFID_latest_tag
//
// Needs: 	RS232 comms must be set up prior to using this function.
//
// Notes:	On ATMEGA8 SEROUT of RFID reader goes to pin #2 (RX).
//
// Bugs:	Since the RFID sensor is dumb and has no flow control (not that
//			we have the ability to process it anyway) we must read the 
// 			characters as fast as possible.  If we're not there to pick them
// 			up as they come in, they will be lost.
///////////////////////////////////////////////////////////////////////////
int read_RFID(int wait_for_START_byte)
{
	int i;	


	// The RFID sensor is dumb.  No flow control.  If we are not there to read
	// the bytes as they arrive, they WILL be lost.

	RFID_enable();

	if( ! wait_for_START_byte ) 
	{
		RFID_latest_tag[0]=RXbyte();				// START Byte 0x0A
	}
	else
	{
		do
		{
			RFID_latest_tag[0] = RXbyte();
		}
		while (RFID_latest_tag[0] != 0x0A);
	}
	RFID_latest_tag[1]=RXbyte();
	RFID_latest_tag[2]=RXbyte();
	RFID_latest_tag[3]=RXbyte();
	RFID_latest_tag[4]=RXbyte();
	RFID_latest_tag[5]=RXbyte();
	RFID_latest_tag[6]=RXbyte();
	RFID_latest_tag[7]=RXbyte();
	RFID_latest_tag[8]=RXbyte();
	RFID_latest_tag[9]=RXbyte();
	RFID_latest_tag[10]=RXbyte();
	RFID_latest_tag[11]=RXbyte();				// STOP byte 0x0D

	RFID_disable();


	if( RFID_latest_tag[0] != 0x0a )			// Invalid start byte?  Error.
	{
		strcpy(RFID_latest_tag, "badstart");
		return 1;
	}

	if( RFID_latest_tag[11] != 0x0D )			// Invalid stop byte?  Error.
	{
		strcpy(RFID_latest_tag, "badstop");
		return 1;
	}


	// If we're good to go, then eliminate the START and STOP bytes.
	//
	for( i=1; i<=10; i++ )
		RFID_latest_tag[i-1] = RFID_latest_tag[i];
	RFID_latest_tag[10]='\0';
	

	return 0;
}
///////////////////////////////////////////////////////////////////////////





///////////////////////////////////////////////////////////////////////////
// Name: 	lcd_clear
// Purpose:	Clear the serial display
//
// Inputs:	
// Outputs: 
//
// Changes: 
// Needs: 	Assumes a serial display that uses this particular clear code.
// Notes:	
// Bugs:	
///////////////////////////////////////////////////////////////////////////
void lcd_clear(void)
{
	TXbyte(0xfe);			// Command byte to display
	TXbyte('X'); 			// Clear display command
	return;
}
///////////////////////////////////////////////////////////////////////////




///////////////////////////////////////////////////////////////////////////
// Name: 	lcd_xy
// Purpose:	Move the cursor
//
// Inputs:	x (X coords) and y (Y coords)
// Outputs: 
//
// Changes: 
// Needs: 	Assumes a serial display that uses this particular code.
// Notes:	
// Bugs:	
///////////////////////////////////////////////////////////////////////////
void lcd_xy(char x, char y)
{
	TXbyte(0xfe);			// Command byte to display
	TXbyte('G'); 			// Display command for goto
	TXbyte(x);
	TXbyte(y);
	return;
}
///////////////////////////////////////////////////////////////////////////


