Home > other >  ESP32/FreeRTOS, how to stop currently running task when the new one was triggered (avoiding overlap)
ESP32/FreeRTOS, how to stop currently running task when the new one was triggered (avoiding overlap)

Time:01-15

I'm working on code to control 2 module relay regarding door access. I'm looking for the way to stop the currently running tasks, before running the new one (the same task). All I want is to avoid overlap.

void TaskOpenManRoom(void *parameter){
  Serial.println("Opening men room");
  digitalWrite(manRelay, LOW);
  vTaskDelay(6000 / portTICK_PERIOD_MS);
  digitalWrite(manRelay, HIGH);
  Serial.println("Closing men room");
  vTaskDelete(NULL);
};

xTaskCreate(
      TaskOpenManRoom,
      "TaskOpenManRoom",
      1000,
      (void *) &man,
      1,
      &TaskMen
    );

My goal is to extend the time when the door should be opened. So basically when the first task was triggered and then after some while the second one, the door should stay opened another 6000ms.

In mu current code, when the second task is called like in the middle of the first one, the door get closed because of first task calling digitalWrite(manRelay, HIGH);

I would appreciate the hint how I can kill the first task when the second been triggered.

CodePudding user response:

Tasks are meant to be long-running, because they are relatively heavyweight. Don't start and end tasks for each user activity and don't delay them for extended periods of time.

You don't need any task at all for your functionality, you just need a timer to perform the closing activity after 6000 ms. Then you can reset it whenever you need.

TimerHandle_t closeManRoomTimer;

void OpenManRoom() {
  xTimerReset(closeManRoomTimer, 100);    // <------ (re)arm the close timer
  Serial.println("Opening men room");
  digitalWrite(manRelay, LOW);
};

void CloseManRoom(TimerHandle_t) {
  Serial.println("Closing men room");
  digitalWrite(manRelay, HIGH);
};

// during program startup, setup a one-shot close timer
closeManRoomTimer = xTimerCreate("closeManRoomTimer", pdMS_TO_TICKS(6000), pdFALSE, 0, &CloseManRoom);

CodePudding user response:

I would not kill the first task when the second starts.

If you use a task at all, I'd rewrite the task to something along this general line:

cast parameter to pointer to uint32
atomic increment open count, and if it was zero {
    open the door
    repeat {
        sleep six seconds
    } atomic decrement count, and exit loop if it was 1
    close the door
}
exit the task

...and when you create the task, pass a pointer to a uint32_t for it to use to store the open count.

So the task starts by atomically incrementing the open count, which returns the value that was previously in the open count. If that was zero, it means the door is currently closed. In that case, we open it and got to sleep.

If the task runs again while it's sleeping, the open count will now be one. We immediately increment that, but when we check the previous value, it was 1, so we don't try to open the door again--we just skip all the stuff in the if statement, and exit the task.

When the first instance of the task wakes up, it decrements the count, and it it was 1, it exits the loop, closes the door, and exits the task. But if the task ran again while it was sleeping, the count will still be greater than 1, so it will stay in the loop and sleep some more.

This is open to a little bit of optimization. As it stands right now, it sleeps a fixed period of time (six seconds) even if the current open count is greater than 1. If the task as expensive enough to justify a little extra work, we could do an atomic exchange, to retrieve the current open count and set the open count to 0, multiply the retrieved value by 6000, then sleep for that long. That adds quite a bit of extra complexity though, and in this case, the benefit would be much too small to justify it.

This does depend on our not running the task more than 4 billion times while the door is open. If we did, our atomic increment would overflow, and the code would misbehave. For the case at hand (and most others) this is unlikely to be a problem. In the rare situation where it might be, the obvious fix is a 64-bit variable (and 64-bit atomic increment and decrement). Incrementing the variable until a 64-bit variable overflows is generally not a realistic possibility (e.g., if you incremented at 1 GHz, it would take centuries).

CodePudding user response:

Many ways:

  1. use vTaskDelay which puts the task in the not running state (it is not blocking
  2. Wait for, mutex semaphore, queue or task notification from another task.

I would appreciate the hint how I can kill the first task when the second been triggered.

It will kill current task:

vTaskDelete(NULL);
  • Related