GMU:Functions and Classes (Arduino): Difference between revisions

From Medien Wiki
Line 108: Line 108:




''
// Defining a class for motor control


// Defining a class for motor control
class MotoMamaMotor {
class MotoMamaMotor {


private: // the following definitions are only of „internal use“ for the class itself
int forwardPin; //Every MotoMamaMotor type Object has its own forwardPin
int reversePin;
int throttlePin;


public: // the following qualities and methods are visible from the outside.
  private: // the following definitions are only of „internal use“ for the class itself
//  This setup-Function has to be called before using the motor control
    int forwardPin; //Every MotoMamaMotor type Object has its own forwardPin
void setup(int newForwardPin, int newReversePin, int newThrottlePin){
    int reversePin;
// we remember the pins for future use         
    int throttlePin;
forwardPin=newForwardPin;
reversePin=newReversePin;
throttlePin=newThrottlePin;
// and at the same time we initialize the outputs !
digitalWrite(throttlePin,LOW); //  so that the motors don’t spin directly from the beginning ..
pinMode(forwardPin,OUTPUT);
pinMode(reversePin,OUTPUT);
pinMode(throttlePin,OUTPUT);
}


// this Function allows to control the motor speed  
  public: // the following qualities and methods are visible from the outside.
void setThrottle (int newThrottle){
    //  This setup-Function has to be called before using the motor control
if (newThrottle>0){ // should it spin forwards or backwards?
    void setup(int newForwardPin, int newReversePin, int newThrottlePin) {
// spin forwards
      // we remember the pins for future use
digitalWrite(reversePin,LOW);
      forwardPin = newForwardPin;
digitalWrite(forwardPin,HIGH);
      reversePin = newReversePin;
}else{
      throttlePin = newThrottlePin;
// spin backwards
      // and at the same time we initialize the outputs !
digitalWrite(forwardPin,LOW);
      digitalWrite(throttlePin, LOW); //  so that the motors don’t spin directly from the beginning ..
digitalWrite(reversePin,HIGH);
      pinMode(forwardPin, OUTPUT);
}
      pinMode(reversePin, OUTPUT);
// adjust the speed:
      pinMode(throttlePin, OUTPUT);
analogWrite(throttlePin, newThrottle);
    }
}
 
    // this Function allows to control the motor speed
    void setThrottle (int newThrottle) {
      if (newThrottle > 0) { // should it spin forwards or backwards?
        // spin forwards
        digitalWrite(reversePin, LOW);
        digitalWrite(forwardPin, HIGH);
      } else {
        // spin backwards
        digitalWrite(forwardPin, LOW);
        digitalWrite(reversePin, HIGH);
      }
      // adjust the speed:
      analogWrite(throttlePin, newThrottle);
    }
};
};


// create two separate MotoMamaMotor type Objects (leftMotor, rightMotor). They can be used as normal variables.  
// create two separate MotoMamaMotor type Objects (leftMotor, rightMotor). They can be used as normal variables.
MotoMamaMotor leftMotor;
MotoMamaMotor leftMotor;
MotoMamaMotor rightMotor;
MotoMamaMotor rightMotor;


void setup(){ // this is our „main“ setup Function
void setup() { // this is our „main“ setup Function
leftMotor.setup(3,4,5); // assign the pin numbers for each motor in the setup Function: setup(int newForwardPin, int newReversePin, int newThrottlePin)
  leftMotor.setup(3, 4, 5); // assign the pin numbers for each motor in the setup Function: setup(int newForwardPin, int newReversePin, int newThrottlePin)
rightMotor.setup(6,7,8);
  rightMotor.setup(6, 7, 8);
};
};


void loop(){ // this is our „main“ loop Function where concrete actions are executed. For example:
void loop() { // this is our „main“ loop Function where concrete actions are executed. For example:
// Start backwards, slow down and accelerate forwards  
  // Start backwards, slow down and accelerate forwards
for (int i =-255, i<=255;i++){
  for (int i = -255, i <= 255; i++) {
leftMotor.setThrottle(i);
    leftMotor.setThrottle(i);
rightMotor.setThrottle(i);
    rightMotor.setThrottle(i);
  };
  // Start forwards, slow down and accelerate backwards
  for (int i = 255, i >= -255; i--) {
    leftMotor.setThrottle(i);
    rightMotor.setThrottle(i);
  };
};
};
// Start forwards, slow down and accelerate backwards
for (int i =255, i>=-255;i--){
leftMotor.setThrottle(i);
rightMotor.setThrottle(i);
};
};
''


==Organizing classes in separate files==
==Organizing classes in separate files==

Revision as of 11:42, 28 March 2017

Motivation

Soon after starting to write the program for your robot you will encounter some typical problems:

  • clarity: the bigger the program gets the harder it is to keep the general overview
  • decoupling: you only want to change a certain part of the program without influencing all the rest
  • recyclability: there are several similar parts in your robot for which you would like to reuse particular sections of your code


These problems reach back to the beginning of programming and their solution is an art and a science at the same time. It generally helps a lot to separate your program into single subunits that are relatively independent from each other.


Example: Controlling Motors

The function is an enormous useful tool that can help you structuring your code. Functions allow you to sum up several operations under the same command. This command can be called again and again without knowing about or duplicating the included code.

Here is an example for a function for setting the speed of a motor that is connected to an H-bridge:


OOPARD1.jpg


Now (and for every prospective motor movement) we only need to write one single line in order to for instance setting the left wheels speed:


OOPARD2.jpg


This already saves us a lot of typing – But one thing still seems quite laborious: we have to know the exact pin numbers every time we want to change the speed.

Imagine a program in which similar function calls are distributed all over. In case of a hardware update you would have to find every single function call and change the parameters manually. This effort could surely be spent for more useful things..

It would be ideal to find a way of calling a certain function that only offers the most useful parameters (like setting the speed) and which handles technical details (switching pins on and off) internally.


Possible solution: Data and code in one package – „Classes“ and „Objects“

C++ (and also Arduino) provides a specific feature that can solve our problem: Classes and Objects

A Class predefines common qualities of a group of Objects. It for instance determines that every motor has the possibility to exert a certain amount of throttle. The corresponding program could look like this:


OOPARD3.jpg


Now the computer knows that there is a class called „Motor“. This class (class) provides a function called „setThrottle“ that is called as an argument by using a number (int) and that does not return anything (void).


Classes are Data-types

We can use our „Motor“ - class the same way we are using other data - types (int, long, float).

The following lines use that quality in order to implement two motors:

OOPARD4.jpg


Calling inner functions of a class (Methods)

Imagine we want to drive the left motor with full throttle whereas the right motor should stop completely. This can be expressed as follows:


OOPARD5.jpg


A positive side effect: your program gains readability by picking distinct names for your Objects and Methods.


Classes can contain other variables that can only be accessed „internally“

How do the Objects „leftMotor“ and „rightMotor“ know which pins they want to switch on and off?

Until now our „Motor“ class is quite abstract since it does not contain this information. A class that should conduct practical actions needs further variables (concrete pin numbers) and lines of code that describe what to do exactly. We can simply include both things in the class definition:


OOPARD6.jpg


How do we include the pin numbers into the Object if they are not visible from the outside? We simply write a „setup“ Function for the class:


OOPARD7.jpg



A complete example

In order to construct a complete example we only have to include the „speed“-Function (see description above):


// Defining a class for motor control class MotoMamaMotor {


 private: // the following definitions are only of „internal use“ for the class itself
   int forwardPin; //Every MotoMamaMotor type Object has its own forwardPin
   int reversePin;
   int throttlePin;
 public: // the following qualities and methods are visible from the outside.
   //  This setup-Function has to be called before using the motor control
   void setup(int newForwardPin, int newReversePin, int newThrottlePin) {
     // we remember the pins for future use
     forwardPin = newForwardPin;
     reversePin = newReversePin;
     throttlePin = newThrottlePin;
     // and at the same time we initialize the outputs !
     digitalWrite(throttlePin, LOW); //  so that the motors don’t spin directly from the beginning ..
     pinMode(forwardPin, OUTPUT);
     pinMode(reversePin, OUTPUT);
     pinMode(throttlePin, OUTPUT);
   }
   // this Function allows to control the motor speed
   void setThrottle (int newThrottle) {
     if (newThrottle > 0) { // should it spin forwards or backwards?
       // spin forwards
       digitalWrite(reversePin, LOW);
       digitalWrite(forwardPin, HIGH);
     } else {
       // spin backwards
       digitalWrite(forwardPin, LOW);
       digitalWrite(reversePin, HIGH);
     }
     // adjust the speed:
     analogWrite(throttlePin, newThrottle);
   }

};

// create two separate MotoMamaMotor type Objects (leftMotor, rightMotor). They can be used as normal variables. MotoMamaMotor leftMotor; MotoMamaMotor rightMotor;

void setup() { // this is our „main“ setup Function

 leftMotor.setup(3, 4, 5); // assign the pin numbers for each motor in the setup Function: setup(int newForwardPin, int newReversePin, int newThrottlePin)
 rightMotor.setup(6, 7, 8);

};

void loop() { // this is our „main“ loop Function where concrete actions are executed. For example:

 // Start backwards, slow down and accelerate forwards
 for (int i = -255, i <= 255; i++) {
   leftMotor.setThrottle(i);
   rightMotor.setThrottle(i);
 };
 // Start forwards, slow down and accelerate backwards
 for (int i = 255, i >= -255; i--) {
   leftMotor.setThrottle(i);
   rightMotor.setThrottle(i);
 };

};

Organizing classes in separate files

We can improve our program structure by putting classes in separate files.

1) Click on the arrow in the right corner in order to add a new file. Select „new Tab“.


BILD----

2) Enter a filename at the bottom of the window. It must end with .h ! In our case I suggest the name motorControl.h.

3) Enter the first line:


  1. include „Arduino.h“


This line „links“ the file to the Arduino-specific features. Now we can include the code of our class.

4) Go back to your main file. Now you have to „link“ the new file to your main file in order to use the features of our class. Include the following line in the top of your main file


  1. include „MotorControl.h“


and write the rest of your program as usual.


More details on the concept

Classes can build up on each other (inheritance)

In the beginning we promised that it is possible to replace different motor drivers without changing the rest of the program. Classes can indeed include other Objects in the form of „member variables“.

C++ allows class declarations and the code for included Functions to be be placed in different files. If you want to know more about that you can read this article about creating libraries in Arduino: http://arduino.cc/en/Hacking/LibraryTutorial