Getting mobile

In this chapter, I am going to explore the creation of a C++ class which could be saved for future use as a library. The class will be developed to assist in the control of a DC motor by collecting feedback on speed and distance travelled. To start off with though, we could create a very simple management circuit that will run a small DC motor and allow some basic control over the running speed.

Parts

1 x 6 volt DC motor with leads
1 x PN2222 transistor
1 x 1N4001 diode
1 x 330 ohm resistor
3 x Jumper wires
1 x Arduino of choice
1 x PP9 battery to act as external power supply (or wall wart or breadboard PSU)

PWM colours breadboard layout

The transistor is correctly set for polarity when the flat face is towards the other components. The diode is marked to help orientate it and this plays the vital role of diverting any current generated within the motor, as it brakes, away from the transistor. The transistor acts as a “tap” for the power being fed to the DC motor – with the tap being adjustable all the way from closed to fully open based upon the voltage applied to the central pin.

With an external power supply plugged into the Arduino, upload the following test code:

const int motorPin = 11;
void setup() {
  pinMode(motorPin, OUTPUT);
  Serial.begin(115200);
  Serial.println("Set speed 0 to 255");
}

void loop() {
   if (Serial.available()) {
      int speed = Serial.parseInt();
      speed = constrain(speed, 0, 255);
      analogWrite(motorPin, speed);
      Serial.print("Setting ");
      Serial.println(speed);
   }
}

Once the hardware is set up and the program uploaded, you can use the Serial Monitor to send a number between 0 and 255 to the Arduino. The program would then use that value to vary the output on pin 11. Varying the value applied to the motor control pin should result in a related motor speed variation. In my tests, the motor speed change seemed very responsive to small changes in the voltage at the output pin. The Arduino is, of course, using Pulse Width Modulation (PWM) to apply the voltage in response to the analogWrite() commands.

The motor I used came fitted with a set of reduction gears making the motor particularly suitable for driving a wheel. The trouble I experienced was that the gears needed some significant effort to get them running. If the motor was already running then it could be slowed down to a value of 30 but it would not start to turn at any value less than 100. I therefore modified the program a little so that if the motor was not running and the start value was less than 100 then the motor was started at 100 and then, after a delay of 100 microseconds, slowed to the required speed.

void loop() {
   if (Serial.available()) {
      int speed = Serial.parseInt();
      speed = constrain(speed, 30, 255);
      setSpeed(speed);
      Serial.print("Setting to: ");
      Serial.println(speed);
   }
}
void setSpeed(byte speed) {
  if(lastSpeed == 0 && speed < 100) {
    analogWrite(motorPin, 100);
    delay(100);
  }
  lastSpeed = speed;
  analogWrite(motorPin, speed);
}

I am not going to suggest for a moment that using a PNP transistor and other discrete components on a breadboard is an effective way to manage DC motor control in any practical project. Not with clones of the original (discontinued) AdaFruit motor shield (and many others) retailing at less than £5. However, this gentle start established the principals involved and identified a feature of my motors that needs to be factored in to any further development.

DC Motors are probably the most practical for robotics as they have high torque at low cost and come in a vast range of sizes and operating voltages. The only snag is that a pair of DC motors need additional hardware and programming support if we want to keep track of their rotational speed and (perhaps) calculate a distance travelled. One way to achieve this is to fit a rotary encoder (a wheel with slots or holes in it) to the motor drive shaft. The form of rotary encoder is “read” by an infra-red light source and detector that sit either side of the encoder wheel generating signal pulses as each slot passes between the IR source and detector. Suitable encoders and detectors (of varying quality) can be sourced at very low cost with an Internet search.

The breadboard layout to support testing the IR sensor module is shown below.

PWM colours breadboard layout

The IR speed sensor modules I purchased were on a small circuit board with four pins to make connections using female jumper wires. Connections were made for 5volts, ground and to the D0 digital output pin. The fourth pin, marked A0, was ignored. I tested the board using the first program in this chapter with a small modification to attach an external interrupt and use that to count incoming pulses. It was immediately clear that “noise” was going to be an issue. Just a visual check showed the number of pulses detected was far too great for a single revolution of the slotted wheel. To be fair, the jumper leads were quite long which may have added to any problem originating at the board end but it looked like the IR sensor board was generating a lot of partial signals. This was greatly reduced by adding a 20K ohm resistor in series with the data line from the sensor. I also found, though, that I needed to solder a 0.1uf ceramic capacitor across the power supply lines (+5v and ground) at the sensor board. These two modifications combined (and included in the image above) finally gave me an accurate sequence of pulses.

This book is supposed to be a programming book and here I am using a soldering iron. OK, OK, next a little strip board with some header pins and the two resistors for two IR modules plus the power supplies. That neatly removed the need for the breadboard when I switched to using a motor driver shield. Back to code very shortly.

Speedo and Trip Meter

In the best of worlds, we should be able to create C++ classes that can both initiate and consume interrupts. Sadly, it turns out that the attachInterrupt() function requires a non-member function (one that is static or not part of a class) to act as the nominal ISR (Interrupt Service Routine) while class functions are always called on a specific object instance created at runtime. If you refer back to the chapter on C++ classes you will see that pointers to class instance members are effectively formed from two parts and that means they can’t be used as a target in the interrupt vector table.

In some circumstances, a static class member can be used to service interrupts but in most instances, it is better to use a normal ISR to redirect a given interrupt to a waiting class handler. Implementing C++ classes that need to respond to interrupts looks like something likely to come up in a whole range of projects so well worth exploring.

We can maybe start with a rough specification for a speedo/trip software object. I wanted to be able to implement and manage multiple instances to support software designed to synchronise multiple DC motors but potentially to support motors carrying out different tasks within a single project. Given that each instance would be fed pulses from an infra-red detector monitoring wheel rotation, my initial requirement was for feedback on both speed and distance travelled.

I had better come clean and say that I tried more than one strategy to tackle this task. I was looking for an “elegant solution”. The lesson here is that almost always the simplest solutions are the best. The other take-away is to “fail often and fail fast”, which implies that you should stop and try a different tack once it becomes clear that an approach is starting to become needlessly complex. I am presenting the “tidiest” solution as this also introduces a couple of interesting code features along the way. Pure elegance will have to wait for another day.

Alongside class instances directly supporting the Speedo model, I decided to use another class to manage the interrupts. This additional class is instantiated automatically by the code and as only one instance is ever required it is known as a “singleton”. This is an instance of the infamous “singleton pattern” much abused by programmers over the years but I hope properly implemented here.

[A purist might require that a properly implemented singleton class itself ensures that only one instance exists. So maybe a near miss then, as I have not implemented an instance count.]

Let’s start with this singular SpeedoMaster class header file:

#ifndef SpeedoMaster_h
#define SpeedoMaster_h
class Speedo; // “Forward Declaration” supports Speedo class pointer
class SpeedoMaster
{
  public:
    SpeedoMaster();
    void addSpeedo(int,Speedo*);
    Speedo* speedo1;
    Speedo* speedo0;
};
extern SpeedoMaster speedoMaster; // this instance is global
#endif

This has a couple of interesting features. The first is the “forward declaration” of a Speedo class that is going to get its own header and code file. You can use a forward declaration when you have multiple class types that need to relate to each other in some way. In this case, we want the SpeedoMaster class to store pointers to multiple Speedo class instances. The linker will resolve the references to the Speedo class during the compile/link process.

The SpeedoMaster class has a constructor that takes no arguments and a single method addSpeedo(). There are also (in this example) two variables for Speedo class instance pointers.

The final content line in the header uses the “extern” keyword and flags up that we will be creating a single global instance of this class. Before we do that, we might take a look at the Speedo class header.

#ifndef Speedo_h
#define Speedo_h
#define sampleRate 250 // sample speed every 250ms
class Speedo{
  public:
    Speedo(int, float);
    float getRPM();
    float getCPM();
    float getTrip();
    void resetTrip();
    void setDirection(bool);
    void pulseCallback();
  private:
    unsigned long lastMillis;
    unsigned int pulseCount;
    volatile float tripValue;
    volatile float rpm;
    volatile bool forwards;
    float cmPerPulse;
    int pulsesPer;
    float wheelCirc;
};
#endif

If the Speedo class had needed to reference the SpeedoMaster class then we could have added a forward declaration to SpeedoMaster in this header file but that was not required here. Otherwise, the header is pretty much as you might expect with five methods used to gather speedo or trip data plus the pulseCallback() method that will be called each time an interrupt on the relevant pin is triggered. The class data members are all private.

Next the C++ code for SpeedoMaster.

#include <Arduino.h>
#include "SpeedoMaster.h"
#include "Speedo.h"

SpeedoMaster speedoMaster; // create global instance

// define required ISRs. Each ISR calls an instance of pulseCallback()
ISR(INT0_vect) {
  speedoMaster.speedo0->pulseCallback();
}
ISR(INT1_vect) {
  speedoMaster.speedo1->pulseCallback();
}
SpeedoMaster::SpeedoMaster() {
}
// addSpeedo sets the interrupt and stores the Speedo instance pointer
void SpeedoMaster::addSpeedo(int intPin, Speedo* speedo) {
  switch (intPin) {
    case 2:
      speedo0 = speedo;
      EIFR |= (1 << INTF0);
      EIMSK |= (1 << INT0);
      EICRA |= (1 << ISC01);
      break;
    case 3:
     speedo1 = speedo;
     EIFR |= (1 << INTF1);
     EIMSK |= (1 << INT1);
     EICRA |= (1 << ISC11);
     break;
  }
}

Note first, that the C++ code file includes both the SpeedoMaster and Speedo header files. The code starts by creating the single global instance of the SpeedoMaster class. The next two code blocks are the ISRs for the two external interrupt vectors that are going to be used by my Uno based project. Adding additional ISRs would be a straightforward addition for (perhaps) an Arduino Due managing more motors needing speedo/trip data feedback. Locating the ISR code here, keeps the required functionality located with the relevant class and supports the calls to any Speedo class pulseCallback() methods when the relevant interrupt are set up.

There is then an empty constructor. A constructor without arguments would pretty much be a requirement in any code following the technique of creating a single instance without intervention from the Arduino program using the class. Remember this is a “singleton” and so should not need any unique set-up data.

Then there is the class method that sets up external interrupts for any supplied pin number. If you refer back to the chapter on Interrupts then you will see that these are going to be triggered by a falling edge. Clearly, this function would be easy to extend for boards with additional external interrupt pins where a project needed to support more Speedo instances. Adapting the code to support pin change interrupts should also prove a straightforward task.

The Speedo C++ code is just a little longer as it supports more methods.

#include <Arduino.h> #include "Speedo.h" #include <util/atomic.h> Speedo::Speedo(int ppRev, float wcCm) { pulsesPer = ppRev; wheelCirc = wcCm; cmPerPulse = wheelCirc / pulsesPer; forwards = true; rpm = tripValue = 0; pulseCount = 0; lastMillis = millis(); } void Speedo::pulseCallback() { pulseCount++; tripValue += (forwards) ? cmPerPulse : -cmPerPulse; unsigned long currentMillis = millis(); unsigned long elapsedMillis = (unsigned long)(currentMillis - lastMillis); if(elapsedMillis >= sampleRate) {     rpm = (pulseCount * (60000 / elapsedMillis)) / pulsesPer;     pulseCount = 0;     lastMillis = currentMillis; } }

followed by…

float Speedo::getRPM() {
  unsigned long currentMillis = millis();
  unsigned long elapsedMillis = (unsigned long)(currentMillis - 
                                 lastMillis);
  if( elapsedMillis >= (sampleRate * 1.5)) {
    return 0;
  }
  float cRPM;
  ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
    cRPM = rpm;
  }
  return cRPM;
}
float Speedo::getCPM() {
  unsigned long currentMillis = millis();
  unsigned long elapsedMillis = (unsigned long)(currentMillis - 
                                 lastMillis);
  if( elapsedMillis >= (sampleRate * 1.5)) {
    return 0;
  }
  float cRPM;
  ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
    cRPM = rpm;
  }
  return cRPM * wheelCirc; 
}
float Speedo::getTrip() {
  float repDistance;
  ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
    repDistance = tripValue;
  }
  return repDistance; 
}
void Speedo::resetTrip() {
  cli();
  tripValue = 0;
  sei();
}
void Speedo::setDirection(bool forward = true) {
  forwards = forward;
}

Note that only the Speedo.h file needs to be included and that the code makes use of ATOMIC_BLOCK macros so is also required as an include.

The class constructor takes two arguments; the number of pulses that should be received for a single rotation of the wheel (the number of slots) and the circumference of the wheel in centimetres. The constructor initialises some variables (perhaps redundant code) and pre-calculates a value for centimetres travelled per pulse received.

The pulseCallback() method increments the pulse count and adds to the tripValue variable which logs the net forward distance travelled by the wheel. The latter value assumes there is no wheel “slip”. The method then checks the elapsed time (in milliseconds) and uses that to calculate a new revolutions per minute (RPM) value when the elapsed time matches or exceeds the sample rate constant (which can be tweaked).

The remaining methods other than resetTrip() and setDirection (which both do what their name suggests) return speed or trip data to a calling program. The getRPM() and getCPM() methods have a time check to make sure that the relevant values are still likely to be current and not residual values from the last time the wheel was in motion.

Using the Speedo Classes

What does the main program need to do to make use of these C++ classes? At least one instance of the Speedo class needs to be created: Speedo rwSpeedo(20, 21.3);

The SpeedoMaster class instance already exists so the new Speedo class can be connected to the external interrupt with: speedoMaster.addSpeedo(RWS_PIN, &rwSpeedo);

Following which, the Speedo methods should deliver speed and distance data in response to the rotation of the relevant motor drive shafts.

The following program is a simple test rig for the classes that also indirectly introduces the I2C interface used by the Adafruit motor shield and also by an increasing number of peripheral boards and devices intended for use with Arduinos and Raspberry Pis (among others).

If you are using a different motor shield or even sticking with an NPN transistor or two then please substitute the relevant motor control statements in place of the clearly identified Adafruit library code lines below.

#include <Wire.h> #include <Adafruit_MotorShield.h> #include "utility/Adafruit_MS_PWMServoDriver.h" #include "SpeedoMaster.h" #include "Speedo.h" template<class T> inline Print &operator <<(Print &obj, T arg) { obj.print(arg); return obj; } #define _NL "\n" #define RWS_PIN 2 // the ext interrupt pins #define LWS_PIN 3 Speedo rwSpeedo(20, 21.3); // create Speedo/Trip objects Speedo lwSpeedo(20, 21.3); // Create the motor shield object with the default I2C address Adafruit_MotorShield AFMS = Adafruit_MotorShield(); //Select shield ports for motors Adafruit_DCMotor *rightMotor = AFMS.getMotor(1); Adafruit_DCMotor *leftMotor = AFMS.getMotor(2);

void setup() {
  Serial.begin(115200);
  speedoMaster.addSpeedo(RWS_PIN, &rwSpeedo); //set interrupt/callback
  speedoMaster.addSpeedo(LWS_PIN, &lwSpeedo);
  AFMS.begin();  // create with the default I2C frequency 1.6KHz
  rightMotor->setSpeed(150);
  rightMotor->run(FORWARD);
  leftMotor->setSpeed(150);
  leftMotor->run(FORWARD);
}
void loop() {
 unsigned long lastMillis = millis();
 while (true) {
   if ((unsigned long)(millis() - lastMillis) > 5000) {
      break;
   }
 }
 Serial << "Right Wheel RPM: " << rwSpeedo.getRPM() << _NL;
 Serial << "Right Wheel CPM: " << rwSpeedo.getCPM() << _NL;
 Serial << "     Right Trip: " << rwSpeedo.getTrip() << _NL;
 Serial << " Left Wheel RPM: " << lwSpeedo.getRPM() << _NL;
 Serial << " Left Wheel CPM: " << lwSpeedo.getCPM() << _NL;
 Serial << "      Left Trip: " << lwSpeedo.getTrip() << _NL;
}

The Speedo class implementation proved immediately useful when I ran trials on a simple two motor platform. One of the DC motors of an apparently identical pair purchased together always ran a little slower than the other. This was true running both “forwards” and “backwards” and across the usable speed range. Clearly, I was going to need to bias the software speed settings sent to the motor shield class to keep the two synchronised well enough for navigational purposes.

An obvious extension to the Speedo class (and similar monitoring classes) would be to set some value, like a specific distance travelled, to raise an “event” to take some action in the main program. There would be a temptation to pass in a pointer to a function to be called when such an event occurred. This would not be a great idea. The Speedo::pulseCallback() (as it is in this example) is effectively part of the ISR process. ISRs should contain the least possible code and should terminate as quickly as possible ready for any following interrupts (from any source) to be serviced. Implementing a facility to call yet another function before interrupts were re-enabled might well invalidate subsequent data readings from the class.

Multiple flags in a single variable

The best solution is to set one or more variables as flags that can be monitored by the main program code. For a single flag, a volatile Boolean variable might fit the bill but if it is possible that several different flags might be raised in a short period of time then you might like to consider letting them share a single variable. This is straightforward if the “flag” values are powers of 2 as we can set, read and unset them easily using bitwise operators without one value overwriting another.

The following program demonstrates setting, reading and resetting a range of flag values in a single byte. A byte is a good choice as it can store up to 8 flags and needs no special protection when accessed by both an ISR and general code.

enum {trip = 1, angle = 2, meter = 4, rotations = 8};
void setup() {
  Serial.begin(115200);
  byte flags = 0;
  flags |= angle; // set angle flag
  flags |= rotations; // set rotations flag
  if(flags & angle) {
    Serial.println("angle flag is set");
    flags ^= angle; // now unset angle flag
  }
  for (int flgs = trip; flgs <= rotations; flgs++) {
    switch(flgs) {
      case trip:
      case angle:
      case meter:
      case rotations:
        Serial.print(flgs);
        if(flags & flgs) {
          Serial.println(": is set");
        } else {
         Serial.println(": is not set");
        }
        break;
    }
  }
}

The program uses the bitwise OR operator to set a given flag and the XOR operator to unset the flag. Testing to see of a particular flag is set is managed using the bitwise AND.

You can’t iterate over a set of enums as they have no existence after the compile process. They might be described as “syntactic sugar” as they do little more than a set of constants created using #define. The relevant values are substituted into your code during the compile process. You can use them to set a for loop range but the code above had to deal with the values within that range that are not flag values.

Addressing class enums

An enum definition does not need a name. Generally, enum names can be considered to be documentation where a meaningful choice is always useful. When defined as part of a class, individual enum elements are addressed like class members. If the Speedo class had contained this definition in the header file:

enum Speedo_Flags (trip = 1, angle = 2, meter = 4, rotations = 8};

 then individual enum values might be addressed as Speedo::angle or Speedo::meter or alternately, speedo.trip or speedo.meter where an instance called “speedo” exists – you get to choose.