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

From Medien Wiki
No edit summary
No edit summary
Line 146: Line 146:




===A complete example ===
==A complete example ==




Line 217: Line 217:




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


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




===More details on the concept===
==More details on the concept==


Classes can build up on each other (inheritance)
Classes can build up on each other (inheritance)

Revision as of 10:58, 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:


void setMotorSpeed(int newSpeed, int forwardPin, int reversePin, int throttlePin){ // should it spin forwards or backwards? if(newSpeed>0){ //spin forwards digitalWrite(reversePin,LOW); digitalWrite(forwardPin,HIGH); }else{ //spin backwards digitalWrite(forwardPin,LOW); digitalWrite(reversePin,HIGH); } //adjust the speed: analogWrite(throttlePin, newSpeed); }


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:


setMotorSpeed(speed, leftForwardPin, leftReversePin, leftThrottlePin); // the pin numbers are saved in variables


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:


class Motor{ public: // the following qualities and methods are visible from the outside. void setThrottle(int newThrottle); // every motor has the possibility to exert trottle. }


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:


Motor leftMotor; // declare an object of the type „Motor“ called „leftMotor“ Motor rightMotor; // declare another object of the type „Motor“ called „rightMotor“


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:


// the objetcs name „leftMotor“ and the herein implemented function „setThrottle“ are separated by a dot. leftMotor.setThrottle(255); rightMotor.setThrottle(0);


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:


class MotoMamaMotor { ... private: // the following definitions are only of „internal use“ for the class itself int forwardPin; // c int reversePin; int throttlePin; ... };


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:


class MotoMamaMotor { private: ... public: // we want the setup-function to be accessible from the outside. 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); } ... };


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