#include <avr/io.h>


#define UART0_BAUD 207          /* 2400 UART0 BAUD              */
#define UART1_BAUD 51           /* 9600 UART1 BAUD              */
#define EZ1_SAVES 1             /* distance measurements        */
#define EZ1_ELEMENT_SIZE 3      /* 3 digit readings eg 006      */
#define RFID_SAVES 1            /* RFID tag number save once    */
#define RFID_ELEMENT_SIZE 10    /* 10 digit rfid number         */


void ez1_sensor_init (void);
void rfid_reader_init (void);
void bounce_meter_init (void);
void solenoid_init (void);
uint8_t serial_recv_uart0 (void);
uint8_t serial_recv_uart1 (void);
void serial_send_rfid (uint8_t rfid_read);
void serial_send_uart1 (uint8_t ez1_read);
void serial_print_char (char *);
void delay (uint16_t us);
void base_height (void);
void ez1_sensor_read (void);
void clear_lcd (void);
void long_delay (void);
void ez1_2_lcd (void);
void rfid_tag_scan (void);
uint8_t rfid_tag_check (void);
void erase_tag (void);
void rfid_2_lcd (void);
uint8_t ascii_2_digit (void);
void startup_message (void);
void trampoline_height (void);
void normalize_height (void) ;
void bounce_meter (void);
void solenoid_open (void);
int main (void);


/*
 * Yucky globals...these arrays the vital information
 * ez1 sensor data
 * rfid tag numbers
 */
uint8_t ez1_data[EZ1_SAVES][EZ1_ELEMENT_SIZE];          /* stores trampoline distance from ground */
uint8_t base[EZ1_ELEMENT_SIZE];                         /* stores trampoline initial height from ground */
uint8_t rfid_data[RFID_SAVES][RFID_ELEMENT_SIZE];       /* stores rfid tag numebr */
uint8_t height10;                                       /* trampoline height change 10s digit */
uint8_t height10_ascii;                                 /* trampoline height change 10s digit */
uint8_t height1;                                        /* trampoline height change 1s digit */
uint8_t height1_ascii;                                  /* trampoline height change 1s digit */
uint8_t jump_rating;                                    /* jump rating 8 is highest 0 is lowest */
uint8_t jump_rating_ascii;                              /* jump rating 8 is highest 0 is lowest + 48 */
uint8_t bounce_led;                                     /* LEDs to turn on based on height change */


/* 
 * initialize UART1
 * this is used by the ultrasonic sensor
 * maxbotix EZ1
 */
void ez1_sensor_init (void) {

        /* 
         *  calculating baud rate:
         * 
         *  8MHz clock and 9600
         *  $ echo "8000000 / 16 / 9600 - 1" | bc
         *  answer: 51 
         *
         */
        UBRR1H = (UART1_BAUD >> 8);  /* high order bits */
        UBRR1L = UART1_BAUD;         /* low order bits */

        /* 
         * enable uart tx and rx 
         * enable synchronous transfer
         * databits 8
         */
        UCSR1B = (1 << RXEN1) | (1 << TXEN1);
        UCSR1C = (1 << UCSZ11) | (1 << UCSZ10);

        return;
}


/*
 * initializes UART0
 * this is used by the rfid reader
 * parallax $39 module made by Grand Idea Studio
 */
void rfid_reader_init (void) {

        /* 
         *  calculating baud rate:
         * 
         *  8MHz clock and 2400bps
         *  $ echo "8000000 / 16 / 2400 - 1" | bc
         *  answer: 207
         *
         */
        UBRR0H = (UART0_BAUD >> 8);  /* high order bits */
        UBRR0L = UART0_BAUD;         /* low order bits */

        /* 
         * enable uart tx and rx 
         * enable synchronous transfer
         * databits 8
         */
        UCSR0B = (1 << RXEN0) | (1 << TXEN0);
        UCSR0C = (1 << UCSZ00) | (1 << UCSZ01);

        return;

}


/*
 * Enable PORTB for output
 */
void bounce_meter_init (void) {
        DDRB=0xFF;
        PORTB=0x00;
}


/*
 * Enable PORTC for output
 * Close Valve
 */
void solenoid_init (void) {
        DDRC=0xFF;
        PORTC=0x00;
}


/* 
 * Wait for a byte on the serial line and return it.
 */
uint8_t serial_recv_uart0 (void) {

  while (!(UCSR0A & (1 << RXC0)));      /* wait for rx char */
  return UDR0;                          /* return the char */

}


/* 
 * Wait for a byte on the serial line and return it.
 */
uint8_t serial_recv_uart1 (void) {

  while (!(UCSR1A & (1 << RXC1)));      /* wait for rx char */
  return UDR1;                          /* return the char */

}


/* 
 * Send the byte on the serial line.  
 */
void serial_send_rfid (uint8_t rfid_read) {

        while (!(UCSR1A & (1 << UDRE1)));       /* wait for tx buffer empty */
        UDR1 = rfid_read;                       /* send byte */
        return;

}


/* 
 * Send the byte on the serial line.  
 */
void serial_send_uart1 (uint8_t ez1_read) {

        while (!(UCSR1A & (1 << UDRE1)));       /* wait for tx buffer empty */
        UDR1 = ez1_read;                        /* send byte */
        return;

}

/* 
 * Send the buffer on the serial line. 
 */
void serial_print_char (char *msg) {

        while (*msg != 0)
                serial_send_uart1 (*msg++);

        return;
}


/*
 * slow it down
 */
void delay (uint16_t us)
{
    while ( us ) us--;
}


/*
 * Height of trampoline from ground
 * Used when circuit is first powered up
 * It is important to have nobody on the trampoline
 */
void base_height (void) {
        uint8_t i=0;

        ez1_sensor_read();

        /* 
         * copy first sensor reading to another array
         */
        while ( i < EZ1_ELEMENT_SIZE ) {
                base[i] = ez1_data[0][i];
                i++;
        }

}


/*
 * EZ1 (Ultra Sonic Sensor)
 * Reads the sensor N times (N = EZ1_SAVES)
 * values are available through ez1_data array
 */
void ez1_sensor_read (void) {

        uint8_t ez1_read;
        uint8_t j=0;
        uint8_t k=0;

        ez1_read = serial_recv_uart1();

        if ( ez1_read == 'R' && j <= EZ1_SAVES ) {

        while ( ez1_read != '\r' ) {
                ez1_read = serial_recv_uart1();
                /*
                 * Confirm that sensor has put out a ascii value
                 * between 0 and 9. Otherwise play the recursive
                 * game.
                 */
                if ( ez1_read >= 48 || ez1_read <= 57 ) {
                        ez1_data[j][k] = ez1_read;
                        k++;
                } else {
                        ez1_sensor_read();
                }
        }

        k=0;
        j++;


        } else {
                ez1_sensor_read();
        }

        return;
}


/*
 * Clear LCD
 */
void clear_lcd (void) {

        delay(8000);
        serial_send_uart1(254);
        serial_send_uart1(0x01);

}


/* 
 * longer delay
 */
void long_delay (void) {
        uint8_t i=0;

        while ( i < 30 ) {

                delay(65000);
                i++;

        }

}


/*
 * display ultra sonic sensor (EZ1) data on LCD
 */
void ez1_2_lcd (void) {
        uint8_t i=0;

        /* 
         * Base distance will  mostly be 19"
         */
        while ( i < EZ1_ELEMENT_SIZE ) {
                serial_send_uart1(base[i]);
                i++;
        }

        i=0;

        serial_send_uart1(' ');

        /*
         * Current Reading can be as large as 13"
         */
        while ( i < EZ1_ELEMENT_SIZE ) {
                serial_send_uart1(ez1_data[0][i]);
                i++;
        }

        /*
         * Subtracted value of current reading from base reading
         */
        serial_send_uart1(' ');
        serial_send_uart1('0');
        serial_send_uart1(height10_ascii);
        serial_send_uart1(height1_ascii);

        /*
         * Jump Rating 0-8 are elgible values
         * This number determines how long the 
         * lp-gas valve will be open.
         */
        serial_send_uart1(' ');
        serial_send_uart1(jump_rating_ascii);

        return;

}


/* 
 * read rfid tag number with parallax 
 * device made by Grand Idea Studio
 */
void rfid_tag_scan (void) {

        uint8_t rfid_read='n';
        uint8_t j=0;
        uint8_t k=0;

        /* 
         * Bounce Meter used to flash every other LED
         * indicating it is time to scan a RFID tag.
         */
        while ( rfid_read == 'z' ) {
                rfid_read = serial_recv_uart0();
                PORTB=0x55;
                delay(65000);
                PORTB=0xAA;
                delay(65000);
        }


        if ( rfid_read == '\n' && j <= RFID_SAVES ) {

                while ( rfid_read != '\r' && k < RFID_ELEMENT_SIZE ) {
                        rfid_read = serial_recv_uart0();
                        rfid_data[j][k] = rfid_read;
                        k++;
                }
        }

        k=0;
        j++;

        return;

}

/*
 * compare rfid tag read with approved tag list
 * do not proceed if tag fails to match
 */
uint8_t rfid_tag_check (void) {

        uint8_t j=0;
        uint8_t k=0;


        /* removed to protect the innocent in on-line code example */
        uint8_t tags[][RFID_ELEMENT_SIZE] = {
                {'0','0','0','0','0','0','0','0','0','0'},
                {'0','0','0','0','0','0','0','0','0','0'},
                {'0','4','0','0','0','0','0','0','0','0'},
        };

        clear_lcd();
        serial_print_char("Confirming Tag");

        while ( j < 3 ) {

                while ( k < RFID_ELEMENT_SIZE ) {

                        if ( tags[j][k] == rfid_data[0][k] ) {
                                k++;            /* See if each number matches */
                        } else {
                                j++;            /* Check next tag for match */
                                k=0;
                        }

        }

                /* 
                 * We have a tag match 
                 */
                if ( k > 8 ) {

                        clear_lcd();
                        serial_print_char("Valid Tag Found ");
                        rfid_2_lcd();                   /* Show RFID tag number on LCD           */
                        long_delay();                   /* Leave Tag up long enough to see       */
                        erase_tag();                    /* Prevent auto-startup on system reboot */
                        return(1);
                }

        }

        return(0);

}

/* 
 * Erase RFID tag number that was scanned to enable trampoline
 * This prevents the system from autostarting up 
 */
void erase_tag (void) {
        uint8_t i=0;

        while ( i < RFID_ELEMENT_SIZE ) {
                rfid_data[0][i] = 0;
                i++;
        }

}



/*
 * display RFID tag number on LCD
 * note: I am using the TX line on UART1 
 * for the LCD
 */
void rfid_2_lcd (void) {

        uint8_t i=0;

        while ( i < RFID_ELEMENT_SIZE ) {
                serial_send_rfid(rfid_data[0][i]);
                i++;
        }

        i=0;

        return;

}


/*
 * Simple solution to Convert seperate digits
 * into one whole number.
 */

   uint8_t ascii_2_digit (void) {
        uint8_t ez1_num;

        ez1_num = ez1_data[0][2];
        ez1_num += ez1_data[0][1] * 10;
        ez1_num += ez1_data[0][0] * 100;

        return ez1_num;
}


/*
 * LCD startup message
 */
void startup_message (void) {
        clear_lcd();                    /* blank LCD screen */

        serial_print_char("The High-Lighter");
        serial_print_char("                ");
        serial_print_char("Please, scan");
        serial_print_char("    ");
        serial_print_char("your RFID tag.");

        return;
}


/* 
 * trampoline height measurements
 * measures distance from EZ1 sensor to trampoline bottom
 * used for bounce meter and solenoid valve open duration
 */
void trampoline_height (void) {
        uint8_t b;              /* base height    */
        uint8_t c;              /* current height */
        uint8_t reduce=0;       /* subtract 10s column */
        uint8_t add=0;          /* increase 1s column */

        /*
         * 1's digit
         * adding +48 to get us to ascii numeric 0
         * some subtraction code for small base 1s digits
         */
        b = base[2];
        c = ez1_data[0][2];
        if ( b < c ) {
                reduce = 1;
                add = 10;
        }
        height1 = b - c + add;
        height1_ascii = height1 + 48;


        /*
         * 10's digit of height
         * adding +48 to get us to ascii numeric 0
         */
        b = base[1];
        c = ez1_data[0][1];

        /* 
         * If the current reading is greater than the base
         * we have a problem. We need to reset the base
         * to the current.
         */
        if ( c > b || b >= '3' ) {
                base_height();
        }
        height10 = b - c - reduce;
        height10_ascii = height10 + 48;

        /* 
         * 100s digit
         * Since this is a short range application we need to prevent
         * readings like 255 so I am resetting the base to current if
         * its value is not equal to zero.
         */
        b = base[0];

        if ( b != '0' ) {
                base_height();
        }


        return;

}

/*
 * Look at height change vs. base value
 * give a rating of 0 (minimal change) and 7 (significant change)
 */
void normalize_height (void) {


        if ( height10 > 1 ) {
                jump_rating = 0;        /* 20" or more height change */
                bounce_led = 0x00;      /* Do nothing! */
        } else if ( height10 == 1 && height1 >= 6 ) {
                jump_rating = 8;        /* 16" - 19" height change */
                bounce_led = 0xFF;      /* LEDs to illiuminate on PCB */
        } else if ( height10 == 1 && ( height1 == 4  || height1 == 5 ) ) {
                jump_rating = 7;        /* 14" - 16" height change */
                bounce_led = 0xFE;      /* LEDs to illiuminate on PCB */
        } else if ( height10 == 1 && ( height1 == 2  || height1 == 3 ) ) {
                jump_rating = 6;        /* 12" - 13" height change */
                bounce_led = 0xFC;      /* LEDs to illiuminate on PCB */
        } else if ( height10 == 1 && ( height1 == 0  || height1 == 1 ) ) {
                jump_rating = 5;        /* 10" - 12" height change */
                bounce_led = 0xF8;      /* LEDs to illiuminate on PCB */
        } else if ( height10 == 0 && ( height1 == 8  || height1 == 9 ) ) {
                jump_rating = 4;        /* 8" - 9" height change */
                bounce_led = 0xF0;      /* LEDs to illiuminate on PCB */
        } else if ( height10 == 0 && ( height1 == 6  || height1 == 7 ) ) {
                jump_rating = 3;        /* 6" - 7" height change */
                bounce_led = 0xE0;      /* LEDs to illiuminate on PCB */
        } else if ( height10 == 0 && ( height1 == 4  || height1 == 5 ) ) {
                jump_rating = 2;        /* 4" - 5" height change */
                bounce_led = 0xC0;      /* LEDs to illiuminate on PCB */
        } else if ( height10 == 0 && ( height1 == 3 ) ) {
                jump_rating = 1;        /* 3" height change */
                bounce_led = 0x80;      /* LEDs to illiuminate on PCB */
        } else {
                jump_rating = 0;
                bounce_led = 0x00;
        }

        jump_rating_ascii = jump_rating + 48;   /* Add 48 to get a ascii value for jump rating */

        return;

}


/*
 * bounce meter
 * display normalized bounce height on LEDs
 */
void bounce_meter (void) {

        PORTB = bounce_led;

        return;

}


/*
 * open solenoid valve based on normalized height 
 * aka. jump_rating which will be a multiplier of 
 * up to 8
 */
void solenoid_open (void) {
        uint16_t valve_base=32500;
        uint16_t valve_multiplier=4062;
        uint16_t valve_max=65000;
        uint16_t duration = 0;

        PORTC=0x00;             /* Close Valve */

        /*
         * length of time to keep valve open
         */

        if ( jump_rating > 0 ) {
                duration = jump_rating * valve_multiplier;
                duration += valve_base;
        }

        if ( duration <= valve_max ) {

                PORTC=0x01;             /* Open Valve */
                delay(duration);        /* Wait a short period of time */
                PORTC=0x00;             /* Close Valve */

        } else {

                return;

        }

        return;

}


int main() {

        uint8_t valid_tag = 0;          /* Flag to confirm that a valid tag is present */

        /* 
         * Initialize IO devices
         * EZ1 - ultrasonic sensor measure distance
         * RFID READER - lock, requires TAG to engage program
         * Bounce Meter - LED display of normaized distance measurements
         */
        ez1_sensor_init();
        rfid_reader_init();
        bounce_meter_init();
        solenoid_init();
        startup_message();              /* initial LCD message when powered up */

        /* 
         * Initial height from sensor to trampoline
         */
        base_height();

        while(1) {

                ez1_sensor_read();      /* take sensor read */
                trampoline_height();    /* distance between sensor and trampoline */
                normalize_height();     /* decide on scale of 0-7 about height change */
                bounce_meter();         /* Visual Height indicator */
                clear_lcd();            /* blank LCD screen */
                ez1_2_lcd();            /* send sensor read to LCD */

                if ( valid_tag ) {
                        solenoid_open();        /* solenoid open */
                } else {
                        rfid_tag_scan();                /* scan rfid tag */
                        valid_tag = rfid_tag_check();   /* confirm that tag is valid */
                }
        }

        return 0;

}