Wheelie II C

Für das Wheelie II habe ich den Code in C geschrieben. Neu ist der Kalmanfilter zur Berechnung des Plattformwinkels. Nach und nach werden die 6 möglichen Parameter eingebaut, um bei Probefahrten schnell Anpassungen über die Taster am Display durchführen zu können.

/*************************************************
*   _ _ _ _           _ _
*  | | | | |_ ___ ___| |_|___
*  | | | |   | -_| -_| | | -_|
*  |_____|_|_|___|___|_|_|___|
*
**************************************************/


#ifndef F_CPU
#define F_CPU 16000000
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <avr/eeprom.h>
#include "fifo.h"
#include "adc.h"
#include "ars.h"
#include "uart.h"
#include "i2cmaster.h"

//ADC Inputs
#define Ad_kanal_batt   0
#define Ad_kanal_zgyro  1
#define Ad_kanal_adxl   3
#define Ad_kanal_gyro   5
#define Ad_kanal_fuss   7
#define Ad_kanal_rocker 6
#define Ad_kanal_roll_adxl      2
#define Ad_kanal_roll_gyro      4

//maximum Power, limit to 20 for first Tests, then you don't destroy your furniture
#define max_PWM        180 //   180
//limit the I-Part of the PID-Control          
#define Drivesumlimit   39000
//Calculate Wheel_const = circumference of the wheel in cm * 93,75 (f.e. 126 cm * 93,75 = 11812,5) for correct speed  
#define Wheel_const     11812

//Status of the Wheelie
#define m_standby               1
#define m_run                   2
#define m_down                  3 //power down the wheels

//footswitch is pressed under this level
#define Sw_down                 50

int drive_r = 0;
int drive_l = 0;

signed long     Anglecorrection=0;
float                   Angle_Rate=0;
float                   Roll_Angle_Rate=0;
float                   Balance_moment=0;
float               Drive_sum=0;
int                 Drivespeed=0;

int Rockersq=0;
int Rockerold=0;
int Rocker_zero=0;
int Rocker_diff=0;

int Speed_sum=0;
int Rocker_sensivity=0;
int Rocker_position=0;
int Speed_error=0;
int Speed_diff=0;
int Speed_correction=0;
int Steeringsignal=0;

//the I²C-Values are stored in this Array (Speed, Current, and Direction)
uint8_t Speed_array[9]={ 0,0,0,0,0,0,0,0,0} ;
uint16_t* Speed_array16 = (uint16_t*)Speed_array;
#define Speed_left Speed_array16[0]
#define Speed_right Speed_array16[1]
#define Curr_left Speed_array16[2]
#define Curr_right Speed_array16[3]

uint8_t Out_array[44]={ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} ;
int16_t* Out_array16 = (int16_t*)Out_array;

int Speed_left_out;
int Speed_right_out;
int Direction_left;
int Direction_right;
uint8_t cw_r;
uint8_t cw_l;

//This 6 values can controlled from the displayunit to improove PID and steering
int  Parameter1=15;             //Steering depends on speed                             higher Value, lower Steeringsignal
uint16_t P1 = 0x10;
int  Parameter2=5;              //Speederror from the Geartooths 27             higher Value, lower Correction
uint16_t P2 = 0x20;
int  Parameter3=210;
uint16_t P3 = 0x30;
int  Parameter4=10;    //Gain P-Part
uint16_t P4 = 0x40;
int  Parameter5=15;    //Gain I-Part
uint16_t P5 = 0x50;
int  Parameter6=1;   //Gain D-Part                                             
uint16_t P6 = 0x60;

uint8_t Mmode = m_standby;
uint8_t Errno = 0;
uint8_t Zaehler = 0;
uint16_t spl = 0;

uint16_t Ad_gyro = 0;
uint16_t Ad_adxl = 0;
uint16_t Ad_batt = 0;
uint16_t Ad_adxl_roll = 0;
uint16_t Ad_gyro_roll = 0;
uint16_t Ad_rocker = 0;
uint16_t Ad_z = 0;
uint16_t Z_zero = 0;
int Z_error = 0;

long Adxl_zero = 0;
long Roll_adxl_zero = 0;
long Gyro_zero = 0;
long Roll_gyro_zero = 0;
uint16_t Ad_swi = 0;

char Text[40];
char Inputstring[40];
uint8_t StringCounter = 0;
float roll_adxl_sensor = 0;
float adxl_sensor = 0;
float tilt_angle = 0.0;
float roll_angle = 0.0;

char s[20];
char t[20];
float dt;
struct Gyro1DKalman filter;
struct Gyro1DKalman filterX;

int x_error;

void Init(void);
void startup(void);
void set_pwm(void);
void uart_puts (const char *s);
int  main(void);
void algo(void);
void Err_value(void);
void Steering(void);
void Process(void);
void Gear(void);
void Parser(void);
void saveData(void);

/******************************************************************
*    Init:
*
*       set the Registers
******************************************************************/

void Init(void)
{
        DDRA    =       0B00000000;                             // PA0..PA7: ADC                                                                                // PA7 unbelegt
        PORTA   =       0;                                              // no Pull-ups

        DDRB    =       0B00011100;                                                                                                    
        PORTB   =       0B11100011;                    

        DDRC    =       0B00001100;                                                            
        PORTC   =       0B11111111;

        DDRD    =       0B11111010;                                                                                                    
        PORTD   =       0B00000101;            

        TCCR0   |=      (1<<WGM01);

                                                               
        OCR0    =       156;                                    //      10ms Period
        TCCR1A  =       0xb1;                                   //
        TCCR1B  =       0x01;                                   //     
        OCR1AL = 255;
        OCR1BL = 0;
        Adc_init();
        int i = 0;
        for(i = 0;i<10;i++)
        {
                Ad_swi += ReadChannel(Ad_kanal_fuss);
                _delay_ms(1);
        }
        Ad_swi /= 10;
        if (Ad_swi < 100)
        {
                Errno = 4;
                uart_puts ("2:11=14");
                //Err_value();
        }
        i2c_init();
   eeprom_read_block(&Parameter1,(uint8_t *)P1,sizeof(int));
   if (Parameter1 == -1)        //no Data in EEPROM
   {
                Parameter1 = 14;        // write the Standardvalues to EEPROM
                saveData();
   }
   else
   {
           eeprom_read_block(&Parameter2,(uint8_t *)P2,sizeof(int));
           eeprom_read_block(&Parameter3,(uint8_t *)P3,sizeof(int));
           //eeprom_read_block(&Parameter4,(uint8_t *)P4,sizeof(int));
           //eeprom_read_block(&Parameter5,(uint8_t *)P5,sizeof(int));
           //eeprom_read_block(&Parameter6,(uint8_t *)P6,sizeof(int));
   }
}
/*********************************************************************
*    startup:
*
*       calculate the zeropoints from the ADCs
*********************************************************************/

void startup(void)
{
   int i = 0;
   /*
   Ad_rocker = 0;
   Rocker_zero = 0;
   for (i = 0; i<10; i++)
   {                                      
      Rocker_zero += ReadChannel(Ad_kanal_rocker);
      _delay_ms(3);
   }
   Rocker_zero /= 10;
   */

   Rocker_zero = 434; //the rocker is exact vertical to ground

   Ad_gyro = 0;
   Gyro_zero = 0;
   for (i = 0;i < 100;i++)
   {
      Gyro_zero += ReadChannel(Ad_kanal_gyro);
      _delay_ms(3);
   }
   Gyro_zero /=  i;

   Ad_adxl = 0;
   Adxl_zero = 0;
   for (i = 0;i < 100;i++)
   {                                    
      Adxl_zero += ReadChannel(Ad_kanal_adxl);
      _delay_ms(3);
   }
   Adxl_zero /= i;

   Ad_z = 0;
   Z_zero = 0;
   for (i = 0;i < 100;i++)
   {                                    
      Z_zero += ReadChannel(Ad_kanal_zgyro);
      _delay_ms(3);
   }
   Z_zero /= i;


   /*Ad_gyro_roll = 0;
   Roll_gyro_zero = 0;
   for (i = 0;i < 100;i++)
   {
      Roll_gyro_zero += (1024 - ReadChannel(Ad_kanal_roll_gyro));
      _delay_ms(3);
   }
   Roll_gyro_zero /=  i;
   */

   Roll_gyro_zero = 550; //the platform is exact horizontal to ground
/*
   Ad_adxl_roll = 0;
   Roll_adxl_zero = 0;
   for (i = 0;i < 100;i++)
   {                                    
      Roll_adxl_zero += ReadChannel(Ad_kanal_roll_adxl);
      _delay_ms(3);
   }
   Roll_adxl_zero /= i;
   */

   Roll_adxl_zero = 507; //the platform is exact horizontal to ground
}
/************************************************************************
*    main:
*
*       Mainloop
************************************************************************/

int main(void)
{
       
        _delay_ms(1000);
        Init();
        startup();
        PORTD &= ~(1 << PD3); //turn on 15V
        uart_init();
        init_Gyro1DKalman(&filter, 0.0001, 0.0003, 0.3);
        init_Gyro1DKalman(&filterX, 0.0001, 0.0003, 0.3);
        dt = 0.0157;   // time passed in s since last call
        sei();

        while (1)
        {
        if ( Mmode != m_standby){
                  sprintf( Text, "2:1=%i\r\n", (int)tilt_angle);
                  uart_puts (Text);
                  _delay_ms(6);
              sprintf( Text, "2:2=%i\r\n", (int)roll_angle);
                  uart_puts (Text);
              _delay_ms(6);
             sprintf( Text, "2:3=%i\r\n", (int)Z_error);
                  uart_puts (Text);
              _delay_ms(6);
              sprintf( Text, "2:4=%i\r\n", (int)Rockersq);
                  uart_puts (Text);
              _delay_ms(6);
              sprintf( Text, "2:5=%i\r\n", (int)Speed_error);
                  uart_puts (Text);
             /* _delay_ms(6);
              sprintf( Text, "2:6=%i\r\n", (int)Gyro_zero);
                  uart_puts (Text);
              _delay_ms(6);
              sprintf( Text, "2:7=%i\r\n", (int)drive_r);
                  uart_puts (Text);
              _delay_ms(6);
              sprintf( Text, "2:8=%i\r\n", (int)drive_l);
                  uart_puts (Text);
              _delay_ms(6);
              sprintf( Text, "2:9=%i\r\n", (int)Speed_left_out);
                  uart_puts (Text);
              _delay_ms(6);
              sprintf( Text, "2:10=%i\r\n", (int)Speed_right_out);
                  uart_puts (Text);
                  _delay_ms(6);
              sprintf( Text, "2:15=%i\r\n", (int)Curr_left);
                  uart_puts (Text);
              _delay_ms(6);
              sprintf( Text, "2:16=%i\r\n", (int)Curr_right);
                  uart_puts (Text);
              _delay_ms(6);*/

          }
          else
          {

              sprintf( Text, "2:17=%i\r\n", (int)Parameter1);
                  //uart_puts (Text);
              _delay_ms(6);
              sprintf( Text, "2:18=%i\r\n", (int)Parameter2);
                  //uart_puts (Text);
              _delay_ms(6);
              sprintf( Text, "2:19=%i\r\n", (int)Parameter3);
                  //uart_puts (Text);
              _delay_ms(6);
              sprintf( Text, "2:20=%i\r\n", (int)Parameter4);
                  //uart_puts (Text);
              _delay_ms(6);
              sprintf( Text, "2:21=%i\r\n", (int)Parameter5);
                  //uart_puts (Text);
              _delay_ms(6);
              sprintf( Text, "2:22=%i\r\n", (int)Parameter6);
                  //uart_puts (Text);
                  Parser();
                  _delay_ms(6);
          }
          Process();
          sprintf( Text, "2:12=%i\r\n", (int)Ad_batt);
          //uart_puts (Text);
          _delay_ms(6);
        }
}
/***************************************************************
*    ISR:
*
*       Interrupt all 10ms
***************************************************************/

ISR(TIMER0_COMP_vect)
{
        //get all values from the ADCs
        Ad_gyro = ReadChannel(Ad_kanal_gyro);
        Ad_adxl = ReadChannel(Ad_kanal_adxl);
        Ad_z = ReadChannel(Ad_kanal_zgyro);
        Ad_adxl_roll = ReadChannel(Ad_kanal_roll_adxl);
        Ad_gyro_roll = 1024 - ReadChannel(Ad_kanal_roll_gyro);
        Ad_rocker = ReadChannel(Ad_kanal_rocker);
    Ad_swi = 1024 - ReadChannel(Ad_kanal_fuss);
    Ad_batt = ReadChannel(Ad_kanal_batt);
   
        //kill the offset and calculate the Gains, so that all values are ° or °/s
        // 3.0V/1025/0.002V for IDG300
        Angle_Rate = ((float)(Ad_gyro-Gyro_zero))* 1.4634135;
        Roll_Angle_Rate = ((float)(Ad_gyro_roll-Roll_gyro_zero))* 1.4634135;
       
        // 3.0V/1025/0.0019333V         0,348V for 180° = 1,9333mV per ° for ADXL320
        // 3.0V/1025/0.0046666V         0,840V for 180° = 4,6666mV per ° for ADXL322
        adxl_sensor = ((float)(Ad_adxl-Adxl_zero))* 0.6272;    //* 1.5139;             
        roll_adxl_sensor = ((float)(Ad_adxl_roll-Roll_adxl_zero))* 0.6272;    //* 1.5139;
       
        //the two Kalman steps: predict and update
        ars_predict(&filter, Angle_Rate, dt);    // Kalman predict
        ars_predict(&filterX, Roll_Angle_Rate, dt);    // Kalman predict  
        tilt_angle = ars_update(&filter, adxl_sensor);
        roll_angle = ars_update(&filterX, roll_adxl_sensor);// *  2.6; //Anpassung ans Lenkerpoti
       
        //Z-Axis
        Z_error = Ad_z - Z_zero ;

        algo();
        Gear();
        Steering();
        set_pwm();
}

/**************************************************************************
*    algo:
*
*       PID-Controller
***************************************************************************/

void algo(void)
{                          
        //The I-Part of the Controller
    Drive_sum += tilt_angle;                                              
    if(Drive_sum >  Drivesumlimit)Drive_sum = Drivesumlimit;    //you have to limit the I-Controller
    if(Drive_sum < -Drivesumlimit)Drive_sum = -Drivesumlimit;    //Drive_sum max = 255*150

    Drivespeed =  (int)(tilt_angle * Parameter4 + Drive_sum * Parameter5 / 10);                
}
/**************************************************************************
*    Gear:
*
*       get Speed from the Geartoothsensors
**************************************************************************/

void Gear(void)
{
        uint8_t i = 0;
    i2c_start_wait(0x41);
        for (i=0;i<8;i++)                                               // get the 8 Bytes from the I2C-Slave
        {
                Speed_array[i] = i2c_readAck();                      
    }
        Speed_array[i] = i2c_readNak();  // get the 9th Byte
        i2c_stop();
        //calculate Speed*10                     
        Speed_left_out = Wheel_const / Speed_left;                
    Speed_right_out = Wheel_const / Speed_right;
        //evaluate the Direction-byte
    if ((Speed_array[8] & 0x08) != 0) Speed_right_out = -Speed_right_out;
        if ((Speed_array[8] & 0x10) != 0) Speed_right_out = 0;                                  //no Direction detected
    if ((Speed_array[8] & 0x40) != 0) Speed_left_out = -Speed_left_out;
        if ((Speed_array[8] & 0x80) != 0) Speed_left_out = 0;                                   //no Direction detected
       
    Speed_sum -= Speed_sum / 5;
    Speed_sum += Speed_right_out + Speed_left_out;                          

    Speed_diff -= (Speed_diff / 10);
    Speed_diff += (Speed_left_out - Speed_right_out);
                                 
    Speed_correction = Speed_diff / 22; //22 ist ausprobiert, bis aufgebockt bei jedem Lenkereinschlag Null rauskommt
 }
/****************************************************************************
*    Steering:
*
*       calculate the steering
****************************************************************************/

void Steering(void)
{
   int Buf1;

//Ad_rocker = Rocker_zero;

   Rockersq = Rocker_zero - Ad_rocker;// + (int)roll_angle;  
   x_error = Rockersq;
   Speed_error = Rockersq - Speed_correction;                                                              
   Buf1 = abs(Speed_sum / 70);                              
   if (Buf1 > 20) Buf1 = 20;                        
   Rocker_sensivity = 22 - Buf1;                          
   Rocker_sensivity = Rocker_sensivity / 2;              
   if (Rockersq > 45) Rockersq = 45;
   if (Rockersq < -45) Rockersq = -45;
   Rocker_position = Rockersq;
   Rockersq = Rockersq * Rocker_sensivity;                
   Rockersq = Rockersq / 15;  

   Z_error /= 2;
   if (Speed_error > 40) Speed_error = 40;
   if (Speed_error < -40) Speed_error = -40;
   Steeringsignal = Rockersq + Speed_error + Z_error;
   drive_r = Drivespeed - Steeringsignal;
   drive_l = Drivespeed + Steeringsignal;

}
/********************************************************************************
*    Process:
*
*       get and set the Status
********************************************************************************/

void Process(void)
{
   int i;
   switch(Mmode)
   {
           case m_standby:                                          
                  drive_l = 0;
              drive_r = 0;
                  Drivespeed = 0;
              Drive_sum = 0;
                  set_pwm();
                  sprintf( Text, "2:11=Standby\r\n");
                  //uart_puts (Text);
                  Ad_swi = 1024 - ReadChannel(Ad_kanal_fuss);
                  Ad_batt = ReadChannel(Ad_kanal_batt);
                 

              if (Ad_swi > Sw_down){
                         startup();
                         TCNT0 = 0;
                         TCCR0  |=      (1<<CS00)|(1<<CS02)|(1<<WGM01); //Timer0 starten  
                         TIMSK  |=      (1<<OCIE0);                             //      enable Interrupt                                    
                 Mmode = m_run;
                         sprintf( Text, "2:11=Drive!\r\n");
                     uart_puts (Text);
      }
          break;
   case m_run:                                                
      if (Ad_swi < Sw_down) {Mmode = m_down;}
          break;
   case m_down:
             TCCR0      &=      ~(1<<CS00)|(1<<CS02)|(1<<WGM01); //Timer0 ausschalten
                 TIMSK  &=      ~(1<<OCIE0);                            //      enable Interrupt
         for(i = 0; i < 255; i++)
                 {
            if (drive_l == 0 && drive_r == 0) {goto Shutdown;}
            if (drive_l > 0) { drive_l--;}
            if (drive_l < 0) { drive_l++;}
            if (drive_r > 0) { drive_r--;}
            if (drive_r < 0) { drive_r++;}
            _delay_ms(25);                                  
            set_pwm();
         }
         Shutdown:
                 Mmode = m_standby;
                 break;
   default:                                              
      Mmode = m_down;
   }
}
/**********************************************************************************
*    set_pwm:
*
*       set the PWMs and the Directionbits
***********************************************************************************/

void set_pwm(void)
{
    int l = 0;
        int r = 0;

        if(drive_l >  max_PWM)drive_l =  max_PWM;      
    if(drive_l < -max_PWM)drive_l = -max_PWM;      
    if(drive_r >  max_PWM)drive_r =  max_PWM;      
    if(drive_r < -max_PWM)drive_r = -max_PWM;
        r = abs(drive_r);
        l = abs(drive_l);
    if(drive_r < 0)                                 // Check direction
    {                      
        PORTD |=        (1<<PD6);
                cw_r = 1;                            
    }
    else
    {
        PORTD &= ~(1<<PD6);
                cw_r = 0;                            
    }

    if(drive_l < 0)                                 // Check direction
    {                    
        PORTD |=        (1<<PD7);
                cw_l = 1;                            
    }
    else
    {
        PORTD &= ~(1<<PD7);
                cw_l = 0;                            
    }

        OCR1AL = 255 - r;
        OCR1BL = l;
}
/********************************************************************************
*    Err_value:
*
*       throw the program in an black whole
********************************************************************************/

void Err_value(void)
{
   while(1) { _delay_ms(2000);}
}
/********************************************************************************
*    Parser:
*
*       get Commands from USART
********************************************************************************/

void Parser(void)
{

char *ptr;
char delimiter[] = ":=";
uint8_t Channel;
int Wert;
int NextChar;
 
  NextChar = uart_getc_nowait();                                      
  while(NextChar != 10 && NextChar !=-1 && StringCounter < 40)
  {
    if (NextChar != '\0' && NextChar != 13) {Inputstring[StringCounter++] = (unsigned char)NextChar;}
    NextChar = uart_getc_nowait();
  }
  if (NextChar == 10)
  {
        if (Inputstring[1] != 58 || StringCounter > 39) {goto Ausgang;}
        Inputstring[StringCounter++] = '\0';
    ptr = strtok(Inputstring, delimiter);
    if(ptr != NULL)
        {
            if (atoi(ptr)==0)
                {
                        ptr = strtok(NULL, delimiter);
                    Channel = atoi(ptr);
                    ptr = strtok(NULL, delimiter);
                    Wert = atoi(ptr);
                        if (Channel == 17) Parameter1 = Wert;
                        if (Channel == 18) Parameter2 = Wert;
                        if (Channel == 19) Parameter3 = Wert;
                        if (Channel == 20) Parameter4 = Wert;
                        if (Channel == 21) Parameter5 = Wert;
                        if (Channel == 22) Parameter6 = Wert;
                        if (Channel == 16)
                        {
                                saveData();
                                sprintf( Text, "2:11=STORED!\r\n");
                        uart_puts (Text);
                                _delay_ms(500);
                                Zaehler =0;
                        }
                  sprintf( Text, "2:20=%i\r\n", (int)Parameter4);
                  uart_puts (Text);
              _delay_ms(6);
              sprintf( Text, "2:21=%i\r\n", (int)Parameter5);
                  uart_puts (Text);
              _delay_ms(6);
              sprintf( Text, "2:22=%i\r\n", (int)Parameter6);
                  uart_puts (Text);                      
                }
        }
        Ausgang:
        Inputstring[0] = '\0';
        StringCounter = 0;
  }
}
/***************************************************************************************
*   saveData:
*
*       store all Parameters in EEPROM
***************************************************************************************/

void saveData(void)
{
                eeprom_write_block(&Parameter1,(uint8_t *)P1,sizeof(int));
                eeprom_write_block(&Parameter2,(uint8_t *)P2,sizeof(int));
                eeprom_write_block(&Parameter3,(uint8_t *)P3,sizeof(int));
                eeprom_write_block(&Parameter4,(uint8_t *)P4,sizeof(int));
                eeprom_write_block(&Parameter5,(uint8_t *)P5,sizeof(int));
                eeprom_write_block(&Parameter6,(uint8_t *)P6,sizeof(int));
}

CPU-Modul

Der ATMega32 macht die Hauptarbeit, er wertet die Sensoren aus und erzeugt die PWM für die Motoren. Inzwischen ist noch ein ATMega8 hinzugekommen, der mit Geartoothsensoren die Geschwindigkeit der Räder ermittelt und über I²C an den ATMega32 weitergibt

Seit Oktober 2009 betreue ich den Quellcode des Elektor Wheelie. Dort kann auch an der Weiterentwicklung des Codes mitgearbeitet werden. Hilfe und Ideen zum Quellcode sind stehts willkommen!

'---------------------------------------------------------------------------
'  _____ _     _   _              _ _ _ _           _ _
' |   __| |___| |_| |_ ___ ___   | | | | |_ ___ ___| |_|___
' |   __| | -_| '_|  _| . |  _|  | | | |   | -_| -_| | | -_|
' |_____|_|___|_,_|_| |___|_|    |_____|_|_|___|___|_|_|___|
'
' (c)2009 Elektor International Media B.V.
'---------------------------------------------------------------------------
'Version: WOF104 - Elektor Wheelie Open Firmware 1.04
'---------------------------------------------------------------------------
'Purpose:
'  Elektor-Wheelie motor control source code
'---------------------------------------------------------------------------
'Description:
'  On power on the LEDs are flashing in the order green-yellow-red
'  If initialization fails the red LED is blinking and the Wheelie is blocked.
'  Blinking 2 times = gyro failure, 3 times = adxl failure.
'  If initialization has been successful the 3 LEDs show the battery status:
'  green = battery full, yellow = battery empty and red = battery dead.
'---------------------------------------------------------------------------'
'Older sources:
'  FW 2.6: Original Elekor firmware version 2.6 by Chris Krohne 2009
'  based on the AT3329 Project of Reinhold Pieper
'---------------------------------------------------------------------------'
'Changes Log:
'  WOF100:
'     Starting point for improvements from the community. Equivalent to beta.4.
'  WOF101:
'     foot switch: security brake before power down
'     Bug in WOF100 removed
'  WOF102:
'     foot switch: timeout inserted
'     faster Code in Algo, thanks Thomas Scherer
'     Telemetry added again
'     Speedsensors and I²C added
'     Testmode Switch
'     first Steps for steering Control
'  WOF103beta:
'     adxl and gyro * 10 to prevent rounding errors
'  WOF103:
'     Finetuning
'  WOF104:
'     Batteryvalues corrected. The resistor R10 must be changed from 68k to 100k!!!!
'     more Staus and Errorinfos for Telemetry and Display
'     Voltage to Telemetry and Display
'     Bug in Speed measuring fixed
'     Steering depends on Speed now
'     Steering is corrected by the speeddifference of the wheels
'     Wheelie II is supported now
'     First Zzaag tests
'
'---------------------------------------------------------------------------'
'ToDo:
'  Improvement of steering control (Zeroposition depends on geocentre)
'  Speed limit (6km/h-Limit to switch on and off)
'  emergency brake at a fall (analyse Switch, Tilt and Gyro)
'---------------------------------------------------------------------------'
'Contact:
'  Please post ideas, improvements and code into this thread in the Elektor forum:
'  http://www.elektor.de/forum/foren-ubersicht/foren-zu-elektor-projekten/elektorwheelie.996934.lynkx
'  Günter Gerold: tv@gerold-online.de
'---------------------------------------------------------------------------

$crystal = 16000000
$baud = 57600
$regfile = "m32def.dat"
$hwstack = 50                                               'Hardware stack
$swstack = 50                                               'Software stack
$framesize = 50                                             'Frame space

'---------------------------------------------------------------------------
'Switches
'---------------------------------------------------------------------------
'#####################
Const Test_enable = 1                                       'Turn on and off the Testmode here! (0 or 1)
'#####################
'#####################
Const I2c_enable = 0                                        'Turn the I²C-Geartoothsensors on and off here! (0 or 1)
'#####################
'#####################
Const Rocker_enable = 0                                     'Turn the X-Axis on and off here! (0 or 1)
'#####################
'#####################
Const Wheelie = 1                                           'Wheelie = 1, zzaag = 0 (0 or 1)
'#####################
'---------------------------------------------------------------------------
'I2C Part
'---------------------------------------------------------------------------
#if I2c_enable = 1
   $lib "i2c_twi.lbx"
   Config Twi = 400000
   Config Scl = Portc.5
   Config Sda = Portc.4

   Dim Speed_array(4) As Byte
   Dim Speed_left As Word At Speed_array(1) Overlay
   Dim Speed_right As Word At Speed_array(3) Overlay
   Dim Speed_left_out As Integer
   Dim Speed_right_out As Integer
   Dim Speed_sum As Integer
   Dim Speed_single As Single
   Dim Speed_diff As Integer
   Dim Speed_correction As Integer
   Dim Speed_error As Integer
   Declare Sub Gear
#endif
'---------------------------------------------------------------------------
'End I2C Part
'---------------------------------------------------------------------------
'---------------------------------------------------------------------------
'Start X-Axis Part
'---------------------------------------------------------------------------
#if Rocker_enable = 1
   Dim Ad_adxl_r As Long
   Dim Ad_gyro_r As Long
   Dim Total_adxl_gyro_r As Long
   Dim Average_gyro_r As Long
   Dim Tilt_angle_r As Long
   Dim Angle_rate_r As Long
   Dim Buf_r As Long
   Dim Adxl_zero_r As Word
   Dim Gyro_zero_r As Word
   Const Total_looptime_r = 20                              ' Looptime for filters
   Const Total_looptime10_r = 2                             ' Looptime for filters
   Declare Sub Get_rocker_angle
   Dim Test As Long
#endif
'---------------------------------------------------------------------------
'End X-Axis Part
'---------------------------------------------------------------------------
Config Serialin = Buffered , Size = 40 , Bytematch = 13
Config Serialout = Buffered , Size = 40
Echo Off

Pwm_al Alias Ocr1al                                         'Pin 19 PWM to right Motordriver
Pwm_bl Alias Ocr1bl                                         'Pin 18 PWM to left Motordriver
Led3 Alias Portb.4                                          'red LED
Led2 Alias Portb.3                                          'yellow LED
Led1 Alias Portb.2                                          'green LED

Config Watchdog = 32

Ddra = &B00000000
Ddrb = &B00011100
Portb = &B11100011
Ddrc = &B00001000
Portc = &B11111111
Ddrd = &B11110000
Portd = &B00001111

'A complete A/D-conversion uses 13 ADC clock cycles.
'Because of prescaling (divider = 128) 1 cycle takes 104 µs
Config Adc = Single , Prescaler = 128 , Reference = Off
Start Adc

'Timer0 as a 8 bit timer triggers interrupt service routine Ontimer0 every 16,32ms (on overflow).
'To speed up the interrupt intervall Timer0 is preset to 100. Now counting from 100 to 255.
'=> Calcutulation: 155 x 1024 x 62,5 ns = 10 ms
Config Timer0 = Timer , Prescale = 1024
On Timer0 Ontimer0

'Timer1 is used to generate both PWM signals (backward and forward).
'One of them is inverted.
Tccr1a = &B10110001
Tccr1b = 1
Ocr1al = 255

Dim Cw_change_a As Bit
Dim Cw_change_b As Bit
Dim Cw_old_a As Bit
Dim Cw_old_b As Bit
Dim Ad_adxl As Long
Dim Ad_gyro As Long
Dim Ad_batt As Integer
Dim Ad_swi As Integer
Dim Total_adxl_gyro As Long
Dim Average_gyro As Long
Dim Average_batt As Long
Dim Drivea As Integer
Dim Driveb As Integer
Dim Buf1 As Long
Dim Buf As Long
Dim Buf2 As Long
Dim Tilt_angle As Long
Dim Drive_a As Integer
Dim Drive_b As Integer
Dim Drivespeed As Long
Dim Steeringsignal As Long
Dim Anglecorrection As Long
Dim Angle_rate As Long
Dim Balance_moment As Long
Dim Mmode As Byte
Dim Drive_sum As Long
Dim Ad_rocker As Integer
Dim Timeout As Long
Dim Rockersq As Long
Dim Rocker_zero As Integer
Dim Adxl_zero As Long
Dim Gyro_zero As Long
Dim Errno As Byte

'************* Wheelie Hardware *****************
#if Wheelie = 1
Const Ad_kanal_fuss = 7                                     ' AD Kanal Fußschalter
Const Ad_kanal_adxl = 3
Const Ad_kanal_gyro = 5
Const Ad_kanal_batt = 0
Const Ad_kanal_rocker = 6
Const Ad_kanal_rocker_adxl = 2
Const Ad_kanal_rocker_gyro = 4
15v_off Alias Portc.3
Cw_ccw_a Alias Portd.6                                      'Pin 20 Direction right Motordriver
Cw_ccw_b Alias Portd.7                                      'Pin 21 Direction left Motordriver
'***************** Wheelie hardware Ende ************
#else
'***************** Zzaag Hardware ************
Const Ad_kanal_fuss = 4                                     ' AD Kanal Fußschalter
Const Ad_kanal_gyro = 3
Const Ad_kanal_adxl = 1
Const Ad_kanal_batt = 6
Const Ad_kanal_rocker = 5
15v_off Alias Portd.2
Config 15v_off = Output
15v_off = 1
Cw_ccw_a Alias Portd.7                                      'Pin 20 Direction right Motordriver
Cw_ccw_b Alias Portd.6                                      'Pin 21 Direction left Motordriver
#endif
'**************** Zzaag Harware Ende ***************

Const Total_looptime = 150                                  ' Looptime for filters
Const Total_looptime10 = 15                                 ' Looptime for filters

Const _run = 1
Const _standby = 0
Const _down = 3
Const Sw_down = 50                                          'Thershold for foot switch
Const Critical = 100                                        'Time from releasing the foot switch until begin to stop the Wheelie
Const Max_pwm = 180                                         'Maximum motor power
Const Mmax_pwm = -180                                       'Maximum motor power
Const Battdead = 732
Const Battok = 762

Declare Sub Get_tilt_angle
Declare Sub Set_pwm
Declare Sub Algo
Declare Sub Process
Declare Sub Checkbatt
Declare Sub Err_value
Declare Sub Init
Declare Sub Steering

'---------------------------------------------------------------------------
'Telemetry Part
'---------------------------------------------------------------------------
Declare Sub Checkserinp
Declare Sub Parser

Const Mymainchannel = 0
Const Idenditaet = "WOF104"

Dim Serinpstr As String * 63
Dim Serinpbuffer As String * 63
Dim Serinbuffarr(5) As Byte At Serinpbuffer Overlay
Dim Mnemonicbuffer As String * 4
Dim Mnemonicbufferarray(5) As Byte At Mnemonicbuffer Overlay
Dim Cr_received As Byte
Dim Strsize As Byte
Dim Mainchannel As Byte
Dim Subchannel As Word
Dim Wert As Single
Dim Frage As Bit
Dim Zeichenpos As Byte
Dim Zaehler As Byte
Dim Statustext As String * 8
Dim Doppelpunkt As String * 1
Doppelpunkt = ":"
Dim Istgleich As String * 1
Istgleich = "="
'---------------------------------------------------------------------------
'End Telemetry Part
'---------------------------------------------------------------------------
Pwm_al = 255
Pwm_bl = 0
Average_batt = Battok
Mmode = _standby

'LEDshow on start
Set Led1
Waitms 150
Set Led2
Waitms 150
Set Led3
Waitms 250
Reset Led1
Reset Led2
Reset Led3

'---------------------------------------------------------------------------
'Checking foot switch:
'  When the Wheelie is switched on the foot switch should be closed (not pressed down).
'  Otherwise the Wheelie would start. Therefore the foot switch is read 10 times
'  and the mean of this values is compared with a threshold of 100.
'  If this mean is smaller than all is okay. Otherwise an error occurs..
'---------------------------------------------------------------------------

'wait while the sensors drifting at power on.
Enable Interrupts
Gosub Init
Stop Watchdog
Waitms 200
15v_off = 0
Ad_swi = 0
For Buf = 1 To 10
  Ad_swi = Ad_swi + Getadc(ad_kanal_fuss)
  Waitms 1
Next Buf
'calculate middel'
Ad_swi = Ad_swi / 10
Waitms 2
'must be closed
If Ad_swi < 100 Then
  Errno = 4
  Print "2:11=14"
  Goto Err_value
End If

#if Test_enable = 0
   Start Watchdog
   Enable Timer0
   Statustext = Idenditaet
   '---------------------------------------------------------------------------
   'Main Loop:
   '  serial Output of Telemetry Data to PC or Display
   '---------------------------------------------------------------------------
   Do
     If Cr_received = 1 Then                                'wenn was im ser. Buffer ist abholen
         Cr_received = 0
         Input Serinpstr
         'zum Abtrennen eines oder mehrerer LF am Anfang des Strings, das noch vom Vorgänger-String im Buffer steht
         While Asc(serinpstr) = 10
            Strsize = Len(serinpstr) - 1
            Serinpstr = Right(serinpstr , Strsize)
         Wend
         'Hier ist der String empfangen und ein event. LF entfernt
         Call Checkserinp
      End If
     'Das Senden der Telemetriedaten ist noch verbesserungsfähig ;-)
      Waitms 8
      Print "2:1=" ; Str(tilt_angle)
      #if Rocker_enable = 1
         Waitms 8
         Print "2:2=" ; Str(tilt_angle_r)
      #endif
      Waitms 8
      Print "2:3=" ; Str(steeringsignal)
      Waitms 8
      Print "2:4=" ; Str(balance_moment)
      #if I2c_enable = 1
         Waitms 8
         Print "2:5=" ; Str(speed_correction)
         Waitms 8
         Print "2:6=" ; Str(speed_error)
      #endif
      Waitms 8
      Print "2:7=" ; Str(drive_a)
      Waitms 8
      Print "2:8=" ; Str(drive_b)
      #if I2c_enable = 1
         Waitms 8
         Print "2:9=" ; Str(speed_left_out)
         Waitms 8
         Print "2:10=" ; Str(speed_right_out)
      #endif
      If Zaehler > 100 Then
           Zaehler = 0
           Print "2:11=" ; Statustext
      End If
      Waitms 8
      Print "2:12=" ; Str(ad_batt)
   Loop
#else
   Do
      Waitms 5000
      Drive_a = 20
      Gosub Set_pwm
      Waitms 5000
      Drive_b = 20
      Gosub Set_pwm
      Waitms 5000
      Drive_a = 0
      Gosub Set_pwm
      Waitms 5000
      Drive_b = 0
      Gosub Set_pwm
      Waitms 5000
      Drive_a = -20
      Gosub Set_pwm
      Waitms 5000
      Drive_b = -20
      Gosub Set_pwm
      Waitms 5000
      Drive_a = 0
      Gosub Set_pwm
      Waitms 5000
      Drive_b = 0
      Gosub Set_pwm
For Buf = 1 To 1000
Ad_rocker = Getadc(ad_kanal_rocker)
Rockersq = Rocker_zero - Ad_rocker
Steeringsignal = Rockersq
If Steeringsignal > 2 Then
Drive_a = 30
Drive_b = 0
Gosub Set_pwm
End If
If Steeringsignal < -2 Then
Drive_b = 30
Drive_a = 0
Gosub Set_pwm
End If
Waitms 10
Next Buf
Drive_a = 0
Drive_b = 0
Gosub Set_pwm
   Loop
#endif
'---------------------------------------------------------------------------
'Ontimer0:
'  This service routine contains control logic and start every 10ms.
'  Exact time: 1/16 MHz * 1024 * (255-100) = 9,92 ms
'---------------------------------------------------------------------------
Ontimer0:
   Reset Watchdog
   Timer0 = 100
   Gosub Get_tilt_angle
   #if Rocker_enable = 1
      Get_rocker_angle
   #endif
   Gosub Algo
   #if I2c_enable = 1
      Gosub Gear
   #endif
   Gosub Steering
   Gosub Process
   Gosub Set_pwm
   Gosub Checkbatt
   Incr Zaehler
Return

'---------------------------------------------------------------------------
'Get_tilt_angle:
'  Measurement of all analog values (5 values).
'  Using 104 µs per measurement total time is 520 µs.
'  The values are used to build moving averages.
'---------------------------------------------------------------------------
Sub Get_tilt_angle
   Ad_gyro = Getadc(ad_kanal_gyro)
   Ad_gyro = 1024 - Ad_gyro                                 'Inverted value for Ad_gyro
   Ad_adxl = Getadc(ad_kanal_adxl)
   Ad_batt = Getadc(ad_kanal_batt)
   Ad_rocker = Getadc(ad_kanal_rocker)
   Ad_swi = 1024 - Getadc(ad_kanal_fuss)                    'Inverted value for Ad_swi
   Ad_adxl = Ad_adxl * 10
   Ad_gyro = Ad_gyro * 10
   'Subtract   1/250th   of   the   value
   Buf = Total_adxl_gyro / Total_looptime
   Total_adxl_gyro = Total_adxl_gyro - Buf
   Buf = Ad_adxl - Adxl_zero                                'Deviation from horizontal position
   Total_adxl_gyro = Total_adxl_gyro + Buf                  'A new 1/250th value is added

   'Filter for gyro
   Buf1 = Average_gyro / Total_looptime
   Average_gyro = Average_gyro - Buf1                       'Subtract 1/250th of the value
   Average_gyro = Average_gyro + Ad_gyro                    'A new 1/250th value is added
   Buf1 = Average_gyro / Total_looptime10                   'Calculation of angular rate

   Buf = Ad_gyro * 10
   Buf1 = Buf1 - Buf                                        'Deviation from zero
   Buf1 = Buf1 * 30
   Angle_rate = Buf1 / 100                                  '3.5 * deviation rate

   'Calculation of Tilt_angle
   'correct the Tilt_angle with the Average of the gyro.
   'The ADXL-Lowpass Filter terminate all fast Peaks.
   'The Angleratepart rebuilds the correct flanks in Tilt_angle
   Total_adxl_gyro = Total_adxl_gyro + Angle_rate
   Buf = Total_looptime10 * 10
   Tilt_angle = Total_adxl_gyro / Buf
   Angle_rate = Angle_rate / 10
End Sub
'---------------------------------------------------------------------------
'Get_rocker_angle:
'  calculate the tiltangle for the rocker
'  this is done in the same way as Get_tilt_angle
'---------------------------------------------------------------------------
#if Rocker_enable = 1
   Sub Get_rocker_angle
      Ad_adxl_r = Getadc(ad_kanal_rocker_adxl)
      Ad_adxl_r = Ad_adxl_r * 10

      'Subtract   1/250th   of   the   value
      Buf_r = Total_adxl_gyro_r / Total_looptime_r
      Total_adxl_gyro_r = Total_adxl_gyro_r - Buf_r
      Buf_r = Ad_adxl_r - Adxl_zero_r
      Total_adxl_gyro_r = Total_adxl_gyro_r + Buf_r

      Buf = Total_looptime_r * 10
      Buf = Buf / 2.7
      Tilt_angle_r = Total_adxl_gyro_r / Buf
      If Tilt_angle_r > 35 Then Tilt_angle_r = 35
      If Tilt_angle_r < -35 Then Tilt_angle_r = -35
      Tilt_angle_r = Tilt_angle_r / 2
   End Sub
#endif
'---------------------------------------------------------------------------
'Algo:
'  Calculation of motor current out of sensor values.
'---------------------------------------------------------------------------
Sub Algo
   Balance_moment = Tilt_angle + Anglecorrection
   Balance_moment = Angle_rate + Balance_moment
   Balance_moment = Balance_moment * 17
   Drive_sum = Drive_sum + Balance_moment
   If Drive_sum > 55000 Then                                'Limiting Drive_sum
      Drive_sum = 55000
   End If
   If Drive_sum < -55000 Then                               'Limiting Drive_sum
      Drive_sum = -55000
   End If
   Buf = Drive_sum / 155
   Buf1 = Balance_moment / 165
   Drivespeed = Buf + Buf1
End Sub

'---------------------------------------------------------------------------
'Steering:
'  Calculation of steering values.
'---------------------------------------------------------------------------
Sub Steering
   Rockersq = Rocker_zero - Ad_rocker
   #if I2c_enable = 1
      Buf1 = Speed_sum / 70                                 'Steering depends on speed (new and cool)
      Buf1 = Abs(buf1)
      If Buf1 > 15 Then Buf1 = 15
      Buf2 = 30 - Buf1
      Buf2 = Buf2 / 3
      If Rockersq > 35 Then Rockersq = 35
      If Rockersq < -35 Then Rockersq = -35
      Rockersq = Rockersq * Buf2
      Rockersq = Rockersq / 12
      Speed_error = Rockersq - Speed_correction
      If Speed_error > 30 Then Speed_error = 30
      If Speed_error < -30 Then Speed_error = -30
      Rockersq = Rockersq + Speed_error
   #else
      Buf1 = Drive_sum / 20000                              'Steering depends on power (old and ugly)
      Buf1 = Abs(buf1)
      If Buf1 < 1 Then Buf1 = 1
      Rockersq = Rockersq / Buf1
      'Some safety lines, limits steering: Drive_sum 55000 max = +-5
      Buf1 = Drive_sum / 2000
      Buf1 = Abs(buf1)
      If Buf1 > 22 Then Buf1 = 22
      Buf1 = 27 - Buf1
      Buf2 = Buf1 * -1
      If Rockersq > Buf1 Then Rockersq = Buf1
      If Rockersq < Buf2 Then Rockersq = Buf2
   #endif
   Steeringsignal = Rockersq
   Drive_a = Drivespeed - Steeringsignal
   Drive_b = Drivespeed + Steeringsignal
End Sub

'---------------------------------------------------------------------------
'Process:
'  Processing Wheelie states.
'  On standby delete all values, so the motors standing still
'---------------------------------------------------------------------------
Sub Process
   Select Case Mmode
   Case _standby                                            'Foot switch is released:wating...
      Drive_a = 0
      Drive_b = 0
      Drivespeed = 0
      Anglecorrection = 0
      Drive_sum = 0
      Tilt_angle = 0
      Total_adxl_gyro = 0
      Timeout = 0
      Statustext = "Standby"
      If Ad_swi > Sw_down Then                              'If foot switch is pressed: change Mmode to run
         Gosub Init                                         'Initialize sensors
         Mmode = _run
         Statustext = "Drive!"
         Timeout = 0
      End If
   Case _run                                                'Foot switch is pressed: action...
      If Ad_swi < Sw_down Then                              'If foot switch is released change Mmode to down
         Mmode = _down
      End If
   Case _down
         Stop Watchdog
         Timeout = 0
         For Buf1 = 1 To 255
            If Drive_a = 0 And Drive_b = 0 Then
               Exit For
            End If
            If Drive_a > 0 Then Decr Drive_a
            If Drive_a < 0 Then Incr Drive_a
            If Drive_b > 0 Then Decr Drive_b
            If Drive_b < 0 Then Incr Drive_b
            Waitms 25                                       'Loop speed
            Gosub Set_pwm
         Next Buf1
         Mmode = _standby
         Statustext = "Standby"
         Start Watchdog
   Case Else                                                'If an unknown state occurs then slow down
      Mmode = _down
   End Select
End Sub

'---------------------------------------------------------------------------
'Set_pwm:
'  Limiting PWM and set direction flags.
'---------------------------------------------------------------------------
Sub Set_pwm
   'Limiting PWM
   If Drive_a > Max_pwm Then
      Drive_a = Max_pwm
   Elseif Drive_a < Mmax_pwm Then
      Drive_a = Mmax_pwm
   End If
   If Drive_b > Max_pwm Then
      Drive_b = Max_pwm
   Elseif Drive_b < Mmax_pwm Then
      Drive_b = Mmax_pwm
   End If
   Cw_old_a = Cw_ccw_a
   Cw_old_b = Cw_ccw_b
   'Set direction flag
   If Drive_a < 0 Then
      Cw_ccw_a = 1
   Else
      Cw_ccw_a = 0
   End If
   If Drive_b < 0 Then
      Cw_ccw_b = 1
   Else
      Cw_ccw_b = 0
   End If
   If Cw_old_a <> Cw_ccw_a Then
      Set Cw_change_a
   Else
      Reset Cw_change_a
   End If
   If Cw_old_b <> Cw_ccw_b Then
      Set Cw_change_b
   Else
      Reset Cw_change_b
   End If
   Drivea = Abs(drive_a)
   Driveb = Abs(drive_b)

   'Inverse signal with 180° phaseshift to PWMb
   Pwm_al = 255 - Drivea
   Pwm_bl = Driveb
End Sub

'---------------------------------------------------------------------------
'CheckBatt:
'  Battery state (voltage) is signaled with the 3 LEDs.
'---------------------------------------------------------------------------
Sub Checkbatt
   'Moving average of battery voltage
  Buf1 = Average_batt / 300
  Average_batt = Average_batt - Buf1
  Average_batt = Average_batt + Ad_batt
  Buf1 = Average_batt / 300
   'Set the right LED
   If Buf1 > Battok Then
      Set Led1
      Reset Led2
      Reset Led3
   End If
   If Buf1 < Battok Then
      Set Led2
      Reset Led3
      Reset Led1
   End If
   If Buf1 < Battdead Then
      Reset Led2
      Reset Led1
      Set Led3
   End If
End Sub

'---------------------------------------------------------------------------
'Init:
'  Calibration: Calculation of steering mid and  platform horizontal position.
'---------------------------------------------------------------------------
Sub Init
   'Calculate mid position of steering (Steering_zero)
   Stop Watchdog
   Ad_rocker = 0
   For Buf = 1 To 10                                        'Mean out of 10 values
      Ad_rocker = Ad_rocker + Getadc(ad_kanal_rocker)
      Waitms 1
   Next Buf
   Rocker_zero = Ad_rocker / 10
   'Calculate gyro horizontal position of platform
   Ad_gyro = 0
   For Buf = 1 To Total_looptime
      Buf1 = Getadc(ad_kanal_gyro)
      Buf1 = 1024 - Buf1
      Buf1 = Buf1 * 10
      Ad_gyro = Ad_gyro + Buf1
      Waitms 2
   Next Buf
   Gyro_zero = Ad_gyro / Total_looptime
   Average_gyro = Ad_gyro                                   'Preset Average_gyro

   'Calculate ADXL horizontal position of platform
   Ad_adxl = 0
   For Buf = 1 To 100                                       'Mean out of 100 values
      Buf1 = Getadc(ad_kanal_adxl)
      Ad_adxl = Ad_adxl + Buf1
      Waitms 2
   Next Buf
   Adxl_zero = Ad_adxl / 10
#if Rocker_enable = 1
      'Calculate gyro horizontal position of platform
   Ad_gyro_r = 0
   For Buf = 1 To Total_looptime_r
      Buf1 = Getadc(ad_kanal_rocker_gyro)
      Buf1 = Buf1 * 10
      Ad_gyro_r = Ad_gyro_r + Buf1
      Waitms 2
   Next Buf
  Average_gyro_r = Ad_gyro_r
   'Calculate ADXL horizontal position of platform
   Buf1 = 0
   For Buf = 1 To 100                                       'Mean out of 100 values
      Ad_adxl_r = Getadc(ad_kanal_rocker_adxl)
      Buf1 = Ad_adxl_r + Buf1
      Waitms 2
   Next Buf
   Adxl_zero_r = Buf1 / 10
#endif
   'Check if values are in tolerable range
   If Adxl_zero < 4000 Then
      Errno = 3
      Goto Err_value
      Print "2:11=10"
   End If
   If Adxl_zero > 6000 Then
      Errno = 3
      Goto Err_value
      Print "2:11=11"
   End If
   If Gyro_zero < 5000 Then
      Errno = 2
      Goto Err_value
      Print "2:11=12"
   End If
   If Gyro_zero > 6500 Then
      Errno = 2
      Goto Err_value
      Print "2:11=13"
   End If
   Start Watchdog
End Sub

'---------------------------------------------------------------------------
'Err_value:
'  On error the program is processing this routine endlessly.
'  Switch the Wheelie off to reset.
'---------------------------------------------------------------------------
Sub Err_value
   Stop Watchdog
   Do
      For Buf = 1 To Errno
         Set Led3
         Waitms 150
         Reset Led3
         Waitms 150
      Next Buf
      Wait 2
   Loop
End Sub
'---------------------------------------------------------------------------
'     Gear
'
'     get values from the Geartoothsensors
'
'---------------------------------------------------------------------------
#if I2c_enable
Sub Gear
   I2creceive &H40 , Speed_array(1) , 0 , 4                 'Gearsensoren über I²C abfragen
   Speed_left = 11810 / Speed_left                          'umrechnen in km/h *10 damit wir eine Nachkommastelle ohne Single haben
   Speed_right = 11810 / Speed_right                        'der Wert müsste deshalb durch 10 geteilt werden
   If Cw_ccw_b = 1 Then                                     '1181 errechnet sich aus 64µs pro Timertakt des Gearprozessors und 2,1cm Wegstrecke pro Zahnrad
      Speed_left_out = Speed_left * -1
   Else
      Speed_left_out = Speed_left
   End If
   If Cw_ccw_a = 1 Then
      Speed_right_out = Speed_right * -1
   Else
      Speed_right_out = Speed_right
   End If
   Buf = Speed_sum / 5                                      'Mittelwert aus dem Durchschnitt der beiden Geschwindigkeiten bilden
   Speed_sum = Speed_sum - Buf
   Buf = Speed_left_out + Speed_right_out
   Speed_sum = Speed_sum + Buf

   Buf = Speed_diff / 5                                     'Mittelwert aus der Differenz der beiden Geschwindigkeiten bilden
   Speed_diff = Speed_diff - Buf
   Buf = Speed_left_out - Speed_right_out
   Speed_diff = Speed_diff + Buf
   Speed_correction = Speed_diff / 17
End Sub
#endif
'---------------------------------------------------------------------------
'     Serial0charmatch
'
'     Im Eingangspuffer wurde ein "13" empfangen
'     Dieser eine Takt Interrupt verzögert theoretisch die Regelung!?!
'---------------------------------------------------------------------------

Serial0charmatch:
    Cr_received = 1
Return

'---------------------------------------------------------------------------
'     Checkserinp
'
'     Hier ist der String abgeholt
'     Der Mainchannel und der : wird geprüft
'---------------------------------------------------------------------------

Sub Checkserinp
   If Asc(serinpstr) = 35 Then                              'Strings mit # gleich weiterleiten
      Print Serinpstr
      Exit Sub
   End If
   If Asc(serinpstr) = 27 Then                              'Strings mit ESC gleich weiterleiten
      Print Serinpstr
      Exit Sub
   End If
   Zeichenpos = Instr(serinpstr , ":")
   If Zeichenpos > 0 Then                                   ' Doppelpunkt Gefunden
      If Instr(serinpstr , "*") > 0 And Instr(serinpstr , "*") < Zeichenpos Then
         Print Serinpstr
         Call Parser
         Exit Sub
      Else                                                  'kein *
         Serinpbuffer = Left(serinpstr , Zeichenpos)
         Mainchannel = Val(serinpbuffer)
         If Mainchannel = Mymainchannel Then
            Call Parser
            Exit Sub
         Else
            Print Serinpstr
            Exit Sub
         End If
      End If
   Else                                                     'kein :
      If Mainchannel = Mymainchannel Then                   'Mainchannel müsste noch von vorigem Durchlauf drinstehen
         Call Parser
         Exit Sub
      Else                                                  'anderes Modul
         Print Serinpstr
         Exit Sub
      End If
   End If
End Sub
'---------------------------------------------------------------------------
'     Parser
'
'     Der Parser zerteilt die empfangene Zeichenkette in Moduladresse,
'     Subchannel (Befehl) und Wert.
'---------------------------------------------------------------------------
Sub Parser
   Dim I As Byte
   Dim A As Byte
   A = 1
   Serinpbuffer = ""
   Mnemonicbuffer = ""
   Wert = 0
   Subchannel = 0                                           'der ":" und alles davor kommt weg
   Zeichenpos = Len(serinpstr) - Instr(serinpstr , Doppelpunkt)
   Serinpbuffer = Right(serinpstr , Zeichenpos)
   Mnemonicbuffer = ""
   Zeichenpos = Instr(serinpbuffer , Istgleich)
   If Zeichenpos = 0 Then Zeichenpos = Len(serinpbuffer)
   For I = 1 To Zeichenpos                                  'Den ganzen String bis Ende oder = nach Zahlen durchsuchen
       If Serinbuffarr(i) >= "0" And Serinbuffarr(i) <= "9" Then
          Mnemonicbufferarray(a) = Serinbuffarr(i)
          Incr A
          Mnemonicbufferarray(a) = 0
       End If
   Next
   Subchannel = Val(mnemonicbuffer)                         'gefundene Zahl mit dem Mnemonic addieren
   If Instr(serinpbuffer , Istgleich) > 0 Then              '= ist vorhanden
      Frage = 0
   Else
      Frage = 1
   End If
   Zeichenpos = Len(serinpbuffer) - Instr(serinpbuffer , Istgleich)
   Serinpbuffer = Right(serinpbuffer , Zeichenpos)          'alles hinter = ist der Wert
   Wert = Val(serinpbuffer)
   'An dieser Stelle ist alles ausgewertet
   'Mainchannel ist die Kartennummer des Befehls
   'Subchannel ist der Befehl
   'Wert ist der Wert der gesetzt werden soll
   'Frage steht auf 1 wenn kein = im Datensatz vorgekommen ist
   Select Case Subchannel
      Case 1                                                'liest den ADXL-320 Wert
         Serinpstr = "#0:ADX=" + Str(ad_adxl) + "!"
         Print Serinpstr
      Case 2                                                'liest den IDG-300 Wert
         Serinpstr = "#0:IDG=" + Str(ad_gyro) + "!"
         Print Serinpstr
      Case 3
         Serinpstr = "#0:IDN=" + Idenditaet
         Print Serinpstr
      Case 6
         Serinpstr = "#0:BAT=" + Str(ad_batt) + "!"
         Print Serinpstr
      Case Else
         Serinpstr = "#0:ERR=[Syntax Error]!"
         Print Serinpstr
   End Select
End Sub