-
Notifications
You must be signed in to change notification settings - Fork 45
Old API documentation
As of version 5.0.0
this information is no longer valid. It is kept for mainly historical reasons. Some of the documentation here might still be valid, however you should consult the project' official documentation instead that is guaranteed to be up-to-date.
Here you can find the Smartcar shield library's API documentation that will allow you to control the Smartcar platform and read data from various sensors on it. You can find the various accessible API components as well as usage examples. If you want to review how these components are used in a common sketch, please refer to the Example sketches section. Currently, the library supports the following sensors:
- The the HC-SR04, SRF05 and SRF08 ultra sound sensors that measure distance.
- The SHARP GP2D120, GP2Y0A02 and GP2Y0A21 infrared sensors that measure distance.
- The L3G4200D gyroscope, which returns angular displacement from an initial position.
- A wheel encoder, which returns pulses that can be mapped to centimeters.
You can use as many infrared and ultra sound sensors your development platform allows. They should all be running independently from each other. Moreover, when using the Arduino IDE, the available API components will be color-coded after being typed, which should help you acclimatize yourself and review code faster.
You can use at most one L3G4200D gyroscope and two wheel encoders.
If you are using the hardware Smartcar shield, i.e. you have adopted the default physical setup of the Smartcar platform and not merely utilizing its software library, then please refrain from using pins D8, D9, D10, D11, D12 and D13, as they are already engaged in driving the motors.
- Dependencies
-
API Components
- SR04(unsigned int maxDistance)
-
SRF08()
- void attach(unsigned short address)
- unsigned int getDistance()
- unsigned int getMedianDistance(short iterations)
- void setGain(unsigned short gainValue)
- void setRange(unsigned short rangeValue)
- void setPingDelay(unsigned short milliseconds)
- void changeAddress(unsigned short newAddress)
- unsigned short getLightReading()
- GP2D120()
- GP2Y0A02()
- GP2Y0A21()
- Odometer(unsigned int pulsesPerMeter)
- Gyroscope(int offset)
- Car(unsigned short shieldOrientation)
- Car(SteeringMotor *steering, ThrottleMotor *throttle)
-
Car(ShieldMotors *shieldMotors)
- void begin()
- void begin(Odometer &encoder)
- void begin(HeadingSensor &heading)
- void begin(Odometer &encoder, HeadingSensor &heading)
- void begin(Odometer &encoder1, Odometer &encoder2)
- void begin(Odometer &encoder1, Odometer &encoder2, HeadingSensor &heading)
- void setSpeed(float speed)
- void setAngle(int degrees)
- float getSpeed()
- int getAngle()
- void stop()
- void enableCruiseControl(float Kp, float Ki, float Kd, unsigned short frequency)
- boolean cruiseControlEnabled()
- void disableCruiseControl()
- void updateMotors()
- void go(int centimeters)
- void rotate(int degrees)
- void setMotorSpeed(int leftMotorSpeed, int rightMotorSpeed)
In order to start using the library in your Arduino sketch, you have to include the Smartcar shield library. If you are using Arduino IDE 1.6.5 or lower, then you will have to include the Wire library (for the Gyroscope and the SRF08 sensor) and the Servo library (for a Servo motor and an ESC). You can achieve that in the standard C/C++ way, by using the #include
statements like this:
#include <Smartcar.h>
//#include <Wire.h> //uncomment for Arduino IDE 1.6.5 or lower
//#include <Servo.h>
The above lines must be written every time, if you want to use the Smartcar shield library.
The constructor of an SR04 instance, representing programmatically an HC-SR04 ultra sound sensor that measures distance. You have to instantiate an SR04 in order to be able to read data from it. You should do this before your void setup()
function in order for the object variable to be globally accessible in your sketch and after the include statements. Don't forget that you can instantiate many sensors, as long as they are on different pins.
Note: Optionally, you can provide an argument which will set the maximum distance the sensor will measure. The default value is set to 70 and is defined by the DEFAULT_MAX_US_DISTANCE in SR04.cpp. The HC-SR04 sensor, theoretically can detect distances up to 4 meters, however in practice measurements over 120 centimeters become increasingly unreliable and noisy.
Note: In order to ensure compatibility with the previous iterations of this library, you can use the alias Sonar
instead of SR04 if you wish, however it is not guaranteed that this convention will remain in the long run.
Example
#include <Smartcar.h>
SR04 rearSonar;
//It's the same as: Sonar rearSonar;
void setup(){
/* put your setup code here to run once */
}
void loop(){
/* your main loop code here to run repeatedly */
}
Always use this function first in the void setup()
in order to initialize the HC-SR04 sensor and define the pins that it will be connected to, namely the echo pin and the trigger pin. Don't forget that you can attach many sensors as long as they are on different pins. Note: It does not really matter if you use an int
instead of unsigned short
, as arguments, as long as you remember that pins are almost always a small natural number.
#include <Smartcar.h>
SR04 rearSonar;
const int TRIG_PIN = 38; //sensor's trig pin
const int ECHO_PIN = 39; //sensor's echo pin
void setup(){
rearSonar.attach(TRIG_PIN, ECHO_PIN);
}
Get the distance measured by the specific ultra sound sensor in centimeters. The default maximum distance the sensor can detect is set to 70 and is defined by the DEFAULT_MAX_US_DISTANCE in SR04.cpp. The HC-SR04 sensor, theoretically can detect distances up to 4 meters, however in practice, measurements over 120 centimeters become increasingly unreliable. If nothing was found within the appropriate range, 0
will be returned instead, as an indication of an error. During measurements, the execution of the sketch is blocked and a measurement can last up to approximately 20 milliseconds.
Example
#include <Smartcar.h>
SR04 rearSonar;
const int TRIG_PIN = 38; //sensor's trig pin
const int ECHO_PIN = 39; //sensor's echo pin
void setup(){
Serial.begin(9600);
rearSonar.attach(TRIG_PIN, ECHO_PIN);
}
void loop(){
int distance = rearSonar.getDistance(); //get distance detected by rearSonar
Serial.println(distance); // print the traveled distance
delay(200); //run the above every 200 milliseconds
}
Get the median distance of the last 5 measurements by the specific sensor, in centimeters. Expect a small delay, between the measurements, of around 20 milliseconds when using this method. This is a simple and handy filter for the ultra sound sensor measurements.
Note: Optionally, you can provide an argument to the method in order to manually set the amount of measurements that will be conducted and from which, the median measurement will be returned.
Example
int rearDistance = rearSonar.getMedianDistance(); //get the median of the last 5 measurements
int reallyFilteredDistance = rearSonar.getMedianDistance(10); //get the median of the last 10 measurements
The constructor of an SRF08 instance, representing programmatically an SRF08 ultra sonic sensor that measures distance. You have to instantiate an SRF08 in order to be able to read data from it. You should do this before your void setup()
function in order for the object variable to be globally accessible in your sketch and after the include statements. Don't forget that you can instantiate many sensors, as long as they are on the I2C bus. The SRF08 is generally better than the HC-SR04, however is slightly more complex. Please refer to this white paper if questions regarding its usage arise. Its main advantage against the HC-SR04 is that since it gets connected to the I2C ports, you can connect many sensors using just two pins (SDA & SCL). Additionally, you can specify its maximum range and the minimum frequency that the measurements can be initiated, meaning that for small distances you can perform really fast measurements. Finally, it comes equipped with an on board light intensity sensor.
Example
#include <Smartcar.h>
SRF08 frontSensor;
void setup() {
// put your setup code here, to run once:
}
void loop() {
// put your main code here, to run repeatedly:
}
Always use this function first in the void setup()
in order to initialize the SRF08 sensor and define its I2C address. If no address is provided, then the SRF08 default address (0x70 or 112) will be used instead. If you are planning to use more than one SRF08 ultrasonic sensor, you have to assign them different addresses. In case you do not know which is the I2C address of a particular SRF08 sensor, run the I2C scanner sketch, by Nick Gammon. Remember that you cannot use an SRF08 sensor, without attaching it first.
#include <Smartcar.h>
SRF08 frontSensor;
void setup() {
frontSensor.attach(0x73); //attach the srf08 sensor with the address 0x73 (or 115)
}
Get the distance measured by the specific ultra sound sensor in centimeters. The maximum distance that the ultra sound sensor can measure in real life conditions is set around 6 meters. The sensor's default range is set to measure such distances. By default, the sensor will need 70 milliseconds in order to perform such a measurement. If nothing was found within the appropriate range, 0
will be returned instead, as an indication of an error.
Example
#include <Smartcar.h>
SRF08 frontSensor;
void setup(){
Serial.begin(9600);
frontSensor.attach(116); //attach the srf08 sensor with the address 0x74 (or 116)
}
void loop(){
int distance = frontSensor.getDistance(); //get distance detected by frontSensor
Serial.println(distance); // print the traveled distance
delay(200); //run the above every 200 milliseconds
}
Get the median distance of the last 5 measurements by the specific sensor, in centimeters. Expect a small delay, between the measurements, of approximately 70 milliseconds (by default, unless manually changed using the appropriate commands) when using this method. This is a simple and handy filter for the ultra sound sensor measurements.
Note: Optionally, you can provide an argument to the method in order to manually set the amount of measurements that will be conducted and from which, the median measurement will be derived and return.
Example
int rearDistance = rearSonar.getMedianDistance(); //get the median of the last 5 measurements
int reallyFilteredDistance = rearSonar.getMedianDistance(10); //get the median of the last 10 measurements
By default, the SRF08 sensor measurements last 70 milliseconds, blocking the execution of the sketch. If you need to measure distances faster then you need to set a default gain, range and ping delay manually, during setup()
. You can find more information about this here. The allowed range for gain values is from 0 to 31. There is no rule about which combinations of values work for every range. Some empirical range, gain and ping delay combinations are listed below. Please beware that these might not apply in your setting. Experiment and adapt accordingly.
Settings: Gain: 31 Range: 7 Delay: 6
Results: Maximum range: 34 centimeters - Measurements every 6 milliseconds
Settings: Gain: 0 Range: 23 Delay: 11
Results: Maximum range: 103 centimeters - Measurements every 11 milliseconds
Settings: Gain: 10 Range: 35 Delay: 15
Results: Maximum range: 155 centimeters - Measurements every 15 milliseconds
Example
frontSensor.setGain(31);
By default, the SRF08 sensor measurements last 70 milliseconds, blocking the execution of the sketch. If you need to measure distances faster, then you need to set a default gain, range and ping delay manually, during setup()
. You can find more information about this here. The maximum range the sensor can measure, is calculated by the following formula, giving a result in centimeters: (rangeValue + 1) * 4.3
. Remember, that just setting the range, will not change the delay between the measurements or how fast the measurements can be initiated. If you need to do this, then you will have to set the rest of the values as well. There is no rule about which combinations of values work for every range. Some empirical range, gain and ping delay combinations are listed below. Please beware that these might not apply in your setting. Experiment and adapt accordingly.
Settings: Gain: 31 Range: 7 Delay: 6
Results: Maximum range: 34 centimeters - Measurements every 6 milliseconds
Settings: Gain: 0 Range: 23 Delay: 11
Results: Maximum range: 103 centimeters - Measurements every 11 milliseconds
Settings: Gain: 10 Range: 35 Delay: 15
Results: Maximum range: 155 centimeters - Measurements every 15 milliseconds
Example
frontSensor.setRange(23); //for maximum range of 103 centimeters
By default, the SRF08 sensor measurements last 70 milliseconds, blocking the execution of the sketch. If you need to measure distances faster then you need to set a default gain, range and ping delay manually, during setup()
. You can find more information about this here. Use this method to set the delay between the measurements. Remember, that just setting this delay, does not by itself mean that the sensor will be able to initiate measurements faster or have a shorter range. If you need to do that, then you will have to set the rest of the values as well. There is no rule about which combinations of values work for every range. Some empirical range, gain and ping delay combinations are listed below. Please beware that these might not apply in your setting. Experiment and adapt accordingly.
Settings: Gain: 31 Range: 7 Delay: 6
Results: Maximum range: 34 centimeters - Measurements every 6 milliseconds
Settings: Gain: 0 Range: 23 Delay: 11
Results: Maximum range: 103 centimeters - Measurements every 11 milliseconds
Settings: Gain: 10 Range: 35 Delay: 15
Results: Maximum range: 155 centimeters - Measurements every 15 milliseconds
Example
frontSensor.setPingDelay(6); //measurements last 6 milliseconds, this is probably the fastest it can get
Use this method in order to change the address of an SRF08 sensor. This way, you can use more than one at a time, all of them connected to the I2C port. To make sure that everything works as it should, please have one sensor connected at a time, when you run this method. In order to change a sensor's address, you will have to know its current address and use attach
in the beginning as usual. Then, use this method, to provide a new address for the sensor. The allowed range of addresses is from 112 to 127. After changing the sensor's address, you can continue working with it (the object's address is also updated), however after rebooting the Arduino, you will not be able to attach
it anymore, as it will have the new address and "old" attach
will not work, as it will still be using the old address.
void setup(){
frontSensor.attach(115); //attach the sensor using the old address
frontSensor.changeAddress(113); //change its address to 113
}
The SRF08 sensor, comes equipped with a light intensity sensor which returns an integer that represents how bright is the detected light. In complete darkness, the sensor returns a value of minimum 2-3 and at a very bright environment, the highest value it can return is 248. Its readings are not particularly stable, however it can be used in occasions where the light intensity is used in order to calibrate components that are sensitive to light, such as infrared sensors or cameras.
int lightIntensity = frontSensor.getLightReading();
GP2D120 (or GP2Y0A41SK0F) is a SHARP infrared sensor, that is able to measure distances between 5 and 25 centimeters.
GP2Y0A02 is a SHARP infrared sensor, that is able to measure distances between 25 and 120 centimeters.
GP2Y0A21 is a SHARP infrared sensor, that is able to measure distances between 12 and 78 centimeters.
All of the SHARP infrared sensors, are used in the same way, with the only difference being their constructors. The constructor of a SHARP infrared instance, represent programmatically one of the above SHARP infrared sensors that measures distance. You have to instantiate a SHARP infrared sensor in order to be able to control it. You should do this before your void setup()
function in order for the instance variable to be globally accessible in your sketch and after the include statements. Don't forget that you can instantiate many sensors, as long as they are on different analog pins.
Note: In order to ensure compatibility with the previous iterations of this library, you can also use the alias Sharp_IR
instead of GP2D120 if you wish, however it is not guaranteed that this convention will remain in the long run.
Example
#include <Smartcar.h>
GP2Y0A21 frontIR; //instantiate a GP2Y0A21 sensor
GP2Y0A02 backIR; //instantiate a GP2Y0A02 sensor
GP2D120 sideIR; //instantiate a GP2D120 sensor
//Sharp_IR sideIR; this is equivalent to instantiating a GP2D120 sensor
void setup(){
/* put your setup code here to run once */
}
void loop(){
/* your main loop code here to run repeatedly */
}
Always use this function first in the void setup()
in order to initialize the infrared sensor and define the analog pin that it will be connected to. Don't forget that you can attach many sensors as long as they are on different analog pins. Note: It does not really matter if you use an int
instead of unsigned short
, as arguments, as long as you remember that pins are almost always a small natural number.
Example
#include <Smartcar.h>
GP2Y0A21 frontIR;
const int IR_pin = A5; //set analog pin 5 to receive infra red's data
void setup(){
frontIR.attach(IR_pin); //attach infra red sensor to IR_pin
}
Get the distance measured by the specific infra red sensor in centimeters. The maximum and minimum distances that the sensor can measure, depends on its model. Particularly, the valid ranges of the supported sensors are:
- GP2D120 Minimum distance: 5 cm - Maximum distance: 25 cm
- GP2Y0A02 Minimum distance: 25 cm - Maximum distance: 120 cm
- GP2Y0A21 Minimum distance: 12 cm - Maximum distance: 78 cm
If a distance is out of range (either too close or too far away) 0 will be returned instead, as an indication of an error. Note that if an object is too close to the sensor, a bad (i.e. not representative of the actual distance) value will be returned instead, as due to hardware limitations there is no way for the sensor to tell the difference between objects standing too close to it and objects at a valid distance.
Example
#include <Smartcar.h>
GP2D120 sideIR;
void setup(){
Serial.begin(9600);
sideIR.attach(A1); //attach the GP2D120 sensor to pin A1
}
void loop(){
int distance = sideIR.getDistance(); //get distance detected by sideIR
Serial.println(distance); // print the traveled distance
delay(200); //run the above every 200 milliseconds
}
Get the median distance of the last 5 measurements by the specific infrared sensor, in centimeters. Expect a small delay, needed between each measurement, of approximately 15 milliseconds when using this method. This is a simple and handy filter for the infra red sensor measurements.
Note: Optionally, you can provide an argument to the method in order to manually set the amount of measurements that will be conducted and from which, the median measurement will be returned.
Example
int frontDistance = frontIR.getMedianDistance(); //get the median of the last 5 measurements
int rearDistance = rearIR.getMedianDistance(10); //get the median of the last 10 measurements
The constructor of an Odometer instance, representing programmatically an odometer (speed encoder) sensor that measures motor revolutions and thus an approximation of the traveled distance. You have to instantiate an Odometer in order to be able to read its data. You should do this before your void setup()
function in order for the instance variable to be globally accessible in your sketch and after the include statements.
The constructor receives an argument defining how many pulses the encoder detects per traveled meter, in order for the sensor to be able to let you know, how much distance the vehicle has traveled. This greatly varies among the various settings, wheels and encoders, so if you do not want to use the default pulses per meter ratio, which is defined in Odometer.cpp and particularly the Odometer::DEFAULT_PULSES_PER_METER constant, then you should provide it as an argument. If you do not know how to find out that value for your own car, please refer to the FAQ.
IMPORTANT
- You can only instantiate up to two odometers
- Connect the odometer data line only to interrupt pins! Find out which those are for your board here.
Example
#include <Smartcar.h>
//instantiate two odometers that detect 150 pulses per meter
Odometer encoderLeft(150), encoderRight(150);
void setup(){
/* put your setup code here to run once */
}
void loop(){
/* your main loop code here to run repeatedly */
}
Always use this function in order to initialize the Odometer and define the interrupt pin that it will be connected to. Don't forget that you can attach at most two odometers and that only on interrupt pins. If the pin is not a valid, then the function will return 0
to indicate an error, otherwise if everything works as it returns 1
. Find out which are the (external) interrupt enabled pins on your board here. Always use this method in the setup()
once, before using other methods of the particular object. Note: It does not really matter if you use an int
instead of unsigned short
, as arguments, as long as you remember that interrupt pins are almost always a small natural number.
Example
#include <Smartcar.h>
Odometer encoder;
const int encoderPin = 2; //digital pin 2
void setup(){
encoder.attach(encoderPin);
//more code
}
void loop(){
/* your main loop code here to run repeatedly */
}
Similar to int attach(unsigned short odometerPin) with the difference that you should use this method if your Odometer can indicate the direction of rotation. Attach the signal that indicates the direction of rotation at directionPin
and set forwardDirState
as HIGH or LOW depending on the state of directionPin
when rotating forward.
Odometer encoderLeft(570), encoderRight(570); //570 pulses per meter
void setup() {
encoderRight.attach(2, 12, HIGH); // Pulse pin, direction pin, the state of the direction pin when going forward
encoderLeft.attach(3, 13, LOW);
}
Start measuring distances with the Odometer. Call this function every time you want to initiate the measurement of the traveled distance, may this be in the main loop()
or more commonly in setup()
. Remember that you have to first attach(unsigned short odometerPin)
the Odometer to a valid interrupt pin before you start measuring. If the Odometer is not attached then this function might have unpredictable results. If begin()
has already been used in the sketch, then it will set the recorded distance back to 0.
Example
#include <Smartcar.h>
Odometer encoder;
const int encoderPin = 3; //digital pin 3
void setup(){
encoder.attach(encoderPin);
encoder.begin();
}
void loop(){
/* your main loop code here to run repeatedly */
}
Get the currently traveled distance, in centimeters, since the beginning of the measurement. In other words since the last begin()
. If the Odometer is not attached or has been attached to an invalid (non-interrupt) pin, then this function will keep returning 0
. The traveled distance is usually empirically determined, by measuring how many pulses the encoder records after traveling known distances and then coming up with a pulses per centimeter ratio. This, greatly varies among the various settings, wheels and encoders, so if you do not want to use the default pulses per meter ratio, which is defined in Odometer.cpp and particularly the Odometer::DEFAULT_PULSES_PER_METER constant, then you should provide it as an argument. If you do not know how to find out that value for your own car, please refer to the FAQ.
Example
#include <Smartcar.h>
Odometer encoder;
const int encoderPin = 18; //digital pin 18
void setup(){
Serial.begin(9600);
encoder.attach(encoderPin);
encoder.begin(); // begin measurement HERE
}
void loop(){
int traveledDistance = encoder.getDistance(); //get distance traveled since begin() in setup()
Serial.println(traveledDistance); // print the traveled distance
delay(200); //run the above every 200 milliseconds
}
Get the last measured speed, in meters per second, by the specific Odometer. Beware that if the wheels are stopped, then this function will return the last speed before the wheels were completely immobilized. This happens due to the fact that speed is measured every time a pulse from the Odometer is read. Therefore, when wheels are not moving, no pulses arrive and subsequently, the speed readings are not getting updated.
Example
#include <Smartcar.h>
Odometer encoder;
const int encoderPin = 19; //digital pin 19
void setup(){
Serial.begin(9600);
encoder.attach(encoderPin);
encoder.begin(); // begin measurement HERE
}
void loop(){
float encoderSpeed = encoder.getSpeed(); //get the last measured speed from the encoder
Serial.println(encoderSpeed); // print the traveled distance
delay(200); //run the above every 200 milliseconds
}
The constructor of a Gyroscope instance, representing programmatically a gyroscope, a sensor that measures angular velocity in X,Y,Z axis and thus an approximation of angular displacement. In our domain, we merely care about the Z axis, or the yaw. To put it simply, you can use a gyroscope to determine how much the car has rotated. You have to instantiate an Gyroscope in order to be able to read its data. You should do this before your void setup()
function in order for the instance variable to be globally accessible in your sketch and after the include statements. As an argument, you should provide the gyroscope's offset, which is the average raw value it returns when being still. This value is different for each gyroscope. If no argument is passed the default will be applied, which is set by the Gyroscope::DEFAULT_GYRO_OFFSET constant in Gyroscope.cpp. You should probably determine your own value, using the calibrate
function and then supplying it as an argument in the above constructor. If the gyroscope has not been calibrated with an appropriate value, then you will notice its output being unstable and decreasing rapidly, despite it not being moved. Refer to FAQ if you do not know how to do that.
Note: You can only instantiate the gyroscope once and connect the gyroscope to SDA and SCL pins! Find out which those are for your board here. If you are using the Smartcar shield, with the GY-50 gyroscope attached to it, then the connections are already taken care of.
Example
#include <Smartcar.h>
Gyroscope gyro;
void setup(){
/* put your setup code here to run once */
}
void loop(){
/* your main loop code here to run repeatedly */
}
Always use this function in order to initialize the gyroscope and establish the connection to it. Don't forget that you can attach attach just one gyroscope and that only on the appropriate pins. Keep in mind that you do not necessarily have to do this during setup()
. Finally, it is advised (but not necessary) that you give it some time to set up the connection and get ready, with a delay of around 1.5 seconds.
Example
void setup() {
gyro.attach(); //initializes the gyroscope
delay(1500); //wait for it to get ready
}
Initiate a measurement by the Gyroscope. It is suggested to call this function every time you want to start measuring the angular displacement (rotation) of the smartcar. Remember that you always have to first attach()
the Gyroscope before you start measuring. Generally you should avoid measuring angular displacement over long periods of time, due to errors accumulating in the result, so it is advised (but not necessary) to begin()
every time you want to initiate a measurement. As an argument, you can optionally provide the sampling period in milliseconds, in other words how often will the gyroscope readings be updated. If you do not know what you are doing, you should leave the argument empty, which will set the sampling period to a default number, that is specified in the Gyroscope::DEFAULT_GYRO_SAMPLING constant, found in Gyroscope.cpp.
Example
#include <Smartcar.h>
Gyroscope gyro;
void setup() {
gyro.attach(); //initializes the gyroscope
delay(1500); //wait for it to get ready
gyro.begin(); //start measuring angular displacement NOW with the default sampling period
//gyro.begin(60); //start measuring angular displacement with a sampling period of 60 milliseconds
}
void loop() {
/* your main loop code here to run repeatedly */
}
Always have this function preferably in your main loop()
or being accessed as frequently as possible, in order to get the latest measurements from the gyroscope.
Example
#include <Smartcar.h>
Gyroscope gyro;
void setup() {
gyro.attach(); //initializes the gyroscope
delay(1500); //wait for it to get ready
gyro.begin(); //start measuring angular displacement NOW
}
void loop() {
gyro.update();
}
Get the current angular displacement, in degrees, since the beginning of the measurement in the range of [0,360). By the default setup, clockwise movement increases the degree count and counter-clockwise decreases them. If you have different set up (orientation of gyroscope) you will have to adapt accordingly. This method will measure the angular displacement (rotation) since the last begin()
. If begin()
has been omitted, you will be receiving 0
as result, regardless of rotation. Keep in mind that the longer time has passed since the last begin()
the less the accurate the results will be, due to error accumulation. You can use begin()
as often as you must in order to minimize the drift caused from errors. Always remember to use update()
in your main loop along with this method.
Example
#include <Smartcar.h>
Gyroscope gyro;
void setup() {
Serial.begin(9600);
gyro.attach(); //initializes the gyroscope
delay(1500); //wait for it to get ready
gyro.begin(); //start measuring angular displacement NOW
}
void loop() {
gyro.update();
Serial.println(gyro.getAngularDisplacement());
delay(30);
}
Gets a good representation of the gyroscope's offset. This is the average value that the gyroscope returns while it is standing still. It is the equivalent of noise and therefore has to be ignored, so to conduct useful measurements. As an argument, you can optionally provide the amount of sample measurements you want to conduct, in order to derive an average value from them. If no argument is provided, then the default amount of measurements, which is 100
, will be used.
Example
#include <Smartcar.h>
Gyroscope gyro;
void setup() {
gyro.attach();
Serial.begin(9600);
delay(1500);
gyro.begin();
Serial.println("Calibrating gyroscope, this might take some seconds");
unsigned int offset = gyro.calibrate();
Serial.print("This gyro's offset value is: ");
}
void loop() {
}
The constructor of a Car instance. The Car class represents programmatically the Smartcar and contains the core functionality of the library. You have to instantiate a Car in order to be able to control it. You should do this before your void setup()
function in order for the instance variable to be globally accessible in your sketch and after the include statements.
There are two ways to instantiate a car. The first one, takes optionally at most one argument, that indicates the shield's orientation. You should only care about this, if you have placed the shield NOT in the intended way, where motors that were supposed to be on the right, are found on the left and vice versa. If you are using the default setup of the platform (i.e. the Smartcar, with a Smartcar shield properly aligned) you do not need to use any argument.
Note: You should instantiate the Car class only once.
Note: The constructor can optionally receive one argument, specifying the orientation of the shield and whether the terminals intended for the right motors are connected to the right and the left to the left. If you have followed the default setup, you do not need to supply any argument. In the opposite case, you will need to supply INVERTED
as an argument to the constructor.
Note: If you are going to utilize sensors to enrich the car's movement (i.e. go
, rotate
or cruise control) then you must instantiate the Car class after the various sensors, before the setup()
.
Example
#include <Smartcar.h>
//here you should instantiate sensors
Car car;
//Car car(INVERTED); //could have also been used like this if you want to use inverted orientation
void setup(){
// put your setup code here, to run once:
}
void loop(){
// put your main code here, to run repeatedly:
}
The constructor of a Car instance. The Car class represents programmatically the Smartcar and contains the core functionality of the library. You have to instantiate a Car in order to be able to control it. You should do this before your void setup()
function in order for the instance variable to be globally accessible in your sketch and after the include statements.
There are two ways to instantiate a car. This one, which takes exactly two arguments, indicates that the default Smartcar setup is not used. Instead, a separate motor is used for steering, either a Servo motor or a DC motor (found usually in cheap RC cars). Same goes for throttling. An ESC (Electronic Speed Controller) can be used, or a separate brushed DC motor or even the motors controlled through the Smartcar shield. Using this constructor, means that you can be totally independent from motors that are connected on the shield and that you can adopt this library for different miniature vehicles, with fundamentally different setup.
Note: You should instantiate the Car class only once.
Note: If you are using a car, that utilizes such a setup, therefore not using any motor connected to the shield (if using the shield at all), then please use the method useServo(unsigned short pin)
in order to provide the servo argument and the method useESC(unsigned short pin)
in order to provide the esc argument.
Note: If you are going to utilize sensors to enrich the car's movement (i.e. go
, rotate
or cruise control) then you must instantiate the Car class after the various sensors, before the setup()
.
Example
- RC car with Servo motor for steering and ESC for throttling
#include <Smartcar.h>
//here you should instantiate sensors
Car car(useServo(5), useESC(6)); //initialize the car, that uses a servo motor for steering that is attached to pin 5 and an ESC attached to pin 6
void setup(){
// put your setup code here, to run once:
}
void loop(){
// put your main code here, to run repeatedly:
}
- RC car with Servo motor for steering and the default motors, attached to (either side of) the Smartcar shield, for throttling
Car car(useServo(4), useShieldMotors()); //initialize the car, that uses a servo motor for steering that is attached to pin 4 and the standard brushed DC motors that are attached to the Smartcar shield
If you notice that the throttle speed is constantly at maximum, then the problem is that the timers for controlling the servo motor are disabling the PWM output at pin 9, which is connected to the motors on the "LEFT" side of the shield. If that is the case, you should connect your throttle motors to the "RIGHT" side of the shield, using the constructor below.
Car car(useServo(4), useDCMotor(RIGHT));
- RC car with DC motor for (Ackerman) steering, attached to the left side of the Smartcar shield some other DC motor(s), attached to the right side of the Smartcar shield, for throttling
Car car(useDCMotor(LEFT), useDCMotor(RIGHT)); //initialize the car, that uses a dc motor for steering that is attached to the left side motors and another dc motor for throttling, attached to the right
- RC car with DC motor for (Ackerman) steering and throttling, attached to arbitrary pins
Car car(useDCMotor(3,4,5), useDCMotor(8,7,6)); //initialize the car, that uses a dc motor for steering that is attached to pins 3,4 (direction pins) and 5 (PWM enable pin for speed) and another dc motor to pins 8,7 (direction) and 6 (PWM) for throttling
- RC car with Servo motor for steering and brushed DC motor for throttling, attached to arbitrary pins
Car car(useServo(4), useDCMotor(8,7,6)); //initialize the car, that uses a servo motor for steering that is attached to pin 4 and a brushed dc motor to pins 8,7 (direction) and 6 (PWM) for throttling
Use this constructor in order to initialize the Car with two generic brushed DC motors, using a differential way of steering, connected at arbitrary pins. You can use this if you do not have the Smartcar shield or do not want to use the default motor setup. Utilize the helper functions as demonstrated below.
unsigned short LEFT_MOTOR[] = {8, 9, 6}; // When pin 8 is set HIGH the motor shall spin "forward". Pin 6 is a PWM that controls the speed
unsigned short RIGHT_MOTOR[] = {11, 10, 5};
Car wheelchair(useShieldMotors(LEFT_MOTOR, RIGHT_MOTOR)); //Left-hand side motor, Right-hand side motor
Always use one of the above functions in the void setup()
in order to initialize the car. This function initializes the motor pins and allows the Car to use some additional sensors in order to provide enhanced functionality, such as turning according to specified degrees or travelling specified distances.
Note: The method can optionally receive up to three arguments, depending on the functionality you want to achieve. If you just want to move the car manually around, then you do not need to provide any arguments. If you want the car to rotate at specified degrees (using rotate(int degrees)
), you should provide a gyroscope (or something else that is a Heading sensor, such as the MPU6050) as an argument and/or if you want the car to move at specified distances and be able to be in cruise control mode, you should attach one or two encoders. If you want the full functionality, provide all three arguments (recommended).
Important: Do not worry about the "scary looking" C++ references (&), as you do not have to actually use them. Just instantiate the sensor(s) the provide them as arguments to the begin()
function
Example
- The Smartcar basic setup
Car car;
//more code here
car.begin()
- A Smartcar that is able to rotate
Gyroscope gyro;
Car car;
//more code here
car.begin(gyro); //car that is able to rotate
- A Smartcar that is able to go to specific distances and do basic cruise control
Odometer encoderRight;
Car car;
//more code here
car.begin(encoderRight);
- A Smartcar that is able to go to specific distances and do basic cruise control
Odometer encoderRight, encoderLeft;
Car car;
//more code here
car.begin(encoderRight, encoderLeft);
- A Smartcar that is able to rotate, go to specific distances and do basic cruise control
Odometer encoderRight;
Gyroscope gyro;
Car car;
//more code here
car.begin(encoderRight, gyro);
- A Smartcar that is able to rotate, go to specific distances and do cruise control (recommended)
Odometer encoderRight, encoderLeft;
Gyroscope gyro;
Car car;
//more code here
car.begin(encoderRight, encoderLeft, gyro);
Set the speed that the car will drive. The argument you provide, is in reality how much you want to deviate from the "neutral" (immobilized) state. The method will receive two kind of arguments, depending on whether we are on the cruise control mode or not. If we are on cruise control, then its input is the actual (ground) speed of the car, in meters/sec. The car will try to maintain that speed, regardless of how heavy it is, how charged the batteries are or other disturbances. If we are not on cruise control, then the input corresponds to the signal that is fed to the motors. In that case, the range is from -100 (full speed backward) to 100 (full speed forward), with 0 immobilizing the car. Essentially, in this mode, the argument stands for the percentage of the full speed that is applied to the motors. (e.g. speed 50, means half of the full speed forward)
In both modes, the sign of the argument implies the direction that the car will move, with positive making the car move forward and negative, backward. Furthermore, if an out of range value is supplied, either too large or too small, it will be constrained to the maximum and minimum allowed values respectively. You can find the maximum and minimum allowed speed for the cruise control mode in Car.cpp and particularly the values MAX_BACK_CRUISE_SPEED
and MAX_FRONT_CRUISE_SPEED
.
Example
If cruise control mode is OFF: Set the speed to 80, moving forward
car.setSpeed(80);
If cruise control mode is OFF: Set the speed to 50, moving backward
car.setSpeed(-50);
If cruise control mode is ON: Set the speed to 1.5 meters per second, moving forward
car.setSpeed(1.5);
If cruise control mode is ON: Set the speed to 2 meters per second, moving backward
car.setSpeed(-2);
Sets the steering wheel angle, that will allow you to steer the car. As an argument, you supply how many degrees you want to deviate from going straight which is at 0 degrees. If you want to turn left, then you should supply an argument with a negative value, while if you want to turn right, then the argument should contain a positive value. The maximum degrees the car can deviate towards the left or right from the middle position which is defined in STRAIGHT_WHEELS
, can be found in MAX_RIGHT_DEGREES
and MAX_LEFT_DEGREES
at Car.cpp.
The vehicle will turn, using the speed that is already set, by setSpeed
. If it is stopped, then setting an angle will not do anything, until setting a speed.
Unless the car is equipped with a servo motor that handles the steering, then it can only simulate the steering, by making the motors on one of its sides, run faster than the other. For example, turning 45 degrees left, means that the left side motors, run at half the set speed than their right side counterparts. In order for the car to turn 90 degrees to the right, the right side's motors are immobilized, while left side motors run at the specified (by setSpeed
) speed. While on cruise control, the vehicle will try to simulate a differential axis while turning, by keeping the total combined rate of rotation of the two motors, stable. When only one encoder is attached, this function might not work as accurately, when the motor with the encoder is immobilized or turning very slow.
Furthermore, when a brushed DC motor is used for steering (a setup commonly found in cheap RC cars) then the supplied value, will only reflect whether the car will turn towards left or right (with negative and positive value respectively) or continue straight (if the value is 0). To put it simply, this means that values of the same sign produce the same behavior in this particular setup, e.g. setAngle(45) with setAngle(90).
To make this particular mode of steering less confusing, you can alternatively use setAngle(LEFT) for turning left, setAngle(RIGHT) for turning right and finally setAngle(STRAIGHT) for remaining straight.
Example
Set the angle to 20 degrees to the left
car.setAngle(-20);
Set the angle to 60 degrees to the right
car.setAngle(60);
Set the angle to 0, in order to move straight
car.setAngle(0);
This function returns the speed (either as the percentage of the full speed or in meters per second, depending on the mode) as supplied by the user. If the user has previously supplied an out of range value in setSpeed(float speed)
then getSpeed()
will return the valid value that was actually used.
Example
car.setSpeed(60); //a valid speed percentage value (in non cruise control mode)
int currentSpeed = car.getSpeed(); //currentSpeed == 60
car.setSpeed(-5550); //an invalid value
currentSpeed = car.getSpeed(); //currentSpeed == -100
Returns the speed currently supplied by the user. If the user has previously supplied an out of range value in setAngle(int degrees)
then getAngle()
will return the valid value that was actually used.
Example
car.setAngle(-25); //a valid value
int currentAngle = car.getAngle(); //currentSpeed == -25
car.setAngle(300); //an invalid value
currentAngle = car.getAngle(); //currentSpeed == 90 or whatever the maximum degrees are
Makes a good effort to stop the car. Due to the absence of dynamic breaking, there is no way to force the wheels to "lock", in order to prevent movement. This method tries to spin the wheels towards the opposite direction for a small amount of time, but does not guarantee that the vehicle will immediately stop, due to inertia. It works more efficiently in cruise control mode, but be aware that this method is blocking the execution of the sketch, since it needs some time (approximately 100 milliseconds) to be executed, in order to make sure that the car is stopped.
Example
car.stop();
Enables the cruise control mode. When the vehicle is in this mode, it tries to maintain its ground speed, as measured by the encoders. The speed will be stable, regardless of the weight of the car, the battery voltage or other disturbances.
Note: The arguments of this method are optional. Insert your own if you wish to optimize the PID controller algorithm. For more information on how to do that, please refer to the FAQ. The default values are specified in Car::DEFAULT_KP, Car::DEFAULT_KI and Car::DEFAULT_KD in Car.cpp.
Note: In order to use cruise control, you must have used at least one (calibrated) encoder in the Car::begin
function.
Note: In order to use cruise control, you must place the updateMotors
method in the loop()
.
Note: When cruise control is enabled the car expects the speed to be set in meters per second (e.g. `setSpeed(1.5) for 1.5 m/s forward) and not in the range of -100 to 100.
Example
Using the default PID values:
car.enableCruiseControl();
Using custom PID values. The first value is the P, the second is the I, the third is the D and the last one is the frequency that the controller algorithm is updated. You do not have to provide all of the arguments. If you want to just define the P,I and D values leaving the frequency as is, then just don't provide a fourth argument.
car.enableCruiseControl(2.0,0,5.0,60);
Returns a boolean value (true or false) depending on whether the cruise control mode is currently enabled or not. For example, you can use it in order to check what mode the car is currently in and adjust the input to setSpeed
accordingly.
car.enableCruiseControl();
//more code here
if (car.cruiseControlEnabled()){
car.setSpeed(1.5); //set the speed to 1.5 meters per second
}else{
car.setSpeed(50); //otherwise set the speed to 50 percent of the total power
}
Disables the cruise control mode and puts the car back into the normal mode, where you can provide a speed directly to the motors. Be aware, that after exiting cruise control mode, the motors will keep spinning at the same speed and direction they used to in the previous mode.
Example
car.disableCruiseControl();
Allows the car to maintain a stable speed by updating the PID controller. If you want to use cruise control, then this must be in the main loop
. If the vehicle is not in cruise control mode, this method will have no effect.
Example
#include <Smartcar.h>
Odometer encoderLeft, encoderRight;
Car car;
void setup(){
encoderLeft.attach(2);
encoderRight.attach(3);
encoderLeft.begin();
encoderRight.begin();
car.begin(encoderLeft, encoderRight); //initialize the car using the encoders
car.enableCruiseControl();
car.setSpeed(0.3); //set the speed to 0.3 meters per second
}
void loop(){
car.updateMotors(); //allows the motors to maintain their speed against external discrepancies
}
Allows the vehicle to travel the specified distance in centimeters. The sign of the value, implies the direction, with positive values causing the car to drive forward for the specified distance and the negative ones, backward. The current speed and angle before starting the method will be logged down and then the car will travel to the specified distance with a predetermined speed. After the completion of go
, the original speed and angle will be restored. The predetermined speed is defined by the constants GO_CRUISE_SPEED and GO_RAW_SPEED in Car.cpp.
Note: In order to use this method, you must have used at least one encoder in the Car::begin
function. If you haven't, this method will have no effect.
Example
Drive 100 centimeters (1 meter) forward
#include <Smartcar.h>
Odometer encoderLeft, encoderRight; //need to know traveled distance
Car car;
void setup() {
Serial.begin(9600);
encoderLeft.attach(2);
encoderRight.attach(3);
encoderLeft.begin();
encoderRight.begin();
car.begin(encoderLeft, encoderRight);
car.go(100);
}
void loop() {
}
Drive 50 centimeters backward
car.go(-50);
Allows the vehicle to rotate around its axis according to the specified degrees. The sign of the value, implies the direction, with positive values causing the car to rotate clockwise and the negative values, counter clockwise. The car rotates by having the motors on one side spinning and the other side immobilized. The current speed and angle before starting the method will be logged down and then the car will rotate to the specified degrees with a predetermined speed. After the completion of rotate
, the original speed and angle will be restored. The predetermined speed is defined by the constants GO_CRUISE_SPEED and GO_RAW_SPEED in Car.cpp.
Note: In order to use this method, you must have used a gyroscope in the Car::begin
function. If you haven't, this method will have no effect.
Note: Do not expect absolute accuracy using this method. Apart of the gyroscope's hardware limitations, even if the car stops spinning the wheels when it has reached the desired degree, due to inertia there can be some drift. You could always try and improve the accuracy of rotation by implementing your own stabilization algorithm.
Example
Rotate 90 degrees clockwise
#include <Smartcar.h>
Gyroscope gyro; //needed to know how much to rotate
Car car;
void setup() {
Serial.begin(9600);
gyro.attach();
gyro.begin();
car.begin(gyro);
car.rotate(90);
}
void loop() {
}
Drive 60 degrees counter clockwise
car.rotate(-60);
Allows the manual control of the speed on each side of the car. Use this method if you want to drive the vehicle in a totally customized manner. The first argument is intended for the left motor and the second argument controls the right motor. The range of the values is the same as with setSpeed
when not in the cruise control mode. The sign of the values implies direction, where positive stands for forward and negative for backward.
Note: This won't work while cruise control is enabled or if you are not using the default setup, in other words to steer the car in a tank-manner, using the DC motors that are connected to the shield.
Example
A movement equivalent to:
car.setSpeed(50);
car.setAngle(0);
car.setMotorSpeed(50,50);
Rotating fast around its axis, counter clockwise, spinning wheels on both sides but opposite directions.
car.setMotorSpeed(-80,80);