/*
 * Hauptprogramm
 *
 *
 * Interrupt Belegung ATmega16                      wo              warum
 *--------------------------------------------------------------------------------------------
 * INT0          external Int0                     odometers.c     linke  Rad Impulse
 * INT1          external Int1                     odometers.c     rechte Rad Impulse
 * TIMER2_COMP   Timer/Counter2 Compare Match      prog10.c        4,0277 msek  
 * TIMER2_OVF    Timer/Counter2 Overflow
 * TIMER1_CAPT   Timer/Counter1 Capture Event
 * TIMER1_COMPA  Timer/Counter1 Compare Match A    motorctrl.c     Motorsteuerung links
 * TIMER1_COMPB  Timer/Counter1 Compare Match B    motorctrl.c     Motorsteuerung rechts
 * TIMER1_OVF    Timer/Counter1 Overflow
 * TIMER0_OVF    Timer/Counter0 Overflow
 * SPI           STC Serial Transfer Complete
 * RXC           USART Rx Complete                 abc/avr_uart.c  receive Zeichen da
 * UDRE          USART Data Register Empty         abc/avr_uart.c  transmit Zeichen buffer leer
 * TXC           USART Tx Complete                 abc/avr_uart.c  transmit fertig
 * ADC           ADC Conversion Complete           analog.c        Analoge Messwerte (11 Werte)
 * EE_RDY        EEPROM Ready
 * ANA_COMP      Analog Comparator
 * TWI           Two-wire Serial Interface
 * INT2          External Interrupt Request 2
 * TIMER0 COMP   Timer/Counter0 Compare Match
 * SPM_RDY       Store Program Memory Ready
 *
 * Prozessor:  ATmega16
 * Frequenz:   15,0000 MHz
 *--------------------------------------------------------------------------
* Pin Belegung:
*
*                           +-------------------+
* LED0        (XCK/T0) PB0 -|1       XXX      40|- PA0 (ADC0)     X1 1
*                           |                   |
* LED1            (T1) PB1 -|2                39|- PA1 (ADC1)     X1 2
*                           |                   |
* LED2     (INT2/AIN0) PB2 -|3                38|- PA2 (ADC2)     X2 1
*                           |                   |
* LED3      (OC0/AIN1) PB3 -|4                37|- PA3 (ADC3)     X3 1
*                           |                   |
* LINE_EN         (SS) PB4 -|5                36|- PA4 (ADC4)     VBAT
*                           |                   |
* MOSI          (MOSI) PB5 -|6                35|- PA5 (ADC5)     LINE_L
*                           |                   |
* MISO          (MISO) PB6 -|7                34|- PA6 (ADC6)     LINE_C
*                           |                   |
* SCK            (SCK) PB7 -|8                33|- PA7 (ADC7)     LINE_R
*                           |                   |
* RESET              RESET -|9                32|- AREF
*                           |                   |
* +5V                  VCC -|10               31|- GND             GND
*                           |                   |
* GND                  GND -|11               30|- AVCC            +5V
*                           |                   |
*                    XTAL2 -|12               29|- PC7 (TOSC2)     SENS_R1
*                           |                   |
* 15 MHz             XTAL1 -|13               28|- PC6 (TOSC1)     SENS_R1
*                           |                   |
* X5 1           (RXD) PD0 -|14               27|- PC5 (TDI)       SENS_L2
*                           |                   |
* X5 2           (TXD) PD1 -|15               26|- PC4 (TDO)       SENS_L1
*                           |                   |
* ODO_L         (INT0) PD2 -|16               25|- PC3 (TMS)       X3 2
*                           |                   |
* ODO_R         (INT1) PD3 -|17               24|- PC2 (TCK)       X2 2
*                           |                   |
* PWM_R         (OC1B) PD4 -|18               23|- PC1 (SDA)       X4 2
*                           |                   |
* PWM_L         (OC1A) PD5 -|19               22|- PC0 (SCL)       X4 1
*                           |                   |
* DIR_L         (ICP1) PD6 -|20    ATmega16   21|- PD7 (OC2)       DIR_R
*                           +-------------------+
*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
* 23.12.2010 li Sender der Daten als eigener Task
* 25.12.2010 li Rückwärts- und Kurven Fahrt: Kontrolle über odometer Werte
* 05.08.2012 li Einbau von prozess.c und scheduler.c mit relativen Zeitwerten
* 07.08.2012 li Zufalls Werte für die Distanz bei Ausweichmanövern 
* 08.08.2012 li neue analog.c, ohne prozess.c, batterie wert immer senden               
*/

#include <stdlib.h>
#include <avr/pgmspace.h>
#include "config.h"
#include "../incl002/iodefs.h"
#include "../incl002/feelers.h"
#include "../incl002/leds.h"
#include "../incl002/motorctrl.h"    // Motor control
#include "../incl002/odometers.h"    // Radsensor ticks
#include "../incl002/analog.h"       // Analoge Signale
#include "../incl002/line.h"         // linien Sensor Werte
#include "../../abc/basis.h"         // UART
#include "../../abc/avr_uart.h"      // UART
#include "../../abc/scheduler.h"     // time table scheduler

#define LMot  (1)
#define RMot  (0)
#define VOR   (0)
#define RET   (1)

unsigned char sflag = 0;
// fine TX_POWER 0x01          // .... ...1
//                             // .... ..1.
//                             // .... .1..
//                             // .... 1...
//                             // ...1 ....
//                             // ..1. ....
//                             // .1.. ....
#define T_ERROR  0x80          // 1... ....


char msg3[]  PROGMEM = "3no Cal: Motor on";
char msg31[] PROGMEM = "3Cal: first white";
char msg32[] PROGMEM = "3Cal: w or b miss";
char msg[20];

unsigned char line_flag = 0;

char receive_buffer[ RBUF_SIZE ];    // da per IR Echo die gesendeten Daten auch hier landen
                                     // sollte dieser so gross sein wie die Sendedaten    

unsigned char receive_ind = 0;       // receive Index
int uarterr =0;                      // error count
int uart_ok =0;                      // erkannte Nachrichten

extern S_UART    volatile us;        // siehe ../../abc/avr_uart.h

#define CLSW_LKVH  0x01              // bereinigter Switch links,  kraft von hinten
#define CLSW_LKVV  0x02              // bereinigter Switch links,  kraft von vorn
#define CLSW_RKVH  0x04              // bereinigter Switch rechts, kraft von hinten
#define CLSW_RKVV  0x08              // bereinigter Switch rechts, kraft von vorn

unsigned char distanz = 0;            // weg zwischen 2 Punkten, zufällig berechnet zwischen 40 und 103
unsigned char wegstrecke = 0;         // gefahrener weg 
unsigned char old_l = 0;
unsigned char old_r = 0;
unsigned char feeler_status;

#define  RUN_FLAG_ON     BIT0         // .... ...1  ON /OFF
#define  RUN_FLAG_BANDR  BIT1         // .... ..1.  back and turn right                   
#define  RUN_FLAG_BANDL  BIT2         // .... .1..  back and turn left
unsigned char run_flag = 0;           // 

unsigned int  motor_speed = 170;      // speed default
unsigned int  current_speed = 0;      // 

unsigned char volatile tim2tick = 0;

unsigned char req_par = 0;
                                       
                                        // ************************ Wege Programm *********************
                                        //  Wenn Start dann Vor
                                        //  VOR: wenn Behinderung (switch) stopp und 12cm zurück
                                        //  Zurück end: Kurve nach Rechts oder Links je nach Switch
                                        //  Kurve beendet nach 12cm aussen Rad, dann weiter geradeaus                                                        
                                        //  ************************************************************                                       
void Drive_Control_0( unsigned char );  // warten auf Start
void Drive_Control_1( unsigned char );  // vorwärts bis any switch
void Drive_Control_2( unsigned char );  // rückwärts 12 cm
void Drive_Control_3( unsigned char );  // Kurve nach rechts vorwärts 12cm, dann wieder vorwärts
void Drive_Control_4( unsigned char );  // Kurve nach links vorwärts  12cm, dann wieder vorwärts

void LED_Control( unsigned char );
void Remote_Control( unsigned char);    // sende angeforderte werte
void Power_Control( unsigned char);     // sende batterie wert
void decode_message(void);
void timer2_init(void);

void odometrie_ground( void );
void compute_speed( void );
void remote_stopp( void );
void compute_weg( unsigned char);
void sendmsg3( prog_char *pgmaddr );


/*****************************************************************
** TIMER2 Init, Unterbrechung je 4,027733 msek
** ca. 60000 2 Byte Befehle in dieser Zeit 
** 
*/
void timer2_init(void)
{
   TCCR2 = 0x8F;                 // prescaler = 1024 setzen, CTC Modus und FOC2
   TCNT2 = 0;                    // reset
   OCR2  = 58;                   // compare match 59 * 1024 = 60416 Takte = 4,0277 msek
   TIMSK |= (1 << OCIE2);        // enable compare match interrupt
}



/*****************************************************************
** TIM2 COMPARE Interupt, je 4msek
** startet ueber prozess queue time2_over 
*/
ISR ( TIMER2_COMP_vect )
{   
   tim2tick++;                   // count 4msek ticks
}



/*****************************************************************
** Main
**
** in der Time_table sind 4 Loops:
**
** 1: feeler abfragen über Feelers_Init
** 2: Drive_Control_x
** 3: LED_Control
** 4: Remote_Control             
*/
int main(void)
{
   int ic;
   unsigned char cda ;
   
   Scheduler_Init();               // Time Table scheduler init
   //prozess_init();                 // prozess queue init
   LEDS_INIT();                    // LED Ports aktivieren
   Odometers_Init();               // Messung der Rad Impulse
   MotorCtrl_Init();               // Motoren Ports und PWM init.
   timer2_init();                  // 4ms Timer aktivieren
   uart_init();                    // remote Senden/Empfang aktivieren
   analog_init();                  // analog Wandler starten, Loop ohne Time_table
   line_init();                    // linien Init 
   
   sei();                          // enable Int
   
   Feelers_Init( NULL );           // Fuehlerabtastung aktivieren
  
   Insert_TT_Entry( T2_TIME_12 , Drive_Control_0, 0 );     // starte Drive loop
   Insert_TT_Entry( T2_TIME_52 , LED_Control, 0 );         // starte LED loop
   Insert_TT_Entry( T2_TIME_1000, Remote_Control, 0 );     // starte remote Sende loop
      
   
   // **************************** loop ***************************** 
   for (;;)
   {
      if ( tim2tick )              // 4msek vorbei ? 
      {
         schedule( tim2tick );     // Schedule Time 
         tim2tick=0;               // clear ticks 
      }      

      // ++++++++++++++++ UART Abfrage auf Zeichen empfangen ++++++
      ic = uart_getc();            // abfrage   
      if ( ic != -1 )              // ungleich -1: Zeichen da
      {
         cda = (unsigned char)ic & 0xFF ;

         if ( us.rx_error )        // transfer fehler ??
         {
            us.rx_error = 0;
         }
         else
         {
            if ( cda == 0x02 )     // start of txt = reset error
            {
               receive_ind = 0;    // receive Index auf 0
               sflag &= ~T_ERROR;
            }
            
            if ( receive_ind >= RBUF_SIZE )
            {
               sflag |= T_ERROR;
               uarterr++;
            }
            else
               receive_buffer[ receive_ind++ ] = cda;

            if ( cda == 0x03 && !(sflag & T_ERROR) )
            {
               decode_message();       // <<<<<<<<<<<<<<<<<<<<<<
            }   
         }
      }
   }   
   return(0);
}



/*****************************************************************************
**  Message von nibobee IR remote control decodieren
**
** 0123
**  60   = 
**  61   = req Odometrie
**  62   = req Sensoren
**  63   = req CalibrateWhite
**  64   = req CalibrateBlack
**  65   = req Write Calibrate
**  66   =
**  67   = reset 
**  68   =
**  69   =
**
**  50   = motor_speed -= 26       von 40 bis 300  in 10 Schritten
**  51   = motor_speed += 26
**  52   = 
**  53   = 
**  54   = 
**  55   = 
**  56   = 
**  57   = 
**  58   =
**  59   = toggle Start / stop
*/
void decode_message(void)
{
   unsigned char p;   
   char receive_copy[ 8 ];              // nur max. 8 Bytes relevant
   
   for ( p=0; p < 8; p++ )
      receive_copy[ p ] = receive_buffer[ p ] ; 

   switch (  receive_copy[ 1 ] )          // 2. Zeichen
   {
     case '6':                            // cmd
      p = berechne_PZ( receive_copy+1, 2 ) + 0x30 ;   // Pruefzeichen
      if ( p == receive_copy[3] )                     // check transfer
      {
         uart_ok++;                                   // Nachricht ok
         req_par = receive_copy[2];
         Insert_TT_Entry( T2_TIME_4, Remote_Control, req_par );   // update scheduler entry         
      }
      else
         uarterr++;
      break;
      
     case '5':                            // cmd
      p = berechne_PZ( receive_copy+1, 2 ) + 0x30 ;   // Pruefzeichen
      if ( p == receive_copy[3] )                     // check transfer
      { 
         uart_ok++; 
         
         switch ( receive_copy[2] )     
         {
           case '0':                      // 50 
            if ( motor_speed >= 66 )
               motor_speed -= 26;
            break;               
            
           case '1':                      // 51 
            if ( motor_speed <= 274 )
               motor_speed += 26;           
            break;
            
           case '2':                      // 52 
            run_flag ^= RUN_FLAG_ON;           
            run_flag |= RUN_FLAG_BANDR;           
            break;

           case '3':                      // 53 
            run_flag ^= RUN_FLAG_ON;
            run_flag |= RUN_FLAG_BANDL;            
            break;            
           
           case '9':                      // 59 toggle start Stop
            run_flag ^= RUN_FLAG_ON;       
            break;   
         }       
      }
      else
         uarterr++;
      break;
                                               
     case '1':                            // eigene msg
     case '2':                            // eigene msg
     case '4':                            // eigene msg
     case '9':                            // eigene msg
      break;
      
     default:
      uarterr++;
      break;
   }
}



/*****************************************************************
** Kontrolle der Fahrzeug (nibobee Roboter) Bewegung
** drive_status:   0  = Stillstand, warten auf any-Switch oder IR-Befehl
*/
void Drive_Control_0( unsigned char drive_status )
{
   unsigned char ts;

   ts = Feelers_Read_KeysState() >> 4;              // nur die Fuehlerbits
   current_speed = 0;      
   
   if ( (run_flag & RUN_FLAG_ON) || ts )
   {
      run_flag |= RUN_FLAG_ON;                                 // wegen ts und led control   
      if ( !(run_flag & (RUN_FLAG_BANDR + RUN_FLAG_BANDL)))   
         Insert_TT_Entry( T2_TIME_250, Drive_Control_1, 1 );   // warte 250ms, dann motor start
      else 
      {         
         Insert_TT_Entry( T2_TIME_250, Drive_Control_2, 1 );   // warte 250ms, dann motor start
      }         
   }
   else
   {
      Insert_TT_Entry( T2_TIME_12, Drive_Control_0, 0 );   
   }
}



/*****************************************************************
** Kontrolle der Fahrzeug (nibobee Roboter) Bewegung
** drive_status:   1  = Vorwärts fahren
**                      alle 12 msek check feeler  
**
*/
void Drive_Control_1( unsigned char dpar )
{
   unsigned char ts;
  
   ts = Feelers_Read_KeysState() >> 4;       // nur die Fuehlerbits
   
   if ( !(run_flag & RUN_FLAG_ON) )          // stop cmd von IR-Control ??
   {
      remote_stopp();
   }
   else
   {      
      if ( ts == 0 )                         // fuehler ??
      {
         compute_speed();
         MotorCtrl_SetSpeedDir( LMot, current_speed, VOR );
         MotorCtrl_SetSpeedDir( RMot, current_speed, VOR );
         Insert_TT_Entry( T2_TIME_12, Drive_Control_1, 0 );   // weiter gerade aus
         feeler_status = ts;
      }
      else
      {
         feeler_status = ts;                 // merke Fuehler switch
         MotorCtrl_Stopp();                  // sofort Motor stoppen
         Insert_TT_Entry( T2_TIME_250, Drive_Control_2, 1 );    // Warten 250ms, dann zurück fahren
      }
   }
}



/*****************************************************************
** Kontrolle der Fahrzeug (nibobee Roboter) Bewegung
** drive_status:   2  = Zurück fahren
**
** 25.10.2010 li Zurück: Kontrolle über Odometrie Werte 
** 07.08.2012 li wenn dpar=1 dann reset 
** 07.08.2012 li Zufallswert für die Distanz von 12-20cm
*/
void Drive_Control_2( unsigned char dpar )
{   
   if ( !(run_flag & RUN_FLAG_ON) )          // stop cmd von IR-Control ??
   {
      remote_stopp();
   }
   else
   {
      if ( dpar )
      {       
         odometrie_ground();                 // distanz bestimmen
      }      
      compute_weg( 0 );
      
      if ( wegstrecke > distanz )            // ziel erreicht ?
      {
         MotorCtrl_Stopp();                  // Stopp         
                  
         if ( !(run_flag & (RUN_FLAG_BANDR + RUN_FLAG_BANDL)))        //  kein 
         {
            if ( feeler_status & (CLSW_LKVV + CLSW_LKVH ))            //  any switch left ?
               Insert_TT_Entry( T2_TIME_250, Drive_Control_3, 1 );    //  nach 250 msec Kurve nach rechts
            else
               Insert_TT_Entry( T2_TIME_250, Drive_Control_4, 1 );    //  nach 250 msec Kurve nach links
         }
         else
         {
            if ( run_flag & RUN_FLAG_BANDR )                          //  rechts ??
               Insert_TT_Entry( T2_TIME_250, Drive_Control_3, 1 );    //  nach 250 msec Kurve nach rechts
            else
               Insert_TT_Entry( T2_TIME_250, Drive_Control_4, 1 );    //  nach 250 msec Kurve nach links
            run_flag &= ~(RUN_FLAG_BANDR + RUN_FLAG_BANDL);           // reset flag     
         }          
      }
      else
      {         
         compute_speed();
         MotorCtrl_SetSpeedDir( LMot, current_speed, RET );
         MotorCtrl_SetSpeedDir( RMot, current_speed, RET );
         Insert_TT_Entry( T2_TIME_12, Drive_Control_2, 0 ); // weiter zurück
      }
   }
}



/*****************************************************************
** Kontrolle der Fahrzeug (nibobee Roboter) Bewegung
** drive_status:   3  = Kurve fahren
**
** 25.10.2010 li Kurve nach rechts: Kontrolle über Odometrie Werte 
** 07.08.2012 li dpar =1, reset Odometrie Werte 
*/
void Drive_Control_3( unsigned char dpar )
{
   if ( !(run_flag & RUN_FLAG_ON) )         // stop cmd von IR-Control ??
   {
      remote_stopp();
   }
   else
   {      
      if ( dpar )
      {
         odometrie_ground();              // distanz bestimmen
      }
      compute_weg( 0 );  
      if ( wegstrecke > distanz )         //  ziel erreicht ?
      {         
         Insert_TT_Entry( T2_TIME_12, Drive_Control_1, 1 );  //  wieder gerade aus         
      }
      else
      { 
         compute_speed();
         MotorCtrl_SetSpeedDir( LMot, current_speed, VOR );    // links mehr als rechts = Kurve nach rechts
         MotorCtrl_SetSpeedDir( RMot, current_speed/4, VOR ); 
         Insert_TT_Entry( T2_TIME_12, Drive_Control_3, 0 );
      }
   }   
}



/*****************************************************************
** Kontrolle der Fahrzeug (nibobee Roboter) Bewegung
** drive_status:   3  = Kurve fahren
**
** 25.10.2010 li Kurve nach links: Kontrolle über Odometrie Werte 
*/
void Drive_Control_4( unsigned char dpar )
{   
   if ( !(run_flag & RUN_FLAG_ON) )          // stop cmd von IR-Control ??
   {
      remote_stopp();
   }
   else
   {
      if ( dpar )
      {
         odometrie_ground();                // distanz bestimmen
      }      
      compute_weg( 1 );
      if ( wegstrecke > distanz )           //  ziel erreicht ?
      {
         Insert_TT_Entry( T2_TIME_12, Drive_Control_1, 1 );    //  wieder gerade aus         
      }
      else
      {         
         compute_speed();
         MotorCtrl_SetSpeedDir( LMot, current_speed/4, VOR ); // Kurve nach links
         MotorCtrl_SetSpeedDir( RMot, current_speed, VOR );    
         Insert_TT_Entry( T2_TIME_12, Drive_Control_4, 0 );
      }
   }   
}



/*****************************************************************
** modifizierte lineare spreed ramp funktion 
** SPEED_RAMP_UP aus config.h
*/
void compute_speed( void )
{
   unsigned char delta = SPEED_RAMP_UP;
   
   if ( current_speed > (motor_speed / 2) )
      delta = SPEED_RAMP_UP / 2;

   if ( (motor_speed - delta) > current_speed )
      current_speed += delta;
   else
      current_speed = motor_speed;
}



/*****************************************************************
** Odometrie werte für die Weg-Berechnung vorbereiten
** 07.08.2012 distanz aus den odometrie werten berechnen
**            6 bits = 0-63 + 40 = 40 bis 103 = 12cm bis 30cm 
*/
void odometrie_ground( void )
{
   old_l = Odometers_Get_L_Val();      // current Wert Links
   old_r = Odometers_Get_R_Val();      // current Wert Links 
   current_speed = 0;
   wegstrecke = 0;
                                       // random wert distanz
   distanz = old_l & 0x07;             // 3 Bit links 
   distanz <<= 3;               
   distanz |= old_r & 0x07;            // 3 Bit rechts 
   distanz += 40;                      // + minimum
}



/*****************************************************************
** Weg berechnen
*/
void compute_weg( unsigned char rechts )
{
   unsigned char v, diff;
   
   if ( rechts )                         // use Odometrie rechts ?
   {
      v = Odometers_Get_R_Val();         // current Wert rechts 
      diff = v - old_r;                  // differenz
      old_r = v;                         // merke alten Wert
   }
   else
   {
      v = Odometers_Get_L_Val();         // current Wert links 
      diff = v - old_l;                  // differenz                   
      old_l = v;                         // merke alten Wert
   }   
   wegstrecke += diff;                    // summe  
}



/*****************************************************************
** 07.08.2012
*/
void remote_stopp( void )
{
   MotorCtrl_Stopp();                     // ja, sofort Motor stoppen
   Insert_TT_Entry( T2_TIME_250, Drive_Control_0, 0 );    // Warten 250ms, dann Level = Warten
}



/*****************************************************************
** LED_Control Parameter: 0, 2, 4, 6: LEDs ausschalten
**                       1 = links      gelb einschalten
**                       3 = halblinks  rot  einschalten
**                       5 = halbrechts rot  einschalten
**                       7 = rechts     gelb einschalten
*/
void LED_Control( unsigned char level )
{
   unsigned char new_level, pos;
      
   switch( level )           // LED entsprechend schalten 
   {
     case 0:
     case 2:
     case 4:
     case 6:
      LEDS_OFF(LEDS_ALL);    // alle ausschalten
      break;

     case 1:
      LEDS_ON( LEDS_L_YE );  // links gelb
      break;

     case 3:
      LEDS_ON( LEDS_L_RD );  // halb links rot
      break;

     case 5:
      LEDS_ON( LEDS_R_RD );  // halbrechts rot
      break;

     case 7:
      LEDS_ON( LEDS_R_YE );  // rechts gelb
      break;
   }  
   
   // *******************************   neuer Level einstellen *********     
   
   if ( !(run_flag & RUN_FLAG_ON) )       // LEDs im Uhrzeiger Sinn kreisen
   {
      pos = level & 0x07 ;
      new_level = (pos + 1) & 0x07;       // im Uhrzeigersinn      
   }
   else
   {
      if ( feeler_status == 0 )             // Vorwärts, beide rote LED blinken
      {
         pos = level & 0x07 ;
         if ( pos >= 2 && pos <= 4 )
            new_level = (pos + 1) & 0x07;         // nur rot links oder rechts
         else
           new_level = 2;         
      }
      else
      {
         if ( feeler_status & (CLSW_LKVV + CLSW_LKVH ))   // any switch left ?
         {
            pos = level & 0x07 ;
            if ( pos == 6 )
               new_level = (pos + 1) & 0x07;              // gelb rechts
            else
              new_level = 6;            
         }
         else
         {
            pos = level & 0x07 ;
            if ( pos == 1 )
               new_level = (pos + 1) & 0x07;             // gelb links
            else
              new_level = 1;           
         }
      }
   }
   Insert_TT_Entry( T2_TIME_52, LED_Control, new_level );   // 52 msek
}



/*****************************************************************
**  s1wwwwpe                        Analog wert der Spannung
*/
void Power_Control( unsigned char req_Remote )
{
   char msg[9];
   unsigned char p;
 
   msg[0] = 0x02;               // start of txt
   msg[1] = '1';
   edit99( analog_getValue( ANALOG_VOLT ), msg+2, 4 );
   p = berechne_PZ( msg+1, 5 ) + 0x30 ;    // Pruefzeichen
   msg[6] = p;
   msg[7] = 0x03;               // end of txt
   msg[8] = 0x00;
   usart_write_str( msg );       // senden
}      



/*****************************************************************
**  Transfer Daten, wird von Remote_Control in den Scheduler eingestellt
**  s = Start of Text,  e = End of Text, p = Prüfzeichen
** 
**  s2lllrrrpe                      Odometer Werte links und rechts
**  s4llllccccrrrrpe                line sensor Werte
**  s3textee                        Fehler texte
**  s9mmmmmMMMMMpe                  oder uart_ok uarterr 
*/
void Remote_Control( unsigned char req_Remote )
{
   unsigned char p;
   
   switch ( req_Remote )
   {
     case '1':
      msg[0] = 0x02;               // start of txt
      msg[1] = '2';
      edit99( Odometers_Get_L_Val(), msg+2, 3 );
      edit99( Odometers_Get_R_Val(), msg+5, 3 );
      p = berechne_PZ( msg+1, 7 ) + 0x30 ;    // Pruefzeichen
      msg[8] = p;
      msg[9] = 0x03;               // end of txt
      msg[10] = 0x00;
      usart_write_str( msg );       // senden
      break;
      
     case '2':
      msg[0] = 0x02;               // start of txt
      msg[1] = '4';            
      edit99( line_get( LINE_L ), msg+2, 4 );
      edit99( line_get( LINE_C ), msg+6, 4 );
      edit99( line_get( LINE_R ), msg+10, 4 );
      p = berechne_PZ( msg+1, 13 ) + 0x30 ;    // Pruefzeichen
      msg[14] = p;
      msg[15] = 0x03;               // end of txt
      msg[16] = 0x00;
      usart_write_str( msg );       // senden
      break;
      
     case '3':
      req_Remote = '7';             // nur einmal
      if ( run_flag & RUN_FLAG_ON )
         sendmsg3( msg3 );          // keine Cal wenn motor on
      else
      {
         line_calibrateWhite();
         line_flag = 0x01;
      }
      break;      
   
     case '4':
      req_Remote = '7';               // nur einmal
      if (run_flag & RUN_FLAG_ON)
          sendmsg3( msg3 );         // keine Cal wenn motor on
      else
      {
         if ( line_flag & 0x01 )
         {
            line_calibrateBlack();
            line_flag = 0x03;
         }
         else
         {
            sendmsg3( msg31 );      // erst weiss calibrieren 
         }  
      }
      break;
   
     case '5':
      req_Remote = '7';            // nur einmal
      if (run_flag & RUN_FLAG_ON)
         sendmsg3( msg3 );         // keine Cal wenn motor on
      else
      {
         if ( line_flag == 0x03 )
         {
            line_writePersistent();  // Save Daten
            line_flag = 0; 
         }
         else
         {
            sendmsg3( msg32 );      // black and white nicht da
         }  
      }
      break;
     
     case '7':
      msg[0] = 0x02;               // start of txt
      msg[1] = '9';
      edit99( uart_ok, msg+2, 5 );
      edit99( uarterr, msg+7, 5 );
      p = berechne_PZ( msg+1, 11 ) + 0x30 ;    // Pruefzeichen
      msg[12] = p;
      msg[13] = 0x03;              // end of txt
      msg[14] = 0x00;
      usart_write_str( msg );      // senden             
      break;
   }   
   Insert_TT_Entry( T2_TIME_2000, Remote_Control, req_Remote );   // je angeforde Daten erneut senden 
   Insert_TT_Entry( T2_TIME_1000, Power_Control, 0 );             // Daten versetzt senden  
}



/*****************************************************************************
**  send pgm messages
*/
void sendmsg3( prog_char *pgmaddr )
{
   unsigned char i;
   char *s;
      
   msg[0] = 0x02;               // start of txt
   s = msg+1;
   for ( i=0; i < 20 ; i++)
   {
      *s = pgm_read_byte( pgmaddr++ );
      if ( *s == '\0' )
      {
         *s = 0x03 ;   // end of txt
         s++;
         *s = 0x00 ;   // ende
         break;
      }
      else
         s++;      
   }   
   usart_write_str( msg );      // senden
}