You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
509 lines
17 KiB
509 lines
17 KiB
/**
|
|
* \file
|
|
* \brief Implements line following of the robot
|
|
* \author Erich Styger, erich.styger@hslu.ch
|
|
* \license SPDX-License-Identifier: BSD-3-Clause
|
|
* This is the implementation of the line following.
|
|
*/
|
|
|
|
#include "platform.h"
|
|
#if PL_CONFIG_APP_LINE_FOLLOWING || PL_CONFIG_APP_LINE_MAZE
|
|
#include "LineFollow.h"
|
|
#include "McuRTOS.h"
|
|
#include "McuShell.h"
|
|
#include "Shell.h"
|
|
#include "Motor.h"
|
|
#include "Reflectance.h"
|
|
#include "McuWait.h"
|
|
#include "McuUtility.h"
|
|
#include "Pid.h"
|
|
#include "Turn.h"
|
|
#include "Maze.h"
|
|
#include "Shell.h"
|
|
#if PL_CONFIG_USE_BUZZER
|
|
#include "Buzzer.h"
|
|
#endif
|
|
#if PL_CONFIG_USE_DRIVE
|
|
#include "Drive.h"
|
|
#endif
|
|
|
|
#define LINE_FOLLOW_FW (1) /* test setting to do forward line following */
|
|
#define LINE_DO_DEBUG_WAIT (0) /* if set to one, it will add some debug wait to stop the engines */
|
|
#if LINE_DO_DEBUG_WAIT
|
|
#define LINE_DO_DEBUG_WAIT_MS (500) /* waiting time in milliseconds */
|
|
#endif
|
|
|
|
#if LINE_DO_DEBUG_WAIT /* debug mode */
|
|
static void DebugStopAndWait(void) {
|
|
static volatile bool waitHere = FALSE;
|
|
|
|
TURN_Turn(TURN_STOP, NULL);
|
|
vTaskDelay(LINE_DO_DEBUG_WAIT_MS);
|
|
waitHere = TRUE;
|
|
}
|
|
#endif
|
|
|
|
#define PL_TURN_ON_FINISH (0) /* temporary only */
|
|
|
|
typedef enum {
|
|
LINE_SPEED_LOW,
|
|
LINE_SPEED_MEDIUM,
|
|
LINE_SPEED_HIGH
|
|
} LINE_SpeedMode;
|
|
|
|
static LINE_SpeedMode lineFollowSpeed = LINE_SPEED_LOW; /* set value in LineTask()! */
|
|
|
|
typedef enum {
|
|
STATE_IDLE, /* idle, not doing anything */
|
|
STATE_FOLLOW_SEGMENT, /* line following segment, going forward */
|
|
#if PL_CONFIG_APP_LINE_MAZE
|
|
STATE_TURN, /* reached an intersection, turning around */
|
|
STATE_FINISHED, /* reached finish area */
|
|
#endif
|
|
STATE_STOP /* stop the engines */
|
|
} StateType;
|
|
|
|
/* task notification bits */
|
|
#define LF_START_FOLLOWING (1<<0) /* start line following */
|
|
#define LF_STOP_FOLLOWING (1<<1) /* stop line following */
|
|
static TaskHandle_t LFTaskHandle;
|
|
|
|
static volatile StateType LF_currState = STATE_IDLE;
|
|
#if PL_CONFIG_APP_LINE_MAZE
|
|
static uint8_t LF_solvedIdx = 0; /* index to iterate through the solution, zero is the solution start index */
|
|
#endif
|
|
|
|
void LF_StartFollowing(void) {
|
|
(void)xTaskNotify(LFTaskHandle, LF_START_FOLLOWING, eSetBits);
|
|
}
|
|
|
|
void LF_StopFollowing(void) {
|
|
(void)xTaskNotify(LFTaskHandle, LF_STOP_FOLLOWING, eSetBits);
|
|
}
|
|
|
|
void LF_StartStopFollowing(void) {
|
|
if (LF_IsFollowing()) {
|
|
(void)xTaskNotify(LFTaskHandle, LF_STOP_FOLLOWING, eSetBits);
|
|
} else {
|
|
(void)xTaskNotify(LFTaskHandle, LF_START_FOLLOWING, eSetBits);
|
|
}
|
|
}
|
|
|
|
/*!
|
|
* \brief follows a line segment.
|
|
* \return Returns TRUE if still on line segment
|
|
*/
|
|
bool LF_FollowSegment(REF_LineKindMode mode, bool forward) {
|
|
uint16_t currLine;
|
|
bool onLine;
|
|
REF_LineKind currLineKind;
|
|
|
|
REF_GetLineValue(&onLine);
|
|
currLineKind = REF_GetLineKind(mode);
|
|
if (currLineKind!=REF_LINE_STRAIGHT) {
|
|
SHELL_SendString((unsigned char*)"LF_FollowSegment: ");
|
|
SHELL_SendString(REF_LineKindStr(currLineKind));
|
|
SHELL_SendString((unsigned char*)"\r\n");
|
|
}
|
|
#if PL_CONFIG_APP_LINE_FOLLOWING
|
|
if (currLineKind==REF_LINE_STRAIGHT || currLineKind!=REF_LINE_NONE) { /* as long we have something: follow line */
|
|
#elif PL_CONFIG_APP_LINE_MAZE
|
|
if (currLineKind==REF_LINE_STRAIGHT) { /* as long we have a line: follow line */
|
|
#endif
|
|
currLine = REF_GetLineValue(&onLine);
|
|
PID_Line(currLine, REF_MIDDLE_LINE_VALUE, REF_LineWidth(), forward); /* continue move along the line */
|
|
return TRUE;
|
|
} else { /* stop motors */
|
|
SHELL_SendString((unsigned char*)"LF_FollowSegment stop: ");
|
|
SHELL_SendString(REF_LineKindStr(currLineKind));
|
|
SHELL_SendString((unsigned char*)"\r\n");
|
|
MOT_SetSpeedPercent(MOT_GetMotorHandle(MOT_MOTOR_LEFT), 0);
|
|
MOT_SetSpeedPercent(MOT_GetMotorHandle(MOT_MOTOR_RIGHT), 0);
|
|
vTaskDelay(pdMS_TO_TICKS(20)); /* give time for PWM to change */
|
|
return FALSE; /* intersection/change of direction or not on line any more */
|
|
}
|
|
}
|
|
|
|
/*!
|
|
* \brief Move from outside onto a line/segment to follow it.
|
|
* \return Returns TRUE if still on line segment
|
|
*/
|
|
bool LF_MoveOnSegment(bool turningLeft) {
|
|
uint16_t currLine, targetLine;
|
|
int tmp;
|
|
bool onLine;
|
|
REF_LineKind currLineKind;
|
|
uint16_t lineWidth;
|
|
|
|
currLine = REF_GetLineValue(&onLine);
|
|
currLineKind = REF_GetLineKind(REF_LINE_KIND_MODE_ALL);
|
|
lineWidth = REF_LineWidth();
|
|
if (lineWidth>2000) {
|
|
if (turningLeft) {
|
|
tmp = REF_MIDDLE_LINE_VALUE+lineWidth;
|
|
} else {
|
|
tmp = REF_MIDDLE_LINE_VALUE-lineWidth;
|
|
}
|
|
if (tmp<0) {
|
|
tmp = 0;
|
|
}
|
|
if (tmp>REF_MAX_LINE_VALUE) {
|
|
tmp = REF_MAX_LINE_VALUE;
|
|
}
|
|
targetLine = tmp;
|
|
} else {
|
|
targetLine = REF_MIDDLE_LINE_VALUE;
|
|
}
|
|
if (currLineKind==REF_LINE_STRAIGHT || currLineKind==REF_LINE_LEFT || currLineKind==REF_LINE_RIGHT || currLineKind==REF_LINE_FULL) {
|
|
PID_Line(currLine, targetLine, REF_LineWidth(), TRUE); /* move along the line */
|
|
return TRUE;
|
|
} else {
|
|
return FALSE; /* intersection/change of direction or not on line any more */
|
|
}
|
|
}
|
|
|
|
bool LF_FollowSegmentLinePos(REF_LineKindMode mode, uint16_t setLinePos) {
|
|
uint16_t currLine;
|
|
bool onLine;
|
|
REF_LineKind currLineKind;
|
|
|
|
currLine = REF_GetLineValue(&onLine);
|
|
currLineKind = REF_GetLineKind(mode);
|
|
if (currLineKind==REF_LINE_STRAIGHT) {
|
|
PID_Line(currLine, setLinePos, REF_LineWidth(), TRUE); /* move along the line */
|
|
return TRUE;
|
|
} else {
|
|
return FALSE; /* intersection/change of direction or not on line any more */
|
|
}
|
|
}
|
|
|
|
static void StateMachine(void) {
|
|
for(;;) {
|
|
switch (LF_currState) {
|
|
case STATE_IDLE:
|
|
break;
|
|
case STATE_FOLLOW_SEGMENT:
|
|
#if PL_CONFIG_APP_LINE_MAZE
|
|
if (!LF_FollowSegment(REF_LINE_KIND_MODE_MAZE, LINE_FOLLOW_FW)) {
|
|
TURN_Turn(TURN_STOP, NULL);
|
|
#if LINE_DO_DEBUG_WAIT /* debug mode */
|
|
DebugStopAndWait();
|
|
#endif
|
|
LF_currState = STATE_TURN; /* make turn */
|
|
continue; /* process next step immediately */
|
|
}
|
|
#else
|
|
{
|
|
REF_LineKind currLineKind;
|
|
|
|
if (!LF_FollowSegment(REF_LINE_KIND_MODE_LINE_FOLLOW, LINE_FOLLOW_FW)) {
|
|
SHELL_SendString((unsigned char*)"No segment any more!\r\n");
|
|
currLineKind = REF_GetLineKind(REF_LINE_KIND_MODE_LINE_FOLLOW);
|
|
SHELL_SendString((unsigned char*)"Line: ");
|
|
SHELL_SendString(REF_LineKindStr(currLineKind));
|
|
SHELL_SendString((unsigned char*)"\r\n");
|
|
|
|
SHELL_SendString((unsigned char*)"stop!\r\n");
|
|
LF_currState = STATE_STOP; /* stop if we do not have a line any more */
|
|
}
|
|
}
|
|
#endif
|
|
break;
|
|
|
|
#if PL_CONFIG_APP_LINE_MAZE
|
|
case STATE_TURN:
|
|
if (MAZE_IsSolved()) {
|
|
TURN_Kind turn;
|
|
|
|
turn = MAZE_GetSolvedTurn(&LF_solvedIdx);
|
|
if (turn==TURN_STOP) { /* last turn reached */
|
|
TURN_Turn(turn, NULL);
|
|
#if PL_CONFIG_USE_DRIVE
|
|
(void)DRV_SetMode(DRV_MODE_NONE); /* disable any drive mode */
|
|
#endif
|
|
LF_currState = STATE_STOP;
|
|
SHELL_SendString((unsigned char *)"MAZE: I'm back to the start of maze!\r\n");
|
|
#if PL_CONFIG_USE_BUZZER
|
|
BUZ_PlayTune(1);
|
|
#endif
|
|
MAZE_ClearSolution(); /* clear solution */
|
|
#if PL_TURN_ON_START
|
|
/* turn the robot on start so it is ready to run the maze again */
|
|
#if PL_CONFIG_USE_QUADRATURE
|
|
TURN_Turn(TURN_LEFT180, NULL);
|
|
#else /* not accurate enough without position sensor */
|
|
TURN_Turn(TURN_LEFT90, NULL); /* do turn in two steps */
|
|
TURN_Turn(TURN_LEFT90, NULL);
|
|
#endif
|
|
#endif
|
|
TURN_Turn(TURN_STOP, NULL);
|
|
#if PL_CONFIG_USE_DRIVE
|
|
(void)DRV_SetMode(DRV_MODE_NONE); /* disable any drive mode */
|
|
#endif
|
|
/* now ready to solve maze again */
|
|
SHELL_SendString((unsigned char *)"MAZE: ready to start again!\r\n");
|
|
} else { /* perform turning */
|
|
TURN_Turn(TURN_STEP_LINE_FW_AND_POST_LINE, NULL); /* Step over line */
|
|
TURN_Turn(turn, NULL);
|
|
#if LINE_DO_DEBUG_WAIT /* debug mode */
|
|
DebugStopAndWait();
|
|
#endif
|
|
#if PL_CONFIG_USE_DRIVE
|
|
(void)DRV_SetMode(DRV_MODE_NONE); /* disable any drive mode, so we can do line following (line following is PWM) */
|
|
#endif
|
|
LF_currState = STATE_FOLLOW_SEGMENT;
|
|
continue; /* process next state immediately */
|
|
}
|
|
} else { /* still evaluating maze */
|
|
bool deadEndGoBw = FALSE;
|
|
bool finished = FALSE;
|
|
|
|
if (MAZE_EvaluteTurn(&finished, &deadEndGoBw)==ERR_OK) { /* finished turning */
|
|
#if LINE_DO_DEBUG_WAIT /* debug mode */
|
|
DebugStopAndWait();
|
|
#endif
|
|
#if PL_CONFIG_USE_DRIVE
|
|
(void)DRV_SetMode(DRV_MODE_NONE); /* disable any drive mode */
|
|
#endif
|
|
if (finished) {
|
|
LF_currState = STATE_FINISHED;
|
|
MAZE_SetSolved();
|
|
LF_solvedIdx = 0; /* set index to start of solution */
|
|
#if PL_CONFIG_HAS_BUZZER
|
|
BUZ_PlayTune(BUZ_TUNE_MAZE_DESTINATION);
|
|
#endif
|
|
#if PL_TURN_ON_FINISH
|
|
/* turn the robot */
|
|
#if PL_CONFIG_USE_QUADRATURE
|
|
TURN_Turn(TURN_LEFT180, NULL);
|
|
#else /* not accurate enough without position sensor */
|
|
TURN_Turn(TURN_LEFT90, NULL); /* do turn in two steps */
|
|
TURN_Turn(TURN_LEFT90, NULL);
|
|
#endif
|
|
#endif
|
|
//TURN_Turn(TURN_STOP, NULL);
|
|
#if LINE_DO_DEBUG_WAIT /* debug mode */
|
|
DebugStopAndWait();
|
|
#endif
|
|
#if PL_CONFIG_USE_DRIVE
|
|
(void)DRV_SetMode(DRV_MODE_NONE); /* disable any drive mode */
|
|
#endif
|
|
/* now ready to do line following */
|
|
} else {
|
|
LF_currState = STATE_FOLLOW_SEGMENT;
|
|
}
|
|
} else { /* error case */
|
|
LF_currState = STATE_STOP;
|
|
}
|
|
}
|
|
break;
|
|
#endif
|
|
#if PL_CONFIG_APP_LINE_MAZE
|
|
case STATE_FINISHED:
|
|
SHELL_SendString((unsigned char *)"MAZE: Found maze destination!\r\n");
|
|
#if PL_CONFIG_USE_BUZZER
|
|
BUZ_PlayTune(BUZ_TUNE_MAZE_DESTINATION);
|
|
#endif
|
|
//SHELL_SendString((unsigned char *)"MAZE: Going back to start...\r\n");
|
|
//LF_currState = STATE_FOLLOW_SEGMENT; /* go back to start */
|
|
//MAZE_ClearSolution(); /* clear solution */
|
|
LF_currState = STATE_STOP;
|
|
break;
|
|
#endif
|
|
case STATE_STOP:
|
|
SHELL_SendString((unsigned char *)"Stopped!\r\n");
|
|
(void)DRV_SetMode(DRV_MODE_NONE); /* disable any drive mode */
|
|
TURN_Turn(TURN_STOP, NULL);
|
|
LF_currState = STATE_IDLE;
|
|
break;
|
|
} /* switch */
|
|
break; /* get out of for loop */
|
|
} /* for */
|
|
}
|
|
|
|
bool LF_IsFollowing(void) {
|
|
return LF_currState!=STATE_IDLE;
|
|
}
|
|
|
|
static const unsigned char *LINE_GetSpeedModeString(LINE_SpeedMode mode) {
|
|
switch(mode) {
|
|
case LINE_SPEED_LOW: return (const unsigned char*)"LOW";
|
|
case LINE_SPEED_MEDIUM: return (const unsigned char*)"MEDIUM";
|
|
case LINE_SPEED_HIGH: return (const unsigned char*)"HIGH";
|
|
default: break;
|
|
}
|
|
return (const unsigned char*)"UNKNOWN";
|
|
}
|
|
|
|
static uint8_t LINE_SetSpeed(LINE_SpeedMode speed) {
|
|
uint8_t res;
|
|
PID_Config *lineFwPid, *posLeftPid, *posRightPid;
|
|
|
|
res = PID_GetPIDConfig(PID_CONFIG_LINE_FW, &lineFwPid);
|
|
if (res!=ERR_OK || lineFwPid==NULL) {
|
|
return ERR_FAILED;
|
|
}
|
|
res = PID_GetPIDConfig(PID_CONFIG_POS_LEFT, &posLeftPid);
|
|
if (res!=ERR_OK || posLeftPid==NULL) {
|
|
return ERR_FAILED;
|
|
}
|
|
res = PID_GetPIDConfig(PID_CONFIG_POS_RIGHT, &posRightPid);
|
|
if (res!=ERR_OK || posRightPid==NULL) {
|
|
return ERR_FAILED;
|
|
}
|
|
switch(speed) {
|
|
case LINE_SPEED_LOW:
|
|
lineFwPid->maxSpeedPercent = 15;
|
|
lineFwPid->pFactor100 = 4000;
|
|
lineFwPid->dFactor100 = 500;
|
|
TURN_SetSteps(680, 170, 180);
|
|
posLeftPid->maxSpeedPercent = 30;
|
|
posLeftPid->pFactor100 = 2000;
|
|
posRightPid->pFactor100 = posLeftPid->pFactor100;
|
|
posRightPid->maxSpeedPercent = posLeftPid->maxSpeedPercent;
|
|
break;
|
|
case LINE_SPEED_MEDIUM:
|
|
lineFwPid->maxSpeedPercent = 30; /* 15 => 30 */
|
|
lineFwPid->pFactor100 = 5500;
|
|
lineFwPid->iAntiWindup = 100000;
|
|
TURN_SetSteps(700, 190, 100);
|
|
posLeftPid->maxSpeedPercent = 80;
|
|
posLeftPid->pFactor100 = 2000;
|
|
posRightPid->pFactor100 = posLeftPid->pFactor100;
|
|
posRightPid->maxSpeedPercent = posLeftPid->maxSpeedPercent;
|
|
break;
|
|
case LINE_SPEED_HIGH: /* to be verified */
|
|
lineFwPid->maxSpeedPercent = 40;
|
|
lineFwPid->pFactor100 = 2000;
|
|
lineFwPid->iAntiWindup = 30000;
|
|
TURN_SetSteps(700, 150, 80);
|
|
posLeftPid->maxSpeedPercent = 100;
|
|
posLeftPid->pFactor100 = 2000;
|
|
posRightPid->pFactor100 = posLeftPid->pFactor100;
|
|
posRightPid->maxSpeedPercent = posLeftPid->maxSpeedPercent;
|
|
break;
|
|
default:
|
|
return ERR_FAILED;
|
|
} /* switch */
|
|
lineFollowSpeed = speed;
|
|
SHELL_SendString((unsigned char*)"Changed parameters for speed!\n");
|
|
return ERR_OK;
|
|
}
|
|
|
|
static void LineTask (void *pvParameters) {
|
|
uint32_t notifcationValue;
|
|
BaseType_t notified;
|
|
|
|
(void)pvParameters; /* not used */
|
|
#if PL_SLOWER_SPEED
|
|
(void)LINE_SetSpeed(LINE_SPEED_LOW);
|
|
#else /* check! */
|
|
(void)LINE_SetSpeed(LINE_SPEED_MEDIUM);
|
|
#endif
|
|
for(;;) {
|
|
notified = xTaskNotifyWait(0UL, LF_START_FOLLOWING|LF_STOP_FOLLOWING, ¬ifcationValue, 1); /* check flags, need to wait for one tick */
|
|
if (notified==pdTRUE) { /* received notification */
|
|
if (notifcationValue&LF_START_FOLLOWING) {
|
|
if (REF_CanUseSensor()) {
|
|
PID_Start();
|
|
LF_currState = STATE_FOLLOW_SEGMENT;
|
|
#if PL_CONFIG_USE_DRIVE
|
|
(void)DRV_SetMode(DRV_MODE_NONE); /* disable any drive mode */
|
|
#endif
|
|
} else {
|
|
SHELL_SendString((unsigned char*)"Sensors not ready!\r\n");
|
|
#if PL_CONFIG_USE_BUZZER
|
|
(void)BUZ_Beep(500, 500);
|
|
#endif
|
|
}
|
|
}
|
|
if (notifcationValue&LF_STOP_FOLLOWING) {
|
|
LF_currState = STATE_STOP;
|
|
}
|
|
}
|
|
StateMachine();
|
|
if (LF_IsFollowing()) {
|
|
vTaskDelay(5/portTICK_PERIOD_MS);
|
|
} else {
|
|
vTaskDelay(100/portTICK_PERIOD_MS);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void LF_PrintHelp(const McuShell_StdIOType *io) {
|
|
McuShell_SendHelpStr((unsigned char*)"line", (unsigned char*)"Group of line following commands\r\n", io->stdOut);
|
|
McuShell_SendHelpStr((unsigned char*)" help|status", (unsigned char*)"Shows line help or status\r\n", io->stdOut);
|
|
McuShell_SendHelpStr((unsigned char*)" speed <mode>", (unsigned char*)"Line following speed, either low, medium or high\r\n", io->stdOut);
|
|
McuShell_SendHelpStr((unsigned char*)" start|stop", (unsigned char*)"Starts or stops line following\r\n", io->stdOut);
|
|
}
|
|
|
|
static void LF_PrintStatus(const McuShell_StdIOType *io) {
|
|
uint8_t buf[32];
|
|
|
|
McuShell_SendStatusStr((unsigned char*)"line follow", (unsigned char*)"Line following status\r\n", io->stdOut);
|
|
switch (LF_currState) {
|
|
case STATE_IDLE:
|
|
McuShell_SendStatusStr((unsigned char*)" state", (unsigned char*)"IDLE\r\n", io->stdOut);
|
|
break;
|
|
case STATE_FOLLOW_SEGMENT:
|
|
McuShell_SendStatusStr((unsigned char*)" state", (unsigned char*)"FOLLOW_SEGMENT\r\n", io->stdOut);
|
|
break;
|
|
case STATE_STOP:
|
|
McuShell_SendStatusStr((unsigned char*)" state", (unsigned char*)"STOP\r\n", io->stdOut);
|
|
break;
|
|
#if PL_CONFIG_APP_LINE_MAZE
|
|
case STATE_TURN:
|
|
McuShell_SendStatusStr((unsigned char*)" state", (unsigned char*)"TURN\r\n", io->stdOut);
|
|
break;
|
|
case STATE_FINISHED:
|
|
McuShell_SendStatusStr((unsigned char*)" state", (unsigned char*)"FINISHED\r\n", io->stdOut);
|
|
break;
|
|
#endif
|
|
default:
|
|
McuShell_SendStatusStr((unsigned char*)" state", (unsigned char*)"UNKNOWN\r\n", io->stdOut);
|
|
break;
|
|
} /* switch */
|
|
McuUtility_strcpy(buf, sizeof(buf), LINE_GetSpeedModeString(lineFollowSpeed));
|
|
McuUtility_strcat(buf, sizeof(buf), (unsigned char*)"\r\n");
|
|
McuShell_SendStatusStr((unsigned char*)" speed", buf, io->stdOut);
|
|
}
|
|
|
|
uint8_t LF_ParseCommand(const unsigned char *cmd, bool *handled, const McuShell_StdIOType *io) {
|
|
uint8_t res = ERR_OK;
|
|
|
|
if (McuUtility_strcmp((char*)cmd, (char*)McuShell_CMD_HELP)==0 || McuUtility_strcmp((char*)cmd, (char*)"line help")==0) {
|
|
LF_PrintHelp(io);
|
|
*handled = TRUE;
|
|
} else if (McuUtility_strcmp((char*)cmd, (char*)McuShell_CMD_STATUS)==0 || McuUtility_strcmp((char*)cmd, (char*)"line status")==0) {
|
|
LF_PrintStatus(io);
|
|
*handled = TRUE;
|
|
} else if (McuUtility_strcmp((char*)cmd, (char*)"line start")==0) {
|
|
LF_StartFollowing();
|
|
*handled = TRUE;
|
|
} else if (McuUtility_strcmp((char*)cmd, (char*)"line stop")==0) {
|
|
LF_StopFollowing();
|
|
*handled = TRUE;
|
|
} else if (McuUtility_strcmp((char*)cmd, (char*)"line speed low")==0) {
|
|
(void)LINE_SetSpeed(LINE_SPEED_LOW);
|
|
*handled = TRUE;
|
|
} else if (McuUtility_strcmp((char*)cmd, (char*)"line speed medium")==0) {
|
|
(void)LINE_SetSpeed(LINE_SPEED_MEDIUM);
|
|
*handled = TRUE;
|
|
} else if (McuUtility_strcmp((char*)cmd, (char*)"line speed high")==0) {
|
|
(void)LINE_SetSpeed(LINE_SPEED_HIGH);
|
|
*handled = TRUE;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
void LF_Deinit(void) {
|
|
/* nothing needed */
|
|
}
|
|
|
|
void LF_Init(void) {
|
|
LF_currState = STATE_IDLE;
|
|
if (xTaskCreate(LineTask, "Line", 600/sizeof(StackType_t), NULL, tskIDLE_PRIORITY+2, &LFTaskHandle) != pdPASS) {
|
|
for(;;){} /* error */
|
|
}
|
|
}
|
|
#endif /* PL_CONFIG_APP_LINE_FOLLOWING || PL_CONFIG_APP_LINE_MAZE */
|
|
|