If you use Gitkraken, immediately update to version 8.1 (or later) remove your SSH key from https://gitlab.ai.vub.ac.be/-/profile/keys and generate a new one. SSH keys generated with a vulnerable Gitkraken version are compromised.

Commit 27d53695 authored by Niels Post's avatar Niels Post
Browse files

Update 16-10

- Partly commented
- Movement is now implemented
parent a06d4dcf
......@@ -2,3 +2,4 @@
CMakeListsPrivate.txt
cmake-build-*/
CMakeLists.txt
\ No newline at end of file
......@@ -5,28 +5,60 @@
#include <communication/handler/ActionCommandHandler.hpp>
#include <communication/handler/MeasurementCommandHandler.hpp>
#include <queue>
#include <communication/command/Command.hpp>
#include <communication/command/ReturnCommand.hpp>
class PMSVSettings;
class Command;
class ReturnCommand;
/**
* Controller for the wireless communication aspect of the robot.
*
* This controller continuously checks for received RF24 Commands, parses them, and invokes a \refitem{CommandHandler} for them.
* The controller then transmits the ReturnCode from the handler to the sender of the command
*/
class CommunicationController {
private:
/// The radio used to communicate with the other party
RF24 nrfRadio;
/// A handler for commands in the category GENERAL
GeneralCommandHandler generalCommandHandler;
/// A handler for commands in the category ACTION
ActionCommandHandler actionCommandHandler;
/// A handler for commands in the category MEASUREMENT
MeasurementCommandHandler measurementCommandHandler;
/// Queue of commands that are still to be handled
std::queue<Command> commandQueue;
protected:
void handleCommand(const Command &command);
void sendReturnCommand(const ReturnCommand &command);
/**
* Retrieve the next command from the queue, and handle it.
*/
void handleQueuedCommand();
/**
* Send a ReturnCommand as a result of a Command
* @param returnCommand The returnCommand to send
*/
void sendReturnCommand(const ReturnCommand &returnCommand);
/**
* If data is available on RF24, try to parse the data as command, and add it to the commandQueue
*
* When no data is available, this function returns
* When a command is inproperly formatted, the function automatically transmits the error code BAD_PARAMETERS to the sender
*/
void parseNextCommand();
public:
CommunicationController(const PMSVSettings &settings, MovementController &movementController);
/**
* Check for received messages and handle them.
*/
void update();
......
......@@ -2,54 +2,63 @@
#include <cstdint>
#include <cstdio>
#include <utility>
enum class CommandCategory: uint8_t {
GENERAL,
ACTION,
MOVEMENT
/**
* Available categories for wireless commands
*/
enum class CommandCategory : uint8_t {
GENERAL = 0,
ACTION = 1,
MEASUREMENT = 2
};
enum class GeneralCommand: uint8_t {
SET_COMMUNICATION_ACTIVEMODE
/**
* Commands in the "General" category
*/
enum class GeneralCommand : uint8_t {
SET_COMMUNICATION_ACTIVEMODE = 0
};
enum class ActionCommand: uint8_t {
CANCEL_MOVEMENT,
START_MOVE_STEPS,
START_MOVE_CM,
START_ROTATE_DEGREES,
SET_SPEED
/**
* Commands in the "Action" category
*/
enum class ActionCommand : uint8_t {
CANCEL_MOVEMENT = 0,
START_MOVE_STEPS = 1,
START_MOVE_CM = 2,
START_ROTATE_DEGREES = 3,
SET_SPEED = 4
};
enum class MeasurementCommand: uint8_t {
/**
* Commands in the "Measurement" category
*/
enum class MeasurementCommand : uint8_t {
GET_MEASUREMENT_RAW,
GET_DISTANCE_SENSOR
};
struct CommandData {
uint8_t category_id;
uint8_t command_id;
uint8_t min_param_length;
uint8_t max_param_length;
};
const CommandData commandDetails[] = {
{0,0, 1,1},
{1,0,0,0},
{1,1,2,3},
{1,2,2,3},
{1,3,3,3},
{1,4,1,1},
{2,0,1,2},
{2,1,1,2}
};
struct Command {
/**
* A struct containing a command received by the robot.
*/
struct Command {
/// The category id of the command (see the information sheet)
uint8_t category_id;
/// The command id of the command (see the information sheet)
uint8_t command_id;
/// A value for identifying a specific message.
uint8_t message_id;
uint8_t parameters[30]; // TODO this could be handled better
/// All parameter bytes given for this command
uint8_t parameters[5];
/// The byte count of the parameters stored in "parameters"
size_t parameter_length;
/**
......@@ -77,9 +86,30 @@ struct Command {
* @param len
* @return
*/
static bool validate(const uint8_t * data, const uint8_t & len);
static bool validate(const uint8_t *data, const uint8_t &len);
/**
* Get the minimum and maximum parameter length for a given command.
*
* @param cat The Command Category id
* @param cmd The command ID
* @return A pair of sizes, where first is the minimum size, and second is the maximum size
*/
static std::pair<uint8_t, uint8_t> getParameterLimits(CommandCategory cat, uint8_t cmd);
/**
* Get a 16 bit unsigned integer from the parameters
* @param startIndex Parameter index at which the value starts
* @param msb_first Wether the byte order is msb first (true, default) or lsbyte first(false)
* @return The requested value
*/
uint16_t get_param16(const size_t &startIndex, bool msb_first = true) const;
/**
* Get a 32 bit unsigned integer from the parameters
* @param startIndex Parameter index at which the value starts
* @param msb_first Wether the byte order is msb first (true, default) or lsbyte first(false)
* @return The requested value
*/
uint32_t get_param32(const size_t &startIndex, bool msb_first = true) const;
};
......@@ -3,19 +3,41 @@
#include <cstdint>
#include <cstdio>
/**
* Possible return codes for a wireless command.
*
* ReturnCodes are packed in a \refitem{ReturnCommand} and sent back to the sender.
*/
enum class ReturnCode : uint8_t {
/// The requested action was performed successfully
SUCCESS = 0,
/// The robot cannot handle the command because it was busy with another action
ROBOT_BUSY = 1,
/// Performing the requested action was impossible due to a hardware error
HARDWARE_ERROR = 2,
/// (Only for movement) Performing the requested action failed because a motor was stalling
MOTOR_STALL = 3,
/// The command received by the robot was ill-formatted
BAD_PARAMETERS = 4,
/// A reading was requested for a sensor that doesn't exist
NO_SUCH_SENSOR = 5
};
/**
* A command that is sent by the Robot to respond to a given \refitem{Command}
*/
struct ReturnCommand {
/// The message id of the original Command. This can be used to match a returncommand to a sent command on the other side
uint8_t message_id;
/// ReturnCode for the message
ReturnCode returnCode;
/// (Optional) the data returned from the Command
uint8_t data[4];
/// Size of the data returned
size_t data_size;
/**
* Create a ReturnCommand
*/
ReturnCommand(uint8_t messageId, ReturnCode returnCode, const uint8_t *dat);
};
......@@ -4,10 +4,24 @@
class MovementController;
class ActionCommandHandler: public CommandHandler {
/**
* Handles commands in the category \refitem{CommandCategory::ACTION}
*/
class ActionCommandHandler : public CommandHandler {
/// MovementController, used for performing movement actions
MovementController &movementController;
public:
/**
* Handle an ActionCommand
* @param cmd Command to handle
* @return The result of the action. This should be sent back to the sender of the command
*/
ReturnCommand handle(const Command &cmd) override;
/**
* Construct the ActionCommandHandler
* @param movementController The robot's movementController
*/
ActionCommandHandler(MovementController &movementController);
};
......@@ -3,7 +3,15 @@
#include <communication/command/Command.hpp>
#include <communication/command/ReturnCommand.hpp>
/**
* Interface to handle commands in a specific category
*/
class CommandHandler {
public:
/**
* Handle a Command
* @param cmd Command to handle
* @return The result of the command. This should be sent back to the sender of the command
*/
virtual ReturnCommand handle(const Command &cmd) = 0;
};
#pragma once
#include <communication/handler/CommandHandler.hpp>
class GeneralCommandHandler: public CommandHandler {
/**
* Handles commands in the category \refitem{CommandCategory::GENERAL}
*/
class GeneralCommandHandler : public CommandHandler {
public:
/**
* Handle a General Command
* @param cmd Command to handle
* @return The result of the command. This should be sent back to the sender of the command
*/
ReturnCommand handle(const Command &cmd) override;
};
#pragma once
#include <communication/handler/CommandHandler.hpp>
class MeasurementCommandHandler: public CommandHandler {
/**
* Handles commands in the category \refitem{CommandCategory::MEASUREMENT}
*/
class MeasurementCommandHandler : public CommandHandler {
public:
/**
* Handle a Measurement Command
* @param cmd Command to handle
* @return The result of the command, usually containing a measurement. This should be sent back to the sender of the command
*/
ReturnCommand handle(const Command &cmd) override;
};
#pragma once
#include <cstdint>
#include "AccelStepper.h"
/**
* A boundary used for a single Stepper Motor
*/
class MotorBoundary {
/// Stepper motor object that is used to move the motor
AccelStepper stepper;
public:
/**
* Create a MotorBoundary
*
* The constructor automatically creates the underlying AccelStepper object
* @param enable_pin Pin number connected to the enable pin of the stepper driver
* @param step_pin Pin number connected to the step pin of the stepper driver
* @param dir_pin Pin number connected to the dir pin of the stepper driver
* @param steps_per_mm The number of steps a motor steps fo moving a millimeter
*/
MotorBoundary(uint8_t enable_pin, uint8_t step_pin, uint8_t dir_pin, uint8_t steps_per_mm);
/**
* Start a movement of a specified amount of steps
*
* Note that the method returns before the movement is started. Movement is continued in the update() method.
* @param steps The amount of steps to move
* @return True if the starting of the movement was successful
*/
bool startMoveSteps(uint16_t steps);
/**
* Stop all movement currently being processed
*/
void halt();
/**
* Update the movement currently being processed.
* Usually steps the motor at least once
*/
void update();
};
\ No newline at end of file
#pragma once
#include <cstdint>
class MotorController {
public:
bool startMoveSteps(uint16_t steps) {
return false;
}
void halt() {
}
};
\ No newline at end of file
#pragma once
#include <movement/MotorController.hpp>
#include <movement/MotorBoundary.hpp>
#include <communication/command/ReturnCommand.hpp>
class PMSVSettings;
class MovementController {
MotorController leftMotor;
MotorController rightMotor;
MotorBoundary leftMotor;
MotorBoundary rightMotor;
public:
ReturnCode setSpeed(uint16_t speed);
ReturnCode setDirection(bool dir);
ReturnCode startMoveCM(uint16_t cm);
ReturnCode startMoveSteps(uint16_t steps);
ReturnCode startRotateDegrees(uint16_t degrees, bool direction);
ReturnCode halt();
void update();
MovementController(const PMSVSettings &pmsvSettings );
};
\ No newline at end of file
......@@ -7,11 +7,11 @@
class PMSVRobot {
MovementController movementController;
CommunicationController communicationController;
PMSVSettings settings;
const PMSVSettings &settings;
public:
PMSVRobot(PMSVSettings &settings);
PMSVRobot(const PMSVSettings &settings);
void update();
};
......
......@@ -2,6 +2,19 @@
#include <RF24.h>
struct StepperSettings {
uint8_t pin_enable;
uint8_t pin_step;
uint8_t pin_dir;
uint8_t steps_per_mm;
StepperSettings(uint8_t pinEnable, uint8_t pinStep, uint8_t pinDir, uint8_t stepsPerMm) : pin_enable(pinEnable),
pin_step(pinStep),
pin_dir(pinDir),
steps_per_mm(
stepsPerMm) {}
};
class PMSVSettings {
public:
......@@ -29,11 +42,14 @@ public:
uint8_t nrf_pin_ce;
uint8_t nrf_pin_csn;
StepperSettings leftMotor;
StepperSettings rightMotor;
PMSVSettings(uint16_t stepsPerCm, uint16_t stepsPerDegree, bool leftMotorFwd, bool rightMotorFwd, uint16_t minSpeed,
uint16_t maxSpeed, rf24_pa_dbm_e nrfPalevel, uint8_t nrfChannel, uint64_t nrfReadingpipe,
uint64_t nrfWritingpipe, bool nrfEnableDynamicpayloads, size_t nrfPacketSize, bool nrfEnableAutoack,
uint8_t nrfPinCe, uint8_t nrfPinCsn)
uint8_t nrfPinCe, uint8_t nrfPinCsn, StepperSettings leftMotor, StepperSettings rightMotor)
: steps_per_cm(stepsPerCm),
steps_per_degree(
stepsPerDegree),
......@@ -53,7 +69,7 @@ public:
nrfPacketSize),
nrf_enable_autoack(
nrfEnableAutoack),
nrf_pin_ce(nrfPinCe), nrf_pin_csn(nrfPinCsn) {}
nrf_pin_ce(nrfPinCe), nrf_pin_csn(nrfPinCsn), leftMotor(leftMotor), rightMotor(rightMotor) {}
};
......@@ -12,4 +12,7 @@
platform = atmelsam
board = due
framework = arduino
lib_deps = tmrh20/RF24@^1.3.9
lib_deps =
tmrh20/RF24@^1.3.9
teemuatlut/TMCStepper @ ^0.7.1
pavel/AccelStepper@^1.61.0
......@@ -10,7 +10,8 @@
CommunicationController::CommunicationController(const PMSVSettings &settings, MovementController &movementController)
: nrfRadio(settings.nrf_pin_ce,
settings.nrf_pin_csn), generalCommandHandler(), actionCommandHandler(movementController), measurementCommandHandler(){
settings.nrf_pin_csn), generalCommandHandler(), actionCommandHandler(movementController),
measurementCommandHandler() {
nrfRadio.begin();
nrfRadio.setPALevel(settings.nrf_palevel);
......@@ -37,18 +38,14 @@ CommunicationController::CommunicationController(const PMSVSettings &settings, M
void CommunicationController::update() {
nrfRadio.startListening();
if (nrfRadio.available()) {
parseNextCommand();
}
if (!commandQueue.empty()) {
handleCommand(commandQueue.front());
commandQueue.pop();
}
handleQueuedCommand();
}
void CommunicationController::parseNextCommand() {
if (nrfRadio.available()) {
auto size = nrfRadio.getDynamicPayloadSize();
uint8_t data[size];
nrfRadio.read(data, size);
......@@ -59,9 +56,12 @@ void CommunicationController::parseNextCommand() {
} else {
Serial.println("Invalid command received");
}
}
}
void CommunicationController::handleCommand(const Command &command) {
void CommunicationController::handleQueuedCommand() {
if (!commandQueue.empty()) {
const auto &command = commandQueue.front();
switch (static_cast<CommandCategory>(command.category_id)) {
case CommandCategory::GENERAL:
sendReturnCommand(generalCommandHandler.handle(command));
......@@ -69,16 +69,17 @@ void CommunicationController::handleCommand(const Command &command) {
case CommandCategory::ACTION:
sendReturnCommand(actionCommandHandler.handle(command));
break;
case CommandCategory::MOVEMENT:
case CommandCategory::MEASUREMENT:
sendReturnCommand(measurementCommandHandler.handle(command));
break;
}
commandQueue.pop();
}
}
void CommunicationController::sendReturnCommand(const ReturnCommand &command) {
void CommunicationController::sendReturnCommand(const ReturnCommand &returnCommand) {
nrfRadio.stopListening();
if(!nrfRadio.write(&command, 6) )
{
if (!nrfRadio.write(&returnCommand, 6)) {
Serial.println("Sending return failed");
}
}
......@@ -11,11 +11,11 @@ Command Command::parse(uint8_t *data, const uint8_t &len) {
c.parameter_length = len - 2;
for(size_t i = 0; i < c.parameter_length; i++) {
c.parameters[i] = data[i+2];
for (size_t i = 0; i < c.parameter_length; i++) {
c.parameters[i] = data[i + 2];
}
for(size_t i = c.parameter_length; i < 30; i++) {
for (size_t i = c.parameter_length; i < 30; i++) {
c.parameters[i] = 0;
}
......@@ -23,7 +23,7 @@ Command Command::parse(uint8_t *data, const uint8_t &len) {
}
bool Command::validate(const uint8_t *data, const uint8_t &len) {
if(len < 2) {
if (len < 2) {
Serial.println("toto short");
return false;
}
......@@ -32,16 +32,14 @@ bool Command::validate(const uint8_t *data, const uint8_t &len) {
uint8_t cmd = data[0] & 0x1Fu;
uint8_t param_length = len - 2;
for(const CommandData & details : commandDetails) {
if(details.category_id == cat &&
details.command_id == cmd) {
Serial.println("Command found");
if(param_length >= details.min_param_length && param_length <= details.max_param_length) {
auto sizes = Command::getParameterLimits(static_cast<CommandCategory>(cat), cmd);
if (param_length >= sizes.first && param_length <= sizes.second) {
Serial.println("Good boy command");
return true;
}
}
}
return false;
}
......@@ -50,7 +48,7 @@ Command::Command(uint8_t categoryId, uint8_t commandId, uint8_t messageId) : cat
message_id(messageId) {}
uint16_t Command::get_param16(const size_t &startIndex, bool msb_first) const {
if(msb_first) {
if (msb_first) {
return (parameters[0] << 8) | parameters[1];
} else {
return (parameters[1] << 8) | parameters[0];
......@@ -58,9 +56,48 @@ uint16_t Command::get_param16(const size_t &startIndex, bool msb_first) const {
}
uint32_t Command::get_param32(const size_t &startIndex, bool msb_first) const {
if(msb_first) {