Add mutex to chairAdjust object - fix potential crashes

This commit is contained in:
jonny 2024-09-05 13:21:46 +02:00
parent c19d053867
commit e23d37df4c
2 changed files with 126 additions and 88 deletions

View File

@ -7,6 +7,7 @@ extern "C"
#include "chairAdjust.hpp" #include "chairAdjust.hpp"
#define MUTEX_TIMEOUT (10000 / portTICK_PERIOD_MS)
//--- gloabl variables --- //--- gloabl variables ---
// strings for logging the rest state // strings for logging the rest state
@ -21,12 +22,12 @@ static const char * TAG = "chair-adjustment";
//============================= //=============================
//======== constructor ======== //======== constructor ========
//============================= //=============================
cControlledRest::cControlledRest(gpio_num_t gpio_up_f, gpio_num_t gpio_down_f, uint32_t travelDurationMs, const char * name_f, float defaultPosition):travelDuration(travelDurationMs){ cControlledRest::cControlledRest(gpio_num_t gpio_up_f, gpio_num_t gpio_down_f, uint32_t travelDurationMs, const char * name_f, float defaultPosition): gpio_up(gpio_up_f), gpio_down(gpio_down_f), travelDuration(travelDurationMs){
strcpy(name, name_f); strcpy(name, name_f);
gpio_up = gpio_up_f;
gpio_down = gpio_down_f;
positionNow = defaultPosition; positionNow = defaultPosition;
positionTarget = positionNow; positionTarget = positionNow;
// recursive mutex necessary, because handle() method calls setState() which both have the same mutex
mutex = xSemaphoreCreateRecursiveMutex();
init(); init();
} }
@ -95,117 +96,150 @@ void cControlledRest::updatePosition(){
//============================ //============================
void cControlledRest::setState(restState_t targetState) void cControlledRest::setState(restState_t targetState)
{ {
// TODO: drop this section? // lock the mutex before accessing shared variables
// check if actually changed if (xSemaphoreTakeRecursive(mutex, MUTEX_TIMEOUT) == pdTRUE)
if (targetState == state)
{ {
// update anyways when target is 0 or 100, to trigger movement threshold in case of position tracking is out of sync // TODO: drop this section?
if (positionTarget == 0 || positionTarget == 100) // check if actually changed
ESP_LOGD(TAG, "[%s] state already at '%s', but updating anyway to trigger move to limit addition", name, restStateStr[state]); if (targetState == state)
else
{ {
ESP_LOGV(TAG, "[%s] state already at '%s', nothing to do", name, restStateStr[state]); // update anyways when target is 0 or 100, to trigger movement threshold in case of position tracking is out of sync
return; if (positionTarget == 0 || positionTarget == 100)
ESP_LOGD(TAG, "[%s] state already at '%s', but updating anyway to trigger move to limit addition", name, restStateStr[state]);
else
{
ESP_LOGV(TAG, "[%s] state already at '%s', nothing to do", name, restStateStr[state]);
return;
}
} }
// when switching direction without stop: update position first
if (state != REST_OFF)
updatePosition();
// activate handle task when turning on (previous state is off)
if (state == REST_OFF)
xTaskNotifyGive(taskHandle); // activate handle task that stops the rest-motor again
// apply new state
ESP_LOGI(TAG, "[%s] switching from state '%s' to '%s'", name, restStateStr[state], restStateStr[targetState]);
switch (targetState)
{
case REST_UP:
gpio_set_level(gpio_down, 0);
gpio_set_level(gpio_up, 1);
timestamp_lastPosUpdate = esp_log_timestamp();
break;
case REST_DOWN:
gpio_set_level(gpio_down, 1);
gpio_set_level(gpio_up, 0);
timestamp_lastPosUpdate = esp_log_timestamp();
break;
case REST_OFF:
gpio_set_level(gpio_down, 0);
gpio_set_level(gpio_up, 0);
updatePosition();
positionTarget = positionNow; // disable resuming - no unexpected pos when incrementing
break;
}
state = targetState;
// Release the mutex
xSemaphoreGiveRecursive(mutex);
} }
else
// when switching direction without stop: update position first
if (state != REST_OFF)
updatePosition();
// activate handle task when turning on (previous state is off)
if (state == REST_OFF)
xTaskNotifyGive(taskHandle); //activate handle task that stops the rest-motor again
//apply new state
ESP_LOGI(TAG, "[%s] switching from state '%s' to '%s'", name, restStateStr[state], restStateStr[targetState]);
switch (targetState)
{ {
case REST_UP: ESP_LOGE(TAG, "mutex timeout in setState() -> RESTART");
gpio_set_level(gpio_down, 0); esp_restart();
gpio_set_level(gpio_up, 1);
timestamp_lastPosUpdate = esp_log_timestamp();
break;
case REST_DOWN:
gpio_set_level(gpio_down, 1);
gpio_set_level(gpio_up, 0);
timestamp_lastPosUpdate = esp_log_timestamp();
break;
case REST_OFF:
gpio_set_level(gpio_down, 0);
gpio_set_level(gpio_up, 0);
updatePosition();
positionTarget = positionNow; //disable resuming - no unexpected pos when incrementing
break;
} }
state = targetState;
} }
//========================== //==========================
//==== setTargetPercent ==== //==== setTargetPercent ====
//========================== //==========================
void cControlledRest::setTargetPercent(float targetPercent){ void cControlledRest::setTargetPercent(float targetPercent)
float positionTargetPrev = positionTarget; {
positionTarget = targetPercent; // lock the mutex before accessing shared variables
if (xSemaphoreTakeRecursive(mutex, MUTEX_TIMEOUT) == pdTRUE)
{
positionTarget = targetPercent;
float positionTargetPrev = positionTarget;
positionTarget = targetPercent;
// limit to 0-100 // limit to 0-100
if (positionTarget > 100) if (positionTarget > 100)
positionTarget = 100; positionTarget = 100;
else if (positionTarget < 0) else if (positionTarget < 0)
positionTarget = 0; positionTarget = 0;
// ignore if unchanged // ignore if unchanged
//if (positionTarget == positionTargetPrev){ // if (positionTarget == positionTargetPrev){
// ESP_LOGI(TAG, "[%s] Target position unchanged at %.2f%%", name, positionTarget); // ESP_LOGI(TAG, "[%s] Target position unchanged at %.2f%%", name, positionTarget);
// return; // return;
//} //}
ESP_LOGI(TAG, "[%s] changed Target position from %.2f%% to %.2f%%", name, positionTargetPrev, positionTarget); ESP_LOGI(TAG, "[%s] changed Target position from %.2f%% to %.2f%%", name, positionTargetPrev, positionTarget);
// start rest in required direction // start rest in required direction
// TODO always run this check in handle()? // TODO always run this check in handle()?
// note: when already at 0/100 start anyways (runs for certain threshold in case tracked position out of sync) // note: when already at 0/100 start anyways (runs for certain threshold in case tracked position out of sync)
if (positionTarget > positionNow || positionTarget >= 100) if (positionTarget > positionNow || positionTarget >= 100)
setState(REST_UP); setState(REST_UP);
else if (positionTarget < positionNow || positionTarget <= 0) else if (positionTarget < positionNow || positionTarget <= 0)
setState(REST_DOWN); setState(REST_DOWN);
else // already at exact position else // already at exact position
setState(REST_OFF); setState(REST_OFF);
// Release the mutex
xSemaphoreGiveRecursive(mutex);
}
else
{
ESP_LOGE(TAG, "mutex timeout while waiting in setTargetPercent -> RESTART");
esp_restart();
}
} }
//====================== //======================
//======= handle ======= //======= handle =======
//====================== //======================
// handle automatic stop when target position is reached, should be run repeatedly in a task // handle automatic stop when target position is reached, should be run repeatedly in a task
#define TRAVEL_TIME_LIMIT_ADDITION_MS 2000 // traveling longer into limit compensates inaccuracies in time based position tracking #define TRAVEL_TIME_LIMIT_ADDITION_MS 2000 // traveling longer into limit compensates inaccuracies in time based position tracking
void cControlledRest::handle(){ void cControlledRest::handle()
{
// lock the mutex before accessing shared variables
if (xSemaphoreTakeRecursive(mutex, MUTEX_TIMEOUT) == pdTRUE)
{
// nothing to do when not running atm
// TODO: turn on automatically when position != target?
if (state == REST_OFF)
return;
// nothing to do when not running atm // calculate time already running and needed time to reach target
// TODO: turn on automatically when position != target? uint32_t timeRan = esp_log_timestamp() - timestamp_lastPosUpdate;
if (state == REST_OFF) uint32_t timeTarget = travelDuration * fabs(positionTarget - positionNow) / 100;
return;
// calculate time already running and needed time to reach target // intentionally travel longer into limit - compensates inaccuracies in time based position tracking
uint32_t timeRan = esp_log_timestamp() - timestamp_lastPosUpdate; if (positionTarget == 0 || positionTarget == 100)
uint32_t timeTarget = travelDuration * fabs(positionTarget - positionNow) / 100; timeTarget += TRAVEL_TIME_LIMIT_ADDITION_MS;
// intentionally travel longer into limit - compensates inaccuracies in time based position tracking // target reached
if (positionTarget == 0 || positionTarget == 100) if (timeRan >= timeTarget)
timeTarget += TRAVEL_TIME_LIMIT_ADDITION_MS; {
ESP_LOGW(TAG, "[%s] handle: reached target run-time (%dms/%dms) for position %.2f%% -> stopping", name, timeRan, timeTarget, positionTarget);
setState(REST_OFF);
}
// target reached // Release the mutex
if (timeRan >= timeTarget){ xSemaphoreGiveRecursive(mutex);
ESP_LOGW(TAG, "[%s] handle: reached target run-time (%dms/%dms) for position %.2f%% -> stopping", name, timeRan, timeTarget, positionTarget); }
setState(REST_OFF); else
{
ESP_LOGE(TAG, "mutex timeout while waiting in handle() -> RESTART");
esp_restart();
} }
} }
//============================ //============================
//===== chairAdjust_task ===== //===== chairAdjust_task =====
//============================ //============================

View File

@ -3,6 +3,7 @@ extern "C"
{ {
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/task.h" #include "freertos/task.h"
#include "freertos/semphr.h"
#include "driver/gpio.h" #include "driver/gpio.h"
} }
@ -38,11 +39,14 @@ private:
void init(); void init();
void updatePosition(); void updatePosition();
SemaphoreHandle_t mutex;
char name[32]; char name[32];
gpio_num_t gpio_up; const gpio_num_t gpio_up;
gpio_num_t gpio_down; const gpio_num_t gpio_down;
restState_t state;
const uint32_t travelDuration = 12000; const uint32_t travelDuration = 12000;
restState_t state;
uint32_t timestamp_lastPosUpdate = 0; uint32_t timestamp_lastPosUpdate = 0;
float positionTarget = 0; float positionTarget = 0;
float positionNow = 0; float positionNow = 0;