Page 222
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
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.