My Version of CreaBotLib
Fork of CreaBotLib by
CreaBot.h
- Committer:
- sepp_nepp
- Date:
- 2019-04-18
- Revision:
- 12:530772639065
- Parent:
- 11:5a94af0afa12
File content as of revision 12:530772639065:
/*
* \file CreaBot.h
* \brief File contains Creabot Library.
* CreaBot.h contains the Creabot class, and required enums and structs.
* Imports "mbed.h" and "CreaMot.h"
* Refactorings:
* All variables with suffixes "_cm_sec" = speeds in centimeters per second.
* MotCommand -> BotCommand.
* cmdbot_t -> BotCmdVerb.
* cm -> dist_cm.
* 'motorxx' -> 'wheelxxx': each motor now becomes a 'wheel with a diameter'
* FIFO queue management merged with Creabot Class, now called Queue
* Queue does not use another extra ticker anymore. Instead triggers from Motor End commands.
* Queue is worked through, and Callback is not called, until queue is empty.
*
* @author Tarek Lule based on work of Francois Druilhe, et al.
* @date 21. April 2019
* @see https://os.mbed.com/users/sepp_nepp/code/CreaBotLib/ */
// -------------------- wheel ---------------------------
#ifndef CREABOT_H
#define CREABOT_H
#include "mbed.h"
#include "CreaMot.h"
#define DEFAULT_SPEED_CM_SEC 2.0f /**< Default advancement speed = 2.0cm/sec, was DEFAULT_SPEED */
#define DEPTH_Queue 256 /**< Initialize the depth of the command Queue to 256 */
/** \enum BotCmdVerb
* \brief Robot Commands Verbs, gives the movement direction
* IDLE is no longer supported */
typedef enum {
FORWARD, /**< Advance the robot straight forward */
BACKWARD, /**< Reverse the robot straight backwards */
ROTATE, /**< Rotate around its own axis*/
LEFT, /**< Advance in a left curve */
RIGHT, /**< Advance in a right curve */
BACKLEFT, /**< Reverse in a left curve */
BACKRIGHT /**< Reverse in a right curve */
} BotCmdVerb;
/** \enum TWheelsState
* \brief Possible states of the two wheels of the CreaBot
* Can be ored together */
typedef enum {
LRWHEELS_STOP = 0, /**< All wheels have stopped */
LWHEEL_RUNS = 1, /**< Left wheel runs */
RWHEEL_RUNS = 2, /**< Right wheel runs */
LRWHEELS_RUN = 3, /**< Both wheels run */
} TWheelsState;
/** \struct BotCommand
* \brief Structure of a CreaBot Command.
* The command structure is put into command Queue, and treated by the Queue-Handler */
typedef struct {
BotCmdVerb command; /**< Command type that gives movement direction.*/
float dist_cm; /**< Distance in dist_cm for translational movements .*/
float turn_angle_deg; /**< Angle in degree for rotational movement .*/
void set(BotCmdVerb Acommand, float Aturn_angle_deg, float Adist_cm); /**< Helper; set structure fields to values */
void set(BotCmdVerb Acommand, float Aparam); /**< Helper; set structure fields to values */
} BotCommand;
/** \class Creabot
* \brief Synchronous Control of 2 wheels as part of a two wheel robot
*
* Handles two instances of CreaMot from motor.h library simultaneously.
* Using the set distance between the wheels, allows to run pecise curves.
*
* A first set of movement functions starting with qXXX are using command verbs,
* these commands are queued up in a waiting queue,
* and are executed one by one in programmed order.
*
* A second set of movement functions starting with iXXX are also using command verbs,
* however these commands are executed immediately,
* they override each other if issued while the previous movement still ongoing
*
* A third set of movements functions starting with moveXXXX are provided,
* each function performs one specific movement, also immediately.
* So they also override each other, and collide with queued commands.
*
* A callback is supplied to react to the end of all programmed movements.
*
* Example:
* @code
* // --- Define the Four PINs & Time of movement used for wheel drive -----
* CreaMot wheelLeft(PA_12, PB_0, PB_1, PB_6); // Declare first the 2 wheels (to avoid to have an object with 8 pins to create)
* CreaMot wheelRight(PA_5,PA_4,PA_3,PA_1);
* CreaBot mybot(&wheelLeft, &WheelRight, 10.0f, 13.0f); // insert the wheels and indicate wheel diameter (10cm) and distance between wheels (13cm)
*
* int main() {
*
* mybot.setSpeed(12.5); // Preset speed to 12.5cm/s
* mybot.iMove(FORWARD,10); // Go forward of 10cm
* mybot.iWaitEnd(); // Wait end of Move
* mybot.iMove(ROTATE,90); // Start rotation of 90° around the center between wheels (two wheels running in same direction at same speed)
* mybot.iMove(BACKWARD,40); // Stop immediately the rotation and go backward of 40cm
* mybot.iWaitEnd(); // Wait end of Backward
* mybot.iMoveAndWait(LEFT,60); // Move Left of 60° in circle, center being the left wheel (off). Wait end of move
* mybot.iWaitEnd(); // Not needed, as already waited...
* mybot.iMoveAndWait(RIGHT,45, 33); // Move Right of 45°, center being at 33cm of the right wheel. Right wheel moving slower and on a shorter distance than left one.
* mybot.iMoveAndWait(ROTATE,90);
* mybot.iMove(ROTATE,-90); // Opposite direction.
* mybot.iWaitEnd(6); // with time-out => if after 6s the move is not ended, continue the execution
* mybot.iStop(); // Stop the movement in case it was not ended before ...
*
* // Same with a Queue of command, opposite to the move, receiving a new command will not stop the current execution, but the bot will store it.
* mybot.qMove(FORWARD,10); // Already starting...
* mybot.qMove(BACKWARD,10); // will wait end of previous command to go
* mybot.qMove(ROTATE,120.0);
* mybot.qMove(LEFT, 30, 120);
* mybot.qMove(RIGHT, 25);
* ... // insert other code here that executes while the motors continue to move
* mybot.iWaitEnd(100000); // wait until Queue end... can flush anytime with stopMove...
* mybot.qEmpty(); // before end... Flush the Queue and remove all instructions…
*
* while(1) {
* };
* }
* @endcode
*/
class Creabot {
public:
/** Create a Creabot object with 2 wheels
*
* @param[in] <left> CreaMot object, corresponding to left wheel of the Creabot
* @param[in] <right> CreaMot object, corresponding to right wheel of the Creabot
* @param[in] <wheel_diam_cm> Diameter in cm of the wheels (both must be the same diameter)
* @param[in] <bot_diam_cm> Distance cm between center of left wheel and center of right wheel
*/
Creabot(CreaMot *left, CreaMot *right, float wheel_diam_cm, float bot_diam_cm);
/** Property access to wheel state, indicating which of the wheels are moving */
TWheelsState getState() {return wheelsState; };
/** Setup a Callback method. It is called when all programmed movements are finished. */
void setCallBack(void (*Acallback)(int status)) { extCallBack = Acallback; };
/** High level: set bot-speed parameter for all future wheel commands.
* The set speed is used for immediate as well as the queued movements.
* In a curve movement it determines the speed of the outer wheel.
*
* @param[in] <AbotSpeed_cm_sec> requested movement speed */
void setSpeed(float AbotSpeed_cm_sec);
public:
/** High level, queued: move bot according to command and parameter
*
* Composes a BotCommand and appends it to the queue for queued execution.
* Use for commands that need only one parameter: FORWARD, BACKWARD, ROTATE
* For details refer to docu of moveForward(), moveBackward(), moveRotate().
* Preset the speed using setSpeed().
*
* @param[in] <Acommand> Requested movement, of type BotCmdVerb.
* @param[in] <Aparam> Requested amount, defines angle for ROTATE, or distance for FORWARD, BACKWARD.
*/
void qMove(BotCmdVerb Acommand, float Aparam);
/** High level, queued : move bot according to command and parameters
*
* Composes a BotCommand and appends it to the queue for queued execution.
* Use for commands that need two parameters: LEFT, RIGHT, BACKLEFT, BACKRIGHT
* For details refer to docu of moveLeft(), moveRight(), moveBackLeft(), moveBackRight().
*
* Reserves a new command element at the head of the Queue
*
* Adds incoming commands at the head of the ring buffer at position writeIdx,
* If Queue is full, it passes back NULL
* Otherwise Advances the write index once and returns a pointer the next free command struct.
* The caller then must fill the command structure at that position.
* Do not free memory associated to the pointer.
*
* @param[in] <Acommand> Requested movement, of type BotCmdVerb.
* @param[in] <Aturn_angle_deg> Requested angle in degrees.
* @param[in] <Adist_cm> Requested distance in centimeters.
*/
void qMove(BotCmdVerb Acommand, float Aturn_angle_deg, float Adist_cm);
/** Execute and remove the oldest element at the tail of the Queue
*
* If Queue is empty, nothing happens
* Otherwise executes oldest commands from the tail of the ring buffer at position readIdx,
* Then advances the read index once. */
void qExecuteNext();
/** Public access of Queue used Count.
*
* @return <int> the number of commands in the Queue */
int qCount() {return Count;}
/* Immediately end all queue activites, and the motor
* Does not call the external callback handler */
void qStopAll();
private:
/** Empty the Queue.
* Since the ring buffer is static, it suffice to set all pointers to 0,
* Commands are left in memory. */
void qReset() { qBlock = true; readIdx=writeIdx=Count=0; qBlock = false; qCollide = false;};
/** Check if Queue is full.
*
* Space is limited to DEPTH_Queue=256 BotCommand elements.
* If in doubt it should be checked before trying to add new elements
* @return <bool> True if Queue is full*/
bool qIsFull() {return Count>=DEPTH_Queue;}
/** Check if Queue is empty.
*
* Should be checked before trying to get new elements
* @return <bool> True if Queue is empty*/
bool qIsEmpty() {return Count<=0;}
/** 256 elements deep Command Queue, to put BotCommand in a queue.
* Stores all BotCommand in this static 256 array used as a circular ring buffer. */
BotCommand cmd[DEPTH_Queue]; /**< Actual static Queue array where all elements reside. */
int writeIdx; /**< Index in Queue array where to put the next new element to. */
int readIdx; /**< Index in Queue array where to get the oldest element from. */
int Count; /**< Counts and keeps track of the number of elements in array used. */
bool qBlock; /**< Blocks read access while a write is ongoing. */
bool qCollide;/**< Indicates a colliding access to the queue. */
public:
/** High level, immediate: move bot and wait for movement end.
* Simple wrapper for iMove() and iWaitEnd().
* Refer to those methods for further docs.
*/
void iMoveAndWait(BotCmdVerb Acommand, float Aparam);
/** High level, immediate: move bot and wait for movement end.
* Simple wrapper for iMove() and iWaitEnd().
* Refer to those methods for further docs.
*/
void iMoveAndWait(BotCmdVerb Acommand, float Aturn_angle_deg, float Adist_cm);
/** High level, immediate: move bot according to command and parameter
* Composes a BotCommand and passes it to executeCommand().
* Use for commands that need only one parameter: FORWARD, BACKWARD, ROTATE
* For details refer to docu of moveForward(), moveBackward(), moveRotate().
* Preset the speed using setSpeed().
* Warning: Collides with queued commands.
*
* @param[in] <Acommand> Requested movement, of type BotCmdVerb.
* @param[in] <Aparam> Requested amount, defines angle for ROTATE, or distance for FORWARD, BACKWARD.
*/
void iMove(BotCmdVerb Acommand, float Aparam);
/** High level, immediate: move bot according to command and parameters
* Composes a BotCommand and passes it to executeCommand().
* Use for commands that need two parameters: LEFT, RIGHT, BACKLEFT, BACKRIGHT
* For details refer to docu of moveLeft(), moveRight(), moveBackLeft(), moveBackRight().
* Preset the speed using setSpeed().
* Warning: Collides with queued commands.
*
* @param[in] <Acommand> Requested movement, of type BotCmdVerb.
* @param[in] <Aturn_angle_deg> Requested angle in degrees.
* @param[in] <Adist_cm> Requested distance in centimeters.
*/
void iMove(BotCmdVerb Acommand, float Aturn_angle_deg, float Adist_cm);
/** High level, immediate: move bot according to prefilled command structures.
* Recommended to use iMove() methods to fill the command structure correctly.
* Branches to the moveXXXX() methods. For details see docs for those methods.
* Preset the speed using setSpeed().
* Warning: Collides with queued commands if called individually.
*
* @param[in] <*cmd> Pointer to type BotCommand, the prefilled command structure,
*/
void iExeCommand(BotCommand *cmd);
/** High Level: spends all time with waits,
* returns only once wheelState = LRWHEELS_STOP,
* not recommended due its blocking behavior */
void iWaitEnd();
/** High Level: spends all time with waits,
* returns only once wheelState = LRWHEELS_STOP,
* but waits no more than max_wait_ms milli seconds.
* Not recommended due its blocking behavior */
void iWaitEnd(uint32_t max_wait_ms); // time-out
/** High level, immediate: Both wheels get stopped, and turned off
* updates the wheel state wheelsState, does not call the callback handler!
* It does not empty the queue.
* Adding new elements into the queue after calling iStop()
* Would continue using all commands still in the queue. */
void iStop();
public:
/** Mid level, immediate: advance bot straight forward for a given distance,
* Preset the speed using setSpeed().
* Warning: Collides with queued commands.
*/
void moveForward(float dist_cm);
/** Mid level, immediate: reverse bot straight backwards for a given distance,
* Preset the speed using setSpeed().
* Warning: Collides with queued commands.
*/
void moveBackward(float dist_cm);
/* Mid level, immediate: turn bot forward right,
* around a radius twice the bot size ,
* Same as moveRight(turn_angle_deg, 0);
* Preset the speed using setSpeed().
* Warning: Collides with queued commands.
*/
void moveRight(float turn_angle_deg);
/** Mid level, immediate: turn bot forward right,
* around a radius that is center_cm away from the right wheel,
* Preset the speed using setSpeed().
* Warning: Collides with queued commands.
*/
void moveRight(float turn_angle_deg, float center_cm);
/** Mid level, immediate: turn bot forward left,
* around a radius twice the bot size,
* Same as moveLeft(turn_angle_deg, 0);
* Preset the speed using setSpeed().
* Warning: Collides with queued commands.
*/
void moveLeft(float turn_angle_deg);
/** Mid level, immediate: turn bot forward left,
* around a radius that is center_cm away from the left wheel,
* Preset the speed using setSpeed().
* Warning: Collides with queued commands.
*/
void moveLeft(float turn_angle_deg, float center_cm);
/* Mid level, immediate: turn bot backwards right,
* around a radius twice the bot size ,
* Same as moveBackRight(turn_angle_deg, 0);
* Preset the speed using setSpeed().
* Warning: Collides with queued commands.
*/
void moveBackRight(float turn_angle_deg);
/** Mid level, immediate: turn bot backwards right,
* around a radius that is center_cm away from the right wheel,
* Preset the speed using setSpeed().
* Warning: Collides with queued commands.
*/
void moveBackRight(float turn_angle_deg, float center_cm);
/** Mid level, immediate: turn bot backwards left,
* around a radius twice the bot size,
* Same as moveBackLeft(turn_angle_deg, 0);
* Preset the speed using setSpeed().
* Warning: Collides with queued commands.
*/
void moveBackLeft(float turn_angle_deg);
/** Mid level, immediate: turn bot backwards left,
around a radius that is center_cm away from the left wheel,
* Preset the speed using setSpeed().
* Warning: Collides with queued commands.
*/
void moveBackLeft(float turn_angle_deg, float center_cm);
/** Mid level, immediate: turn bot on its place for a given angle.
* positive angles turn right, negative angles turn left,
* Preset the speed using setSpeed().
* Warning: Collides with queued commands.
*/
void moveRotate(float turn_angle_deg);
private:
/** Low level, immediate: sets Both wheel speeds */
void wheelsSetSpeed(float mot_speed_cm_sec);
/** Callback when left wheel stops;
* updates the wheel state wheelsState */
void wheelLeftStoppedCB();
/** Callback when right wheel stops.
* updates the wheel state wheelsState */
void wheelRightStoppedCB();
/** Callback when all wheels have stopped.
* updates the wheel state wheelsState, switch off all Phases */
void wheelsAllStoppedCB();
/** Callback function pointer:
* If set non-NULL it is called when All wheels Stopped()*/
void (*extCallBack)(int status);
/** Wheel status register:
* Remembers which of the wheels still run */
TWheelsState wheelsState;
/** geometric properties of the bot */
float botDiameter;
/** Ratio of wheel diameter and bot diameter */
float ratio_wheel_bot;
/** last requested bot speed */
float botSpeed_cm_sec;
/** The two wheels, as pointers to their classes */
CreaMot *wheelLeft;
CreaMot *wheelRight;
};
#endif
