Page 222

PWM colours breadboard layout

Page 223

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); } }

void loop() { if (Serial.available()) {      int speed = Serial.parseInt();      speed = constrain(speed, 30, 255);      setSpeed(speed);      Serial.print("Setting to: ");      Serial.println(speed); } }

Page 224

void setSpeed(byte speed) { if(lastSpeed == 0 && speed < 100) {     analogWrite(motorPin, 100);     delay(100); } lastSpeed = speed; analogWrite(motorPin, speed); }

Page 225

PWM colours breadboard layout

Page 226

#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

Page 227

#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

Page 228

#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 up 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; } }

Pages 229 and 230

#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; } } 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; }

Pages 231 and 232

#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; }

Page 233

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;     } } }

More on enums

Chapter 14 ended with some notes on addressing enums declared within a C++ class.
Up-to-date C++ allows us to declare enums external to a class like this:
    class SpeedFlags {trip, angle, meter, rotations}
and address them like:
    int speedoTest = SpeedoFlags::angle;
Appart from better documenting the enum usage in code, this has the additional advantage that a function can contain a variable with the same name as an enum value as the "enum class" definition defines a scope for the enum value names.