/***************************************************************************************************
  
  	Program:	    UAV_GPS_tracker.c
  
    Author:         Herbert Dingfelder
  		
    Application:    GPS data recorder for a unmanned aerial vehicel
  
  	Function:	    Receives GPS data from a Adafruit GPS engine via the UART, extracts the relevant
                    data, compresses the data to not more than 32Bytes per tracking point and stores
                    the data into a external serial (I2C) EEPROM of 32kByte size.
                    Upon receiving a special command via the UART from a PC, it reads out the
                    the data from EEPROM and sends them to PC via the UART.
  
    Target:         ATtiny84A

	Pinning:	    (Pin numbers reference to the 14pin SOIC (SMD) package)
							
							Pin	    Function	Used for				Alternate function
                            1       Vcc
                            2       XTAL1
                            3       XTAL2
                            4       Reset                               ISP Reset
                            5       PB2         SW UART RXD (from GPS)
                            6       PA7         SW UART TXD (debugging)
                            7       PA6         SW UART RXD2 (from PC)  ISP MOSI
                            8       PA5         SW UART TXD2 (to PC)    ISP MISO
                            9       PA4         LED green               ISP SCL
                            10      PA3         ADC for VBatt meas.
                            11      PA2         I2C SCL
                            12      PA1         I2C SDA
                            13      PA0                                
                            14      GND
							
							
    History:	    01.07.2015  Start of development based on some old code from 2002, which was
                                intended for roughly the same purpose (GPS data recording) but was
                                never completed and never used.12.11.2001. The oldest routines are
                                actually from a 68HC11 implementation from 1999.
                                
                    04.07.2015  Concept change: ATtiny84A will be used instead of ATmega8. That
                                means a SW UART must be used (ATtiny84A has no HW UART).
                                - SW UART transmitting works towards RaspberryPi
                                - I2C EEPROM writing and reading seems to work (roughly tested)
                    05.07.2015  - SW UART RX adapted for ATtiny84, timeout feature removed,
                                - UART works fine for RX and TX against terminal form RaspberryPi
                                - Extraction of all relevant GPS data and storing into a 32Byte
                                  array completed and initially tested with example data
                    06.07.2015  - Test for ADC readout implemented, test successfully completed
                    08.07.2015  - Bug fixed in receive_byte_via_sw_uart(). Syncing the timer on the 
                                  the leading edge of start bit of the incoming date was missing.
                                - Calculation of checksum for NMEA messages added and tested
                    12.07.2015  - Data are really written into the EEPROM now
                                - TXD2 introduced, for writing UART data to the MISO pin. This allows
                                  to read out the EEPROM with a very simple external circuit via the 
                                  same 6 pins, that are used for in circuit programming. Simply pull
                                  the MDR (memory data request) low, which is the same pin as MOSI,
                                  then after one second of delay, the circuit will spit out all 32kByte
                                  of data at 9k6 Baud to the PC.
                    14.07.2015  - The concept with pulling MDR low was not good. This was now changed
                                  to a completely independend 2nd SW UART, with RXD2 and TXD2. Since
                                  only one UART can listen at a time, the waiting loop on RXD(1) 
                                  includes a level checker on RXD2. Once a low level is detected, it 
                                  knows that the PC is sending data and switches over to listening on RXD2
                                  after a  10ms pause (for max. 10 seconds). 
                                  If a 'X' is received in that time (which must sent as another char from
                                  the PC after the intial tiggering char), the command
                                  mode on RXD2 is entered to receive further instructions, an 'ok' is
                                  send to the PC to signal successfull entered command mode.
                                  This was tested until reaching the command mode and works great.
                                  NOTE: Not every trial to trigger the intial listening on RXD2 will 
                                  work, as UART(1) may not be listening at that moment, e.g. because
                                  EEPROM writing is just in progress. So the PC must send a row of 'X',
                                  until 'ok' is reported back. (As there is a listening period of about
                                  200ms in each 1s loop, there is a 100% chance for success if the PC
                                  sends 'X' in 50ms intervals for one second.
                                   
                    15.07.2015  - A invalid-flag was introduced to prevent writing the current data set to 
                                  the eeprom in cases where the data set may be corrupted (rather no
                                  data in the eeprom than a corrupted one) in cases like interuption of
                                  reception due to switch to UART2, too low battery voltage or incorrect 
                                  checksums
                                   
                    18.07.2015  - Cosmetics on the source code in preparation of first release   
                                - Added explicit address counter roll-over at 32kByte boundary
                                - Practical tests (carrying around the module attached to a battery)
                                    => Defined first release candidate
                                - To save data to the EEPROM while the GPS has no fix does not make sense,
                                  therefore the dataset is marked invalid (not saved) when fix quality is zero
                                - To give the user a chance to download data during the first minute after
                                  power-on, without the risk that the recording starts and data in the EEPROM
                                  gets overwritten, recording during the first minute was looked
                                    => Defined second release candidate
                    19.07.2015  - Change in philosphie: No circular writing into the EEPROM any more. The memory
                                  must be explicitly cleared from the PC. Once the memory is full, the recording
                                  of new data will be stopped. Only after erasing the memory with the 'e'
                                  command from the PC, new recording will be possible.                                - 
                                - Tested, works fine so far.
                                    => Defined third release candidate
                    20.07.2015  - Minor source code cosmetics for release of first version
                                    => Defined this as Version V1.0
  
***************************************************************************************************/

#define F_CPU 11.059E6
#include <util/delay.h>
#include <avr/io.h>
#include <math.h>
#include <avr/wdt.h>
#include <avr/interrupt.h>
#include <string.h>
#include <stdlib.h>


// ----- prototypes for functions ----- 

void reset_handler(void);

void i2c_start(void);
void i2c_send(unsigned char sendbyte);
void i2c_pause(void);

unsigned int read_adc(unsigned char adc_number);

unsigned char hex2asc(unsigned char hexbyte);

uint8_t read_external_eeprom( uint16_t addr );
void write_external_eeprom( uint16_t addr, uint8_t databyte );

void send_byte_via_sw_uart(char byte_to_send);
void send_byte_via_sw_uart2(char byte_to_send);
int receive_byte_via_sw_uart(void);
int receive_byte_via_sw_uart2(void);

void wait_for_next_uart_cycle(void);

void debug_output(uint16_t value);

void wait_for_certain_byte_from_UART(uint8_t search_char);

void test_adc_vbatt(void);
uint16_t read_vbatt(void);

int send_eeprom_data_to_pc(void);
void write_dataset_to_eeprom(uint16_t addr);

int command_mode_interpreter(void);

void memory_full_handler(void);

uint16_t search_free_memory(void);

// ----- define names for special registers and bits -----

#define I2C_BUS             PORTA	        // i2c-bus port 
#define I2C_DIR             DDRA            // data direction register for i2c
#define I2C_IN              PINA            // physical value of i2c-bus
#define I2C_DATA            _BV(1)          // pin definition: bit 1 = I2C data, bit 2 = I2C clock 
#define I2C_CLOCK           _BV(2)
#define I2C_DATA_AND_CLOCK  (I2C_DATA+I2C_CLOCK)

#define RXD_PORT2 PORTA
#define RXD_PIN   _BV(2)                    // using Port B, Pin 2 for the RXD line from GPS
#define RXD_PIN2  _BV(6)                    // using Port A, Pin 6 for the RXD2 line from PC
#define RXD_READ  PINB
#define RXD_READ2 PINA

#define TXD_PORT PORTA
#define TXD_PORT2 PORTA
#define TXD_PIN  _BV(7)                     // using Port A, Pin 7 for the TXD, for debugging purposes
#define TXD_PIN2 _BV(5)                     // using Port A, Pin 5 for the TXD2, for sending EEPROM data to PC
#define TXD_DDR  DDRA
#define TXD_DDR2 DDRA

#define LED_port  	        PORTA           // LEDs are connected to Port A 
#define LED_DIR		        DDRA
#define LED_GREEN           _BV(4)          // green LED is connected to PA4

#define ADC_VBatt           3               // Vbatt is connected via voltage divider to ADC 3

// ----- macros to switch TXD to low/high, to enhance code readability -----

#define UART_TXD_LOW    (TXD_PORT &= ~(TXD_PIN) )       // makro to set TXD
#define UART_TXD_HIGH   (TXD_PORT |=  (TXD_PIN) )       // makro to set TXD
#define UART_RXD        (RXD_READ &   (RXD_PIN) )       // makro to read RXD back

#define UART_TXD2_LOW   (TXD_PORT2 &= ~(TXD_PIN2) )     // makro to set TXD and TXD2 low
#define UART_TXD2_HIGH  (TXD_PORT2 |=  (TXD_PIN2) )     // makro to set TXD and TXD2 high
#define UART_RXD2       (RXD_READ2 &   (RXD_PIN2) )     // makro to read RXD2 back

// ----- macros to switch the LED on and off, to enhance code readability -----

#define LED_ON          (LED_port |=  LED_GREEN)        // switch on the green LED
#define LED_OFF         (LED_port &= ~LED_GREEN)        // switch off the green LED

// ----- global memory allocation as buffer for the data to be written to EEPROM very second -----

uint8_t gps_data_buffer[32];        // stores all payload data of one set of GPS messages for writing to EEPROM
uint8_t data_set_invalid = 0;		// can be set to one if current data set shall not be stored in EEPROM	
uint16_t write_addr_counter = 0;    // address counter for the location in EEPROM that next set is written to


//;------------------------ here we go --------------------------

int main(void)
{
    
	uint8_t pos, d; 
    char msg_type[5];
    #define MAX_MSG_CONTENT_LENGTH 90
    char gpgga_msg_content[MAX_MSG_CONTENT_LENGTH];
    char gprmc_msg_content[MAX_MSG_CONTENT_LENGTH];
    char gpvtg_msg_content[MAX_MSG_CONTENT_LENGTH];
    uint8_t received_char;
    #define MAX_SUBSTRING_LENGTH 10
    char substring[MAX_SUBSTRING_LENGTH];
    uint16_t consecutive_number = 0;
    float height_float;
    uint8_t height_byte_high, height_byte_low;
    uint16_t heading;
    float speed_float;
    uint8_t speed_byte_high, speed_byte_low;     
    uint8_t checksum;
    uint16_t vbatt;
    uint8_t new_track;
    
    // ----- productive code starts here -----
    
    reset_handler();                    // set all the starting conditions, initialize UART timers, etc.
	
    _delay_ms(100);                     // for tracing on the first UART, signal program was started
    send_byte_via_sw_uart('-');         // (just a convenience for debugging and verifying)
    _delay_ms(100);
    send_byte_via_sw_uart('-');
    _delay_ms(100);
    send_byte_via_sw_uart('-');
    send_byte_via_sw_uart(13);
    send_byte_via_sw_uart(10);
    
    write_addr_counter = search_free_memory();  // jump over already filled memory blocks to avoid overwriting
    if(write_addr_counter>=0x8000)
        memory_full_handler();                  // catch the program flow right away, if memory is full already
    
    new_track=1;                                // set flag, indicating that a new track starts
    
    for(;;)     // ---- main loop for storing GPS data starts here -----
    {
               
        //--- wait for first GPS Message, starting with "$GPGGA", then read it's content ---
        
        do
        {
            wait_for_certain_byte_from_UART('$');           // wait for $, which is always starting a line
            
            for( pos=0 ; pos<5 ; pos++ )                    // read the next 5 chars, which define the message type
                msg_type[pos] = receive_byte_via_sw_uart();
        
        }
        while( strncmp( msg_type, "GPGGA", 5 ) );           // try until message type matches GPGGA

        pos=0;                
                        
        do                                                  // receive the message content until CR received
        {                                                   // or until buffer is full
        
            received_char = receive_byte_via_sw_uart();     // fetch character from UART
        
            if( pos < MAX_MSG_CONTENT_LENGTH )              // check for buffer overflow
            {
                
                gpgga_msg_content[pos] = received_char;     // take character into buffer
                pos++;                                      // increment buffer position
            }
        
        }
        while( received_char != 13 );                       // conclude this message when CR is received
        
        //--- wait for next GPS Message, starting with "$GPRMC", then read it's content ---
        
        do
        {
            wait_for_certain_byte_from_UART('$');           // wait for $, which is always starting a line
            
            for( pos=0 ; pos<5 ; pos++ )                    // read the next 5 chars, which define the message type
                msg_type[pos] = receive_byte_via_sw_uart();
        
        }
        while( strncmp( msg_type, "GPRMC", 5 ) );           // try until message type matches GPGGA

        pos=0;                
                        
        do                                                  // receive the message content until CR received
        {                                                   // or until buffer is full
        
            received_char = receive_byte_via_sw_uart();     // fetch character from UART
        
            if( pos < MAX_MSG_CONTENT_LENGTH )              // check for buffer overflow
            {
                
                gprmc_msg_content[pos] = received_char;     // take character into buffer
                pos++;                                      // increment buffer position
            }
        
        }
        while( received_char != 13 );                       // conclude this message when CR is received
        
        //--- wait for next GPS Message, starting with "$GPVTG", then read it's content ---
        
        do
        {
            wait_for_certain_byte_from_UART('$');           // wait for $, which is always starting a line
            
            for( pos=0 ; pos<5 ; pos++ )                    // read the next 5 chars, which define the message type
                msg_type[pos] = receive_byte_via_sw_uart();
        
        }
        while( strncmp( msg_type, "GPVTG", 5 ) );           // try until message type matches GPGGA

        pos=0;                
                        
        do                                                  // receive the message content until CR received
        {                                                   // or until buffer is full
        
            received_char = receive_byte_via_sw_uart();     // fetch character from UART
        
            if( pos < MAX_MSG_CONTENT_LENGTH )              // check for buffer overflow
            {
                
                gpvtg_msg_content[pos] = received_char;     // take character into buffer
                pos++;                                      // increment buffer position
            }
        
        }
        while( received_char != 13 );                       // conclude this message when CR is received
        
        

        checksum = 0;        // calculate the checksum for the GPGGA message
        checksum = 'G' ^ 'P' ^ 'G' ^ 'G' ^ 'A';     // GPGGA
        pos = 0;
        while((gpgga_msg_content[pos] != '*') & (pos < MAX_MSG_CONTENT_LENGTH) )
        {
            checksum ^= gpgga_msg_content[pos];
            pos++;
        }
        debug_output(checksum);
        
        checksum = 0;        // calculate the checksum for the GPRMC message
        checksum = 'G' ^ 'P' ^ 'R' ^ 'M' ^ 'C';     // GPRMC
        pos = 0;
        while((gprmc_msg_content[pos] != '*') & (pos < MAX_MSG_CONTENT_LENGTH) )
        {
            checksum ^= gprmc_msg_content[pos];
            pos++;
        }
        debug_output(checksum);
        
        checksum = 0;        // calculate the checksum for the GPVTG message
        checksum = 'G' ^ 'P' ^ 'V' ^ 'T' ^ 'G';     // GPVTG
        pos = 0;
        while((gpvtg_msg_content[pos] != '*') & (pos < MAX_MSG_CONTENT_LENGTH) )
        {
            checksum ^= gpvtg_msg_content[pos];
            pos++;
        }
        debug_output(checksum);
        
        // FIXME: compare calculated checksum with entries in GPS messages
        // set data to invalid if checksum is not correct, so it will not be stored to eeprom
        
       
        #ifdef ECHO_GPS_MSG                             // this block is for debugging and is not
                                                        // being compiled for normal operation
            cnt=0;
            while(gpgga_msg_content[cnt]!=13)
            {   
                send_byte_via_sw_uart(gpgga_msg_content[cnt]);
                cnt++;
            }
            send_byte_via_sw_uart(13);
            send_byte_via_sw_uart(10);
            
            cnt=0;
            while(gprmc_msg_content[cnt]!=13)
            {   
                send_byte_via_sw_uart(gprmc_msg_content[cnt]);
                cnt++;
            }
            send_byte_via_sw_uart(13);
            send_byte_via_sw_uart(10);
            
            cnt=0;
            while(gpvtg_msg_content[cnt]!=13)
            {   
                send_byte_via_sw_uart(gpvtg_msg_content[cnt]);
                cnt++;
            }
            send_byte_via_sw_uart(13);
            send_byte_via_sw_uart(10);
        
        #endif
        
        // ----- now we have all the data, lets start to sort it out for storage -----
        
        pos = 0;                                            // reset postion, we start with message GPGGA
        
        
        gps_data_buffer[0] = 0xAA;                          // 0xAA indicates that this memory block is used 
        
        gps_data_buffer[1] = 0xFF;                          // normal records (no starting point) have byte 1 set to 0xFF
        
        gps_data_buffer[2] = consecutive_number >> 8;       // cons. number since start of recording (highbyte)
        gps_data_buffer[3] = consecutive_number & 0xFF;     // cons. number since start of recording (lowbyte)
        
        while(gpgga_msg_content[pos]!=',') pos++;           // search for first comma delimiter
        
        substring[0] = gpgga_msg_content[pos+1];            // extract hour of time
        substring[1] = gpgga_msg_content[pos+2];
        substring[2] = '\0';
        gps_data_buffer[20] = atoi(substring);
        
        substring[0] = gpgga_msg_content[pos+3];            // extract minute of time
        substring[1] = gpgga_msg_content[pos+4];
        substring[2] = '\0';
        gps_data_buffer[21] = atoi(substring);
        
        substring[0] = gpgga_msg_content[pos+5];            // extract second of time
        substring[1] = gpgga_msg_content[pos+6];
        substring[2] = '\0';
        gps_data_buffer[22] = atoi(substring);
        
        pos++; while(gpgga_msg_content[pos]!=',') pos++;    // search for next comma delimiter
        
        substring[0] = gpgga_msg_content[pos+1];            // extract deg of latitude
        substring[1] = gpgga_msg_content[pos+2];
        substring[2] = '\0';
        gps_data_buffer[4] = atoi(substring);
       
        substring[0] = gpgga_msg_content[pos+3];            // extract minute part 0 of latitude
        substring[1] = gpgga_msg_content[pos+4];
        substring[2] = '\0';
        gps_data_buffer[5] = atoi(substring);
        
        substring[0] = gpgga_msg_content[pos+6];            // extract minute part 1 of latitude
        substring[1] = gpgga_msg_content[pos+7];
        substring[2] = '\0';
        gps_data_buffer[6] = atoi(substring);
        
        substring[0] = gpgga_msg_content[pos+8];            // extract minute part 2 of latitude
        substring[1] = gpgga_msg_content[pos+9];
        substring[2] = '\0';
        gps_data_buffer[7] = atoi(substring);
     
        pos++; while(gpgga_msg_content[pos]!=',') pos++;    // search for next comma delimiter
        
        gps_data_buffer[8] = gpgga_msg_content[pos+1];      // latitude N or S
        
        pos++; while(gpgga_msg_content[pos]!=',') pos++;    // search for next comma delimiter
        
        substring[0] = gpgga_msg_content[pos+1];            // extract deg of longitude
        substring[1] = gpgga_msg_content[pos+2];
        substring[2] = gpgga_msg_content[pos+3];
        substring[3] = '\0';
        gps_data_buffer[9] = atoi(substring);
        
        substring[0] = gpgga_msg_content[pos+4];            // extract minute part 0 of longitude
        substring[1] = gpgga_msg_content[pos+5];
        substring[2] = '\0';
        gps_data_buffer[10] = atoi(substring);
        
        substring[0] = gpgga_msg_content[pos+7];            // extract minute part 1 of longitude
        substring[1] = gpgga_msg_content[pos+8];
        substring[2] = '\0';
        gps_data_buffer[11] = atoi(substring);
        
        substring[0] = gpgga_msg_content[pos+9];            // extract minute part 2 of longitude
        substring[1] = gpgga_msg_content[pos+10];
        substring[2] = '\0';
        gps_data_buffer[12] = atoi(substring);
        
        pos++; while(gpgga_msg_content[pos]!=',') pos++;    // search for next comma delimiter
        
        gps_data_buffer[13] = gpgga_msg_content[pos+1];     // longitude E or W
     
        pos++; while(gpgga_msg_content[pos]!=',') pos++;    // next comma delimiter (fix quality)
        
        gps_data_buffer[30] = gpgga_msg_content[pos+1];     // Fix quality: 0=Invalid, 1=GPS fix, 2=DGPS fix
        
        if( gps_data_buffer[30] =='0' )                     // If the data is not valid, there is no point in 
            data_set_invalid = 1;                           //   in storing it in the EEPROM => set invalid-flag
        
        pos++; while(gpgga_msg_content[pos]!=',') pos++;    // search for next comma delimiter (no. of sats)
       
        substring[0] = gpgga_msg_content[pos+1];            // extract number of sats use (max 3 digits)
        substring[1] = gpgga_msg_content[pos+2];
        substring[2] = gpgga_msg_content[pos+3];
        substring[3] = '\0';
        gps_data_buffer[16] = atoi(substring);
        
        pos++; while(gpgga_msg_content[pos]!=',') pos++;    // search for next comma delimiter
        pos++; while(gpgga_msg_content[pos]!=',') pos++;    // (jump HDOP, we ignore that)
        
        for(d=0;d<8;d++)
        {    
            substring[d] = gpgga_msg_content[pos+1+d];              // extract height (ASL), max 8 digits
            debug_output(substring[d]);
        }
        substring[8] = '\0';
        
        height_float = atof(substring);                             // height as floating point value in meters
        height_byte_high = ((uint16_t)(height_float*10))>>8;        // height in 10cm, high byte
        height_byte_low  = ((uint16_t)(height_float*10))&0xff;      // height in 10cm, low byte 
        
        gps_data_buffer[14] = height_byte_high;
        gps_data_buffer[15] = height_byte_low;
        
        #ifdef DEBUG_OUT
        send_byte_via_sw_uart(13);
        send_byte_via_sw_uart(10);
        debug_output(height_byte_high);
        debug_output(height_byte_low);
        #endif
        
        pos=0;                                              // reset postion, start to work on next message (GPRMC)
        
        while(gprmc_msg_content[pos]!=',') pos++;           // search for first comma delimiter
        pos++; while(gprmc_msg_content[pos]!=',') pos++;    // skip time (we have that already)
        
        gps_data_buffer[23] = gprmc_msg_content[pos+1];     // GPS status (A=OK, V=Warning)
        
        pos++; while(gprmc_msg_content[pos]!=',') pos++;    // skip latitude (we have that already)
        pos++; while(gprmc_msg_content[pos]!=',') pos++;    // skip N/S (we have that already)
        pos++; while(gprmc_msg_content[pos]!=',') pos++;    // skip longitude (we have that already)
        pos++; while(gprmc_msg_content[pos]!=',') pos++;    // skip E/W (we have that already)
        pos++; while(gprmc_msg_content[pos]!=',') pos++;    // skip speed of ground in knots (ignored)    
        pos++; while(gprmc_msg_content[pos]!=',') pos++;    // skip course made good true (ignored)    
        pos++; while(gprmc_msg_content[pos]!=',') pos++;    // search for next comma to go to date
        
        substring[0] = gprmc_msg_content[pos+1];            // extract day of date
        substring[1] = gprmc_msg_content[pos+2];
        substring[2] = '\0';
        gps_data_buffer[19] = atoi(substring);
             
        substring[0] = gprmc_msg_content[pos+3];            // extract month of date
        substring[1] = gprmc_msg_content[pos+4];
        substring[2] = '\0';
        gps_data_buffer[18] = atoi(substring);
          
        substring[0] = gprmc_msg_content[pos+5];            // extract year of date
        substring[1] = gprmc_msg_content[pos+6];
        substring[2] = '\0';
        gps_data_buffer[17] = atoi(substring);
          
        pos=0;                                              // reset postion, start to work on next message (GPVTG)
        
        while(gpvtg_msg_content[pos]!=',') pos++;           // search for first comma delimiter
        
        substring[0] = gpvtg_msg_content[pos+1];            // extract deg of heading (true north)
        substring[1] = gpvtg_msg_content[pos+2];
        substring[2] = gpvtg_msg_content[pos+3];
        substring[3] = '\0';
        heading = atoi(substring);
        gps_data_buffer[24] = heading>>8;
        gps_data_buffer[25] = heading&0xff;
        
        pos++; while(gpvtg_msg_content[pos]!=',') pos++;    // skip indicator for true heading
        pos++; while(gpvtg_msg_content[pos]!=',') pos++;    // skip magentic north heading
        pos++; while(gpvtg_msg_content[pos]!=',') pos++;    // skip indicator for magnetic heading
        pos++; while(gpvtg_msg_content[pos]!=',') pos++;    // skip ground speed in knots
        pos++; while(gpvtg_msg_content[pos]!=',') pos++;    // skip indicator that units is knots
        pos++; while(gpvtg_msg_content[pos]!=',') pos++;    // search for next comma to speed in km/h
             
     
        for(d=0;d<8;d++)
        {    
            substring[d] = gpvtg_msg_content[pos+1+d];      // extract speed in km/h, max 8 digits
            debug_output(substring[d]);
        }
        substring[8] = '\0';
        
        speed_float = atof(substring);                      // speed as float value in km/h
        
        speed_byte_high = ((uint16_t)(speed_float*100))>>8;     // speed in 0.01 km/h, high byte
        speed_byte_low  = ((uint16_t)(speed_float*100))&0xff;   // speed in 0.01 km/h, low byte
            
        gps_data_buffer[26] = speed_byte_high;
        gps_data_buffer[27] = speed_byte_low;

         
        vbatt = read_vbatt();                                   // read battery voltage in mV from ADC
        
        if( vbatt < 3300 )                                      // safe EEPROM from corruption if voltage too low
            data_set_invalid = 1;
        
        gps_data_buffer[28] = (vbatt >> 8) & 0xFF;              // store highbyte and lowbyte of battery voltage
        gps_data_buffer[29] = (vbatt     ) & 0xFF;   

        gps_data_buffer[31] = 0xff;
        
     
        // --- all data is processed ---
        
        if( data_set_invalid == 0 )                             // unless data set was marked as invalid...
        {    
            
            if( new_track )                                     // the first block of a new track (i.e. after a new start)
            {
                gps_data_buffer[1] = 0x11;                      // byte 1 is set to 0x11 as an indicator
                new_track=0;                                    // clear the flag, all further records are not the 
            }                                                   //   start of new tracks
            
            write_dataset_to_eeprom(write_addr_counter);        // write the 32 bytes of data to the EEPROM
            
            write_addr_counter += 32;                           // raise the addr counter for next data set to store
            
            if(write_addr_counter>=0x8000)                      // since we have only 32kByte of EEPROM capacity,
                memory_full_handler();                          //   jump to catcher routine when memory full
            
            consecutive_number++;                               // increment data-set counter
        }
        else
        {
            data_set_invalid = 0;                               // for the next data set, clear invalid-flag
        }
        
    }
    
    
}


// ---------------------------------- Subroutines from here on -------------------------------------

//----------------------------------- Reset handler for startup ------------------------------------
void reset_handler(void)
{
                 
    RXD_PORT2 |=  RXD_PIN2;             // switch on pull up, to keep RXD2 line high until externally 
                                        //   pulled low by PC to tigger sending memory data    

    LED_DIR |= LED_GREEN;               // set the green LED pin to output 
        
    TXD_DDR |= TXD_PIN;                 // configure TXD pin aus output
    UART_TXD_HIGH;                      // TXD output to high (means UART idle)
    
    TXD_DDR2 |= TXD_PIN2;               // configure TXD2 pin aus output
    UART_TXD2_HIGH;                     // TXD2 output to high (means UART idle)
   
    
    // --- switch on a permanent clock source that runs at the baudrate of 9600kHz
    
    // baudrate is 9600 bit/s => bit length is 104.167µs
    // main clock is 11.059MHz => clock period 90.42ns 
    // => 1152 main clock cycles per bit length
    
    // set counter compare match to 1152-1 = 1151
    // (the -1 is due to the fact that the value is actually reached before the counter is reset)
    OCR1A = 1151;        // compiler can handle the 16bit register access, simply assign the number
    
    // for sampling incoming bits in the middle, use the second compare match unit at half that value
    OCR1B = 575;
    
    // switch on the counter with prescale factor 1 (i.e. running at the system main clock)
    // and switch on the clear-on-compare-match feature to reset the counter automatically when the
    // value of the compare match register A is reached
    TCCR1B = _BV(CS10) | _BV(WGM12);
    
    TCNT1 = 0;                          // reset the counter, so it starts to count at zero
    
    // Timer/Counter 0 is use for timeouts, it is set run as slowly as possible (10.8kHz)
    
    TCCR0B = 0;                         // stop counter for clean configuration
    TCNT0 = 0;                          // reset the counter
    TIFR0 |= _BV(TOV0);                 // reset overflow flag 
    TCCR0B |= _BV(CS02) | _BV(CS00);    // start 8bit counter at fc/1024=10,8kHz
      
    _delay_ms(1000);                    // give the whole system one second before we go productive
    
}

//------------------------ i2c start command ---------------------
void i2c_start(void)
{

	//keep the databits itself to low, outputs will we controlled by switching the data direction register
	
    I2C_BUS &= ~I2C_DATA_AND_CLOCK;     // output->low at pin, input->extern 10k resistor will create a high at pin	 
   	
    _delay_us(2);                       // reduce speed of I2C bus to specified level

	I2C_DIR &= ~I2C_DATA_AND_CLOCK;     // clearing data+clock dir. bit sets them to input->high at pin)
	
    _delay_us(2);                       // reduce speed of I2C bus to specified level

    I2C_DIR |=  I2C_DATA;               // setting data-dir bit sets data to output->low at pin
	
    _delay_us(2);                       // reduce speed of I2C bus to specified level				
    
	I2C_DIR |=  I2C_CLOCK;              // setting clock-dir bit sets clock to output->low at pin
	
	_delay_us(2);                       // reduce speed of I2C bus to specified level		
    	
}

//------------------------ i2c stop command ----------------------
void i2c_stop(void)
{
			
	I2C_DIR &= ~I2C_CLOCK;              // clearing clock-dir bit sets clock to input->high at pin
	
 	_delay_us(2);                       // reduce speed of I2C bus to specified level	
    
	I2C_DIR &= ~I2C_DATA;               // clearing data-dir bit sets data to input->high at pin
	
    _delay_us(2);                       // reduce speed of I2C bus to specified level	
    	
}

//--------------------- send byte via i2c bus --------------------
void i2c_send(unsigned char sendbyte)
{
 	uint8_t dummy;

	for(dummy=0; dummy<8; dummy++)		// send all 8 bits 
	{
	
		if( sendbyte & 0b10000000 )	 	// if msb of sendbyte is high ->
			 I2C_DIR &= ~I2C_DATA;		// clearing data-dir. releases the data 
					   					// pin and it snaps to high
		
        _delay_us(2);                   // reduce speed of I2C bus to specified level									
    									
		I2C_DIR &= ~I2C_CLOCK;			// shortly release the clock pin for a clock pulse
        
        _delay_us(2);                   // reduce speed of I2C bus to specified level		
        
        I2C_DIR |=  I2C_CLOCK;		    // take the clock back to zero	
		
        _delay_us(2);                   // reduce speed of I2C bus to specified level		
        		
		I2C_DIR |=  I2C_DATA;			// pull data back to low
		
		sendbyte = sendbyte << 1;		// logic shift left to shift the next
						 				// bit to the msb position
		
	}			
		
	// send one additional pulse on the clock-line for i2c-acknowledge 
    
    I2C_DIR &= ~I2C_CLOCK;			    // shortly release the clock pin for a clock pulse
        
    _delay_us(2);                       // reduce speed of I2C bus to specified level		
    
    I2C_DIR |=  I2C_CLOCK;		        // take the clock back to zero	
    
    _delay_us(2);                       // reduce speed of I2C bus to specified level		
    		
}

//------------------------------- read adc -----------------------
unsigned int read_adc(unsigned char adc_number)
{
    uint16_t adc_result;
    
    ADCSRA |= _BV(ADPS2) | _BV(ADPS1) | _BV(ADPS0); // ADC prescaler :128, that 
                                        // gives ADC frequency of 11.059MHz/128 = 86kHz

    ADMUX = adc_number;                 // select Vcc reference (Bits 7 and 6 remain zero)
                                        // and chose the ADC channel

    ADCSRA |= _BV(ADEN);                // switch on the ADC in general

    ADCSRA |= _BV(ADSC);                // start a single ADC conversion
    while( ADCSRA & _BV(ADSC) );        // wait until conversion is complete

    // data sheet recommends to discard first conversion, so we do one more
    ADCSRA |= _BV(ADSC);                // start a single ADC conversion
    while( ADCSRA & _BV(ADSC) );        // wait until conversion is complete

    adc_result = ADC;                   // take over ADC reading result into variable

    ADCSRA = 0;                         // completely disable the ADC to save power
    	
	return adc_result;
	
}

//----- hex to asc converts a hex value (0x00..0x0F) into the ASCII-character '0'..'F'-----
unsigned char hex2asc(unsigned char hexbyte)
{
	
	hexbyte += 48;			            // ASCII code for 0-9 is 48-57
	
	if(hexbyte>=58)			            // ASCII code for A-F is 65-70 
		hexbyte += 7;		            // -> in that case add 7
	
	return hexbyte;
}

// ------------------------------ read_external_eeprom -----------
uint8_t read_external_eeprom( uint16_t addr )
{
 	unsigned char counter;			    // variable for counting the 8 bits
	unsigned char in_value;			    // variable for reading i2c data from port
	unsigned char byteread=0; 		    // reset the read-result variable 
	uint8_t addr_high, addr_low;
    
    addr_high = (addr>>8) & 0xff;       // split 16bit address in high byte and low byte
    addr_low  = (addr   ) & 0xff;	
	
    i2c_start();		   			    // Start command on I2C-Bus

	i2c_send(0b10100000);  			    // send the I2C-addr. of the external EEPROM
                                        // on the I2C-Bus with r/w-bit set to 1 => dummywrite

	i2c_send( addr_high ); 			    // send the higher byte of the address
	i2c_send( addr_low );  			    // send the low byte of the address

	i2c_start();	   	   			    // another start-command on I2C-Bus

	i2c_send( 0b10100001 ); 		    // send the I2C-addr. of the external EEPROM
                                        // on the I2C-Bus with r/w-bit set to 0 => now read

	//release i2c-data (open collector with pull up!) to high so that the
	//external EEPROM can pull it down to 0 (i.e. set the direct. to input)
	I2C_DIR &= ~I2C_DATA;
	
    _delay_us(2);                       // reduce speed of I2C bus to specified level	
	
	
	for( counter=0 ; counter<=7 ; counter++ )   //read all data 8 bits, one after another 
	{
	
        byteread <<= 1;  			    // shift the already read value for one bit to the
                                        // the left to make room for the next bit
                                
        in_value = I2C_IN;		        // read the value of the Port that is used for the I2C-bus 
                                
        in_value &= I2C_DATA;		    // mask out the i2c-data pin
                                
        if(in_value)                    // if a 1 was read on the i2c data pin...
            byteread |= 1;              // set the LSB in byteread

        I2C_DIR &= ~I2C_CLOCK;	        // shortly release the clock pin for a clock pulse
        _delay_us(2);                   // reduce speed of I2C bus to specified level		
        I2C_DIR |=  I2C_CLOCK;
        _delay_us(2);                   // reduce speed of I2C bus to specified level		
	}
				
	i2c_stop();		  	                // send I2C-stop command
	
	return byteread;	                // return the value that was read from the external EEPROM
		   				  
}
	

// ------------------------------ write_external_eeprom --------------------------------- 
void write_external_eeprom( uint16_t addr, uint8_t databyte )
{
	
    uint8_t addr_high, addr_low;
    
    addr_high = (addr>>8) & 0xff;       // split 16bit address in high byte and low byte
    addr_low  = (addr   ) & 0xff;
    
	i2c_start();		   			    // Start command on I2C-Bus

	i2c_send(0b10100000);  			    // send the I2C-addr. of the external EEPROM
                                        // on the I2C-Bus with r/w-bit set to 1 => write
									
	i2c_send( addr_high ); 			    // send the higher byte of the address
	i2c_send( addr_low );  			    // send the low byte of the address		
						
	i2c_send( databyte );			    // send the databyte to the eeprom			
	
	i2c_stop();		   				    // Stop command on I2C-Bus
	
	_delay_ms(5);			            // 5ms pause for the eeprom to complete the write

}

// ------------------------------- send byte via uart --------------------------
void send_byte_via_sw_uart(char byte_to_send)
{
    uint8_t bit;    
    
    wait_for_next_uart_cycle();         // synchronize with the next overflow of the timer
    
    UART_TXD_LOW;                       // send startbit (always 0)
    
    for( bit=0 ; bit<8 ; bit++ )        // send bit by bit, starting with LSB
    {
       
        wait_for_next_uart_cycle();     // synchronize with the next overflow of the timer
        
        if(byte_to_send & _BV(0))       // take TXD line to high if LSB is set
            UART_TXD_HIGH;
        else
            UART_TXD_LOW; 
        
        byte_to_send>>=1;               // shift the byte to be send by one bit towards LSB
    
    }   
    
    wait_for_next_uart_cycle();         // synchronize with the next overflow of the timer

    UART_TXD_HIGH;                      // send stopbit (always 1)
       
}

// ------------------------------- send byte via uart2 (to PC) --------------------------
void send_byte_via_sw_uart2(char byte_to_send)
{
    uint8_t bit;    
    
    wait_for_next_uart_cycle();         // synchronize with the next overflow of the timer
    
    UART_TXD2_LOW;                      // send startbit (always 0)
    
    for( bit=0 ; bit<8 ; bit++ )        // send bit by bit, starting with LSB
    {
       
        wait_for_next_uart_cycle();     // synchronize with the next overflow of the timer
        
        if(byte_to_send & _BV(0))       // take TXD line to high if LSB is set
            UART_TXD2_HIGH;
        else
            UART_TXD2_LOW; 
        
        byte_to_send>>=1;               // shift the byte to be send by one bit towards LSB
    
    }   
    
    wait_for_next_uart_cycle();         // synchronize with the next overflow of the timer

    UART_TXD2_HIGH;                     // send stopbit (always 1)
        
    
}

// ------------------------------ receive byte via uart (blocking)------------------------
int receive_byte_via_sw_uart(void)
{

    unsigned char bit_counter;
    unsigned char byte_received=0;
  
    LED_OFF;                                    // lion part of the 1s interfall from GPS is waiting
                                                // (during that we keep the LED off)
    
    while( UART_RXD )                           // wait for UART RXD to go low (beginning of startbit)
    {                                           // (this is the most frequented waiting loop)
        
        if( UART_RXD2 == 0)                     // here we check if the PC is trying to establish a contact 
        {                                       //   via UART2, to download EEPROM data or erase it
            
            command_mode_interpreter();         // if so, we interupt recording and jump to command mode
            
            data_set_invalid = 1;               // remember that the current data set was interupted
                                                //   and that we should not save it to the EEPROM 
                                                
            return -1;                          // once back we leave this routine with error
        }                                       
        
    }    
     
    LED_ON;                                     // from now till next waiting, switch LED on  
    
    TCNT1 = 0;                                  // reset counter to synchronize on incoming bits

                                                // read 8 data bit (plus start bit)
    for( bit_counter = 0; bit_counter < 9 ; bit_counter++ )
    {

        byte_received >>= 1;                    // shift the so far received bits one right
            
        TIFR1 |= _BV(OCF1B);                    // clear output compare B match flag
        while( !(TIFR1 & _BV(OCF1B)) );         // wait for middle of bit
          
        if( UART_RXD )                          // sample on RXD, if it is high...
        {
            byte_received |= _BV(7);            // ...set MSB in received byte
        }
        
    }

    // now only the stop bit is left, which we simply ignore as it is always high
    TIFR1 |= _BV(OCF1B);                    // clear output compare B match flag
    while( !(TIFR1 & _BV(OCF1B)) );         // wait for middle of bit

    // intentionally we do not wait for the end of the stop bit here, that gives us
    // half a bit of time to process the received byte, before the next one may arive
    // (waiting for end of stop bit could be done with: wait_for_next_uart_cycle(); )

    return byte_received;

}
    // -------------- receive byte via 2nd uart (blocking but with 1sec timeout)--------------------
int receive_byte_via_sw_uart2(void)
{

    unsigned char bit_counter;
    unsigned char byte_received=0;
    uint8_t overflow_counter;
  
    overflow_counter=0;
    
    TCNT0 = 0;                                  // reset the counter
    TIFR0 |= _BV(TOV0);                         // reset overflow flag of timer 0 (used for timeout)
    
  
    while( UART_RXD2 )                          // wait for UART RXD2 to go low (beginning of startbit)
    {                                      
        
        
        if(TIFR0 & _BV(TOV0))                   // if timer has overflow (every 23ms)
        {
            TIFR0 |= _BV(TOV0);                 // reset overflow flag
            overflow_counter++;                 // counter the overflows
        
            if(overflow_counter > 42)           // give up after 42 overflows (1sec)
                return -1;                      // => return with error to calling routine
        }
        
    }    
         
    
    TCNT1 = 0;                                  // reset counter to synchronize on incoming bits

                                                // read 8 data bit (plus start bit)
    for( bit_counter = 0; bit_counter < 9 ; bit_counter++ )
    {

        byte_received >>= 1;                    // shift the so far received bits one right
            
        TIFR1 |= _BV(OCF1B);                    // clear output compare B match flag
        while( !(TIFR1 & _BV(OCF1B)) );         // wait for middle of bit
          
        if( UART_RXD2 )                         // sample on RXD2, if it is high...
        {
            byte_received |= _BV(7);            // ...set MSB in received byte
        }
        
    }

    // now only the stop bit is left, which we simply ignore as it is always high
    TIFR1 |= _BV(OCF1B);                    // clear output compare B match flag
    while( !(TIFR1 & _BV(OCF1B)) );         // wait for middle of bit

    // intentionally we do not wait for the end of the stop bit here, that gives us
    // half a bit of time to process the received byte, before the next one may arive
    // (waiting for end of stop bit could be done with: wait_for_next_uart_cycle(); )

    return byte_received;

}
    

// ----------- synchronize with the next overflow of timer1 -----------------
void wait_for_next_uart_cycle(void)
{
    TIFR1 |= _BV(OCF1A);                        // clear the timer overflow flag
    while( !( TIFR1 & _BV(OCF1A)) );            // wait until overflow flag is set again
}

// --- send the 16bit parameter value as readable hex number to UART for debugging ---
void debug_output(uint16_t value)
{
    
    #ifdef DEBUG_OUT                             // for debugging only, normally not compiled

        send_byte_via_sw_uart('0');
        send_byte_via_sw_uart('x');
        send_byte_via_sw_uart(hex2asc((value>>12)&0x0f));
        send_byte_via_sw_uart(hex2asc((value>> 8)&0x0f));
        send_byte_via_sw_uart(hex2asc((value>> 4)&0x0f));
        send_byte_via_sw_uart(hex2asc((value    )&0x0f));
        send_byte_via_sw_uart(' ');    

    #endif

}

// ----- receive characters from UART until a specific one is received -----
void wait_for_certain_byte_from_UART(uint8_t search_char)
{
    uint8_t received_char;
    
    do
    {
        received_char = receive_byte_via_sw_uart();
    }
    while( received_char != search_char );
    
}

// ----- reads the battery voltage via a 1:2 voltage divider, with averaging, returns voltage in mV -----
uint16_t read_vbatt(void)
{

    uint16_t adc_result, avg_sum, adc_min, adc_max;
    uint8_t a;
    uint32_t voltage_mv;
    
    adc_min=0xffff;
    adc_max=0x0000;
    avg_sum=0;
    
    for(a=0; a<8; a++)                                      // do an averaging over 8 ADC reads
    {
        adc_result = read_adc(3);                           // read out the ADC that is connected to Vbat
        
        avg_sum += adc_result;                              // add up the results
        
        if(adc_result<adc_min) adc_min=adc_result;          // track the minimum value during the averaging
        if(adc_result>adc_max) adc_max=adc_result;          // track the maximum value during the averaging
        
        _delay_ms(1);                                       // wait 1ms for good independece between single reads
        
    }
    
    avg_sum >>= 3;                                          // divide the added results by 8 for final result
    
    voltage_mv = (6600 * (long)avg_sum) / 1024;             // calculate the voltage in mV from the ADC results
    
    return voltage_mv;                                      // retur the battery voltage in mV to calling routine

}


// ----- test routine for ADC and its noise -----
#ifdef ADC_DEBUG                                            // for debugging only, normally not compiled
void test_adc_vbatt(void)
{
    uint16_t adc_result, avg_sum, adc_min, adc_max;
    uint8_t a;
    uint32_t voltage_mv;
    
    for(;;)
    {
        adc_min=0xffff;
        adc_max=0x0000;
        avg_sum=0;
        
        for(a=0; a<8; a++)
        {
            adc_result = read_adc(3);
            
            debug_output(adc_result);
        
            avg_sum += adc_result;
            
            if(adc_result<adc_min) adc_min=adc_result;
            if(adc_result>adc_max) adc_max=adc_result;
            
            _delay_ms(1);
            
        }
        
        avg_sum >>= 3;
        
        send_byte_via_sw_uart('a');
        send_byte_via_sw_uart(':');
        debug_output(avg_sum);
        
        send_byte_via_sw_uart('b');
        send_byte_via_sw_uart(':');
        debug_output(adc_min);
        
        send_byte_via_sw_uart('t');
        send_byte_via_sw_uart(':');
        debug_output(adc_max);
        
        send_byte_via_sw_uart('d');
        send_byte_via_sw_uart(':');
        debug_output(adc_max-adc_min);
        
        send_byte_via_sw_uart('v');
        send_byte_via_sw_uart(':');
        voltage_mv = (6600 * (long)avg_sum) / 1024;
        debug_output(voltage_mv);
        
        char outputstring[32];
        sprintf(outputstring, "Voltage: %ld mV", voltage_mv);
        
        a=0;
        while(outputstring[a]!='\0')
        {
            send_byte_via_sw_uart(outputstring[a]);
            a++;
        }
        
        send_byte_via_sw_uart(13);
        send_byte_via_sw_uart(10);
        _delay_ms(1000);
        
        
    }
}
#endif

// --- completely dump the extracted data to the UART for debugging --- 
#ifdef DEBUG_OUT                            // for debugging only, normally not compiled
void trace_out_extracted_data(void)
{
    
    uint8_t cnt;
    
    for( cnt=0 ; cnt<32 ; cnt++ )
    {
        send_byte_via_sw_uart(13);
        send_byte_via_sw_uart(10);
        send_byte_via_sw_uart(cnt/10+48);
        send_byte_via_sw_uart(cnt%10+48);
        send_byte_via_sw_uart(' ');
        debug_output(gps_data_buffer[cnt]);
        send_byte_via_sw_uart(' ');
        send_byte_via_sw_uart(gps_data_buffer[cnt]/10+48);
        send_byte_via_sw_uart(gps_data_buffer[cnt]%10+48);
        send_byte_via_sw_uart(' ');
        if(gps_data_buffer[cnt]>='0' && gps_data_buffer[cnt]<='z')
            send_byte_via_sw_uart(gps_data_buffer[cnt]);   
    }
   
}
#endif

// ----- read all 32kByte EEPROM data and send it via the UART2 to a connected PC -----
int send_eeprom_data_to_pc(void)
{
    
    uint16_t addr_counter;
    uint8_t eeprom_data;
    
    for( addr_counter = 0 ; addr_counter<= 0x7FFF ; addr_counter ++ )    // process all 32kByte
    {
        
        eeprom_data = read_external_eeprom( addr_counter ); // read the data from the EERPROM
        
        send_byte_via_sw_uart2( eeprom_data );              // write the data to UART2 (TXD2)
        
    }

    _delay_ms(1000);                                        // 1 sec pause to indicate to PC that 
                                                            // transmission is completed
     return 0;
                                                            
}

// ----- write one 32Byte set of data to the EEPROM at the given start address -----
void write_dataset_to_eeprom(uint16_t addr)
{
    
    uint8_t cnt;
    
    for( cnt=0 ; cnt<32 ; cnt++ )
    {
        write_external_eeprom( addr+cnt, gps_data_buffer[cnt] );
    }

}

// -------------------- handling of commands from PC on UART2 ---------------------
int command_mode_interpreter(void)
{
    
    // Defined commands from PC to tracker are:
    // x (exit)  = leave command mode, continue recording where interupted 
    // d (dump)  = dump all 32kByte of EEPROM data in a binary stream to the PC
    // e (erase) = mark all 32byte blocks as free (set first byte to 0xCC), the locks program flow
    
    uint8_t t;
    uint16_t command_byte;
    uint16_t addr_counter;
    
    _delay_ms(10);                          // wait 10ms so that first (triggering) character is completed, 
                                            // this first character will therefore not being received by UART2
                                            // as command, only the next once, but that has a clean start then
    
    // for 10 seconds, try to establish 2-way contact with PC
    for(t=0; t<10; t++)
    { 
        
        command_byte = receive_byte_via_sw_uart2();         // listen on UART2 with 1s timeout
   
        if(command_byte == 'X')                             // leave waiting loop if correct char was received
            break;
    
    }
    
    if(command_byte != 'X')                                 // if no success within 10 trials, go back to normal recording 
        return -1;
    
    // --- connection established, enter command interpetation loop ---
    do
    {
        
        send_byte_via_sw_uart2('o');                        // send 'ok' to PC to indicate connection established
        send_byte_via_sw_uart2('k');                        //   (now waiting for commands)
        send_byte_via_sw_uart2(13);
        send_byte_via_sw_uart2(10);    
        
        // after the ok, a command must be received within 10 seconds, otherwise command mode is terminated
        for(t=0; t<10; t++)
        { 
            
            command_byte = receive_byte_via_sw_uart2();     // listen on UART2 with 1s timeout
       
            if(command_byte == 'd')                         // command "dump"
            {
                LED_ON;                                     // LED continuously on during download
                send_eeprom_data_to_pc();                   // dump all 32kByte of EEPROM data in a binary stream to the PC
                LED_OFF;                                    // LED off after download
                return -5;                                  // leave command mode when done
            }
        
            if(command_byte == 'x')                         // command "exit"
            {
                return -2;                                  // leave command mode immediately                            
            }
            
            if(command_byte == 'e')                         // command "erase"
            {
                LED_ON;                                     // LED continuously on during erasing
                for( addr_counter = 0 ; addr_counter< 0x8000 ; addr_counter+=32 )    // process all 32Byte blocks
                {
                    write_external_eeprom( addr_counter, 0xCC );    // write 0xCC to everys block first byte
                }
                LED_OFF;                                    // LED off after erasing
                
                for(;;);                                    // after an erase we want the user to power off the device
                                                            // so we lock the program flow in an endless loop here
                                                            // (otherwise new records would start to fill the EEPROM
                                                            // right away, if the GPS has a fix)
                
            }
                
        } //-- end of 10s waiting loop --     
            
    }    
    while( t<10 );                                      // stay in command mode for next command, unless timeout
                                                        // was reached while waiting for a command

    return -3;

}

// --- we end up here when all EEPROM memory is used up ---
void memory_full_handler(void)
{
  
    // To indicate to the user that no further recording is possible, a distinct LED pattern is shown.
    
    uint8_t t;
    unsigned char overflow_counter;
    
    for(;;)                                         // endless loop, only way out is via erasing the memory from PC
    {
       
        overflow_counter=0;                         // reset overflow counter
        TCNT0 = 0;                                  // reset the timer/counter
        TIFR0 |= _BV(TOV0);                         // reset overflow flag of timer 0 (used for timeout)
     
        while( overflow_counter < 37 )              // loop 850ms (=37*23ms) while monitoring RXD2
        {                                      
            
            if(TIFR0 & _BV(TOV0))                   // if timer has overflow (every 23ms)
            {
                TIFR0 |= _BV(TOV0);                 // reset overflow flag
                overflow_counter++;                 // counter the overflows
            }
            
            if( UART_RXD2 == 0)                     // here we check if the PC is trying to establish a contact to 
            {                                       //   read out memory data or erase the memory content
                command_mode_interpreter();         // if so, we interupt recording and jump to command mode
            }
        
        }    
        
        for(t=0; t<3; t++ )
        {
            LED_ON;                                 // have the LEDs flashing quickly 3 times (50ms on/off)
            _delay_ms(50);
            LED_OFF;
            _delay_ms(50);
        }
    }
        
}

// --- search first free EEPROM block ---
uint16_t search_free_memory(void)
{
    uint16_t test_addr;
    uint8_t eeprom_content;
    
    for(test_addr=0; test_addr<0x8000; test_addr+=32)       // test all 32byte memory blocks
    {
        eeprom_content = read_external_eeprom(test_addr);   // read the content of the first cell of this block
        
        if( eeprom_content==0xFF || eeprom_content==0xCC )  // if content is 0xFF or 0xCC, it is ok to overwrite this block
            break;                                          // we have found the first free block => leave search loop
        
    }
    
    return test_addr;
    
}

