arkhipenko / TaskScheduler

Cooperative multitasking for Arduino, ESPx, STM32, nRF and other microcontrollers
http://playground.arduino.cc/Code/TaskScheduler
BSD 3-Clause "New" or "Revised" License
1.22k stars 224 forks source link

I want to use the buzzer when the key press and avoid using delay()? #110

Closed KiraVerSace closed 3 years ago

KiraVerSace commented 3 years ago

Hey! Thank you for your library! Here is my question: When I preesed the button in my board, I want my buzzer to sound 200ms, and I use the status request like semaphore in OS, when the key pressed, I release one signal. so my code like this, and I can not avoid use the delay to let user hear the beep sound, and this is against to the principle

Use short efficient callback methods written for cooperative multitasking.

Do you have and good idea? Please help me?

#define BUTTON_PEROID 10
void buttonCheckCB(void);
Task taskButton(BUTTON_PEROID*TASK_MILLISECOND, TASK_FOREVER, &buttonCheckCB, &taskScheduler, false);

StatusRequest beepSR;
void beepOnCB(void);
void beepOffCB(void);
Task taskBeep(&beepOnCB, &taskScheduler);
// setup()
taskButton.enable();

beepSR.setWaiting(1);
taskBeep.waitFor(&beepSR);
// My button click event
void sButtonClick(void)
{
  beepSR.signal();
  Serial.println("sButton click.");
}

// My beepOnCB
void beepOnCB(void)
{
  digitalWrite(BEEP, HIGH);
  **delay(200);** // !!!!!!!!!!!!
  digitalWrite(BEEP, LOW);
  beepSR.setWaiting(1);
  taskBeep.waitFor(&beepSR);
}
chrisalbertson commented 3 years ago

If I understand correctly, you would like to avoid the use of "delay()" in your code. The usual solution is to think of the sound is two events, not one. See some rough pseudo code below

sound the beep{ turn on buzzer set time to stop = CURRENT_TIME + 200 milliseconds }

set time to sop = 0 main loop{ if (button is down ) sound the beep() if (time to stop > 0 && time >= time to stop) { digitalWrite(BEEP, LOW) set time to stop = 0 } }

The main thing is that when you tun on the beep, you don't wait but you do set a timer. The details don't matter. Some timmers can have call-backs or some times you just monitor the time but in all cases the plan is to schedule a turn-off time.

On Sat, Oct 10, 2020 at 12:34 PM KiraVerSace notifications@github.com wrote:

Hey! Thank you for your library! Here is my question: When I preesed the button in my board, I want my buzzer to sound 200ms, and I use the status request like semaphore in OS, when the key pressed, I release one signal. so my code like this, and I can not avoid use the delay to let user hear the beep sound, and this is against to the principle

Use short efficient callback methods written for cooperative multitasking.

Do you have and good idea? Please help me?

define BUTTON_PEROID 10

void buttonCheckCB(void); Task taskButton(BUTTON_PEROID*TASK_MILLISECOND, TASK_FOREVER, &buttonCheckCB, &taskScheduler, false);

StatusRequest beepSR; void beepOnCB(void); void beepOffCB(void); Task taskBeep(&beepOnCB, &taskScheduler);

// setup() taskButton.enable();

beepSR.setWaiting(1); taskBeep.waitFor(&beepSR);

// My button click event void sButtonClick(void) { beepSR.signal(); Serial.println("sButton click."); }

// My beepOnCB void beepOnCB(void) { digitalWrite(BEEP, HIGH); delay(200); // !!!!!!!!!!!! digitalWrite(BEEP, LOW); beepSR.setWaiting(1); taskBeep.waitFor(&beepSR); }

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/arkhipenko/TaskScheduler/issues/110, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABQKNRWPUCT7H5FQ2SCYQW3SKCZNLANCNFSM4SLIKS3Q .

--

Chris Albertson Redondo Beach, California

KiraVerSace commented 3 years ago

If I understand correctly, you would like to avoid the use of "delay()" in your code. The usual solution is to think of the sound is two events, not one. See some rough pseudo code below sound the beep{ turn on buzzer set time to stop = CURRENT_TIME + 200 milliseconds } set time to sop = 0 main loop{ if (button is down ) sound the beep() if (time to stop > 0 && time >= time to stop) { digitalWrite(BEEP, LOW) set time to stop = 0 } } The main thing is that when you tun on the beep, you don't wait but you do set a timer. The details don't matter. Some timmers can have call-backs or some times you just monitor the time but in all cases the plan is to schedule a turn-off time. On Sat, Oct 10, 2020 at 12:34 PM KiraVerSace @.> wrote: Hey! Thank you for your library! Here is my question: When I preesed the button in my board, I want my buzzer to sound 200ms, and I use the status request like semaphore in OS, when the key pressed, I release one signal. so my code like this, and I can not avoid use the delay to let user hear the beep sound, and this is against to the principle Use short efficient callback methods written for cooperative multitasking. Do you have and good idea? Please help me? #define BUTTON_PEROID 10 void buttonCheckCB(void); Task taskButton(BUTTON_PEROIDTASK_MILLISECOND, TASK_FOREVER, &buttonCheckCB, &taskScheduler, false); StatusRequest beepSR; void beepOnCB(void); void beepOffCB(void); Task taskBeep(&beepOnCB, &taskScheduler); // setup() taskButton.enable(); beepSR.setWaiting(1); taskBeep.waitFor(&beepSR); // My button click event void sButtonClick(void) { beepSR.signal(); Serial.println("sButton click."); } // My beepOnCB void beepOnCB(void) { digitalWrite(BEEP, HIGH); delay(200);** // !!!!!!!!!!!! digitalWrite(BEEP, LOW); beepSR.setWaiting(1); taskBeep.waitFor(&beepSR); } — You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub <#110>, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABQKNRWPUCT7H5FQ2SCYQW3SKCZNLANCNFSM4SLIKS3Q . -- Chris Albertson Redondo Beach, California

Thank you for your help , I got it! Thank you !

arkhipenko commented 3 years ago

If I understand correctly, you would like to avoid the use of "delay()" in your code. The usual solution is to think of the sound is two events, not one. See some rough pseudo code below

The idea is great, however implementation was anti-task scheduler :)

@KiraVerSace: you do not really need StatusRequest for this - since there is no external force that controls the beeper. Just this:

Task tBuzzer (BEEP_INTERVAL, 2, &beepCB, &ts, true);

void beepCB() {
 if ( tBuzzer.isFirstIteration() ) {
   turnBeeperOn();
} 
else {
  turnBeeperOff();
}

OR with OnEnable/OnDisable methods:

Task tBuzzer (BEEP_INTERVAL, TASK_ONCE, NULL, &ts, true, &beepOE, &beepOD);

bool beepOE() {
   turnBeeperOn();
  return true;
} 

void boopOD() {
  turnBeeperOff();
}

and you activate this option by calling delayed enable/restart: tBuzzer.enableDelayed() or tBuzzer.restartDelayed();

good luck!

arkhipenko commented 3 years ago

If I understand correctly, you would like to avoid the use of "delay()" in your code. The usual solution is to think of the sound is two events, not one. See some rough pseudo code below sound the beep{ turn on buzzer set time to stop = CURRENT_TIME + 200 milliseconds } set time to sop = 0 main loop{ if (button is down ) sound the beep() if (time to stop > 0 && time >= time to stop) { digitalWrite(BEEP, LOW) set time to stop = 0 } } The main thing is that when you tun on the beep, you don't wait but you do set a timer. The details don't matter. Some timmers can have call-backs or some times you just monitor the time but in all cases the plan is to schedule a turn-off time. On Sat, Oct 10, 2020 at 12:34 PM KiraVerSace @.> wrote: Hey! Thank you for your library! Here is my question: When I preesed the button in my board, I want my buzzer to sound 200ms, and I use the status request like semaphore in OS, when the key pressed, I release one signal. so my code like this, and I can not avoid use the delay to let user hear the beep sound, and this is against to the principle Use short efficient callback methods written for cooperative multitasking. Do you have and good idea? Please help me? #define BUTTON_PEROID 10 void buttonCheckCB(void); Task taskButton(BUTTON_PEROIDTASK_MILLISECOND, TASK_FOREVER, &buttonCheckCB, &taskScheduler, false); StatusRequest beepSR; void beepOnCB(void); void beepOffCB(void); Task taskBeep(&beepOnCB, &taskScheduler); // setup() taskButton.enable(); beepSR.setWaiting(1); taskBeep.waitFor(&beepSR); // My button click event void sButtonClick(void) { beepSR.signal(); Serial.println("sButton click."); } // My beepOnCB void beepOnCB(void) { digitalWrite(BEEP, HIGH); delay(200);** // !!!!!!!!!!!! digitalWrite(BEEP, LOW); beepSR.setWaiting(1); taskBeep.waitFor(&beepSR); } — You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub <#110>, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABQKNRWPUCT7H5FQ2SCYQW3SKCZNLANCNFSM4SLIKS3Q . -- Chris Albertson Redondo Beach, California

@chrisalbertson: your solution goes against the very philosophy of this library. the entire idea behind TS is to avoid doing what you suggested and allow library do the scheduling.

KiraVerSace commented 3 years ago

If I understand correctly, you would like to avoid the use of "delay()" in your code. The usual solution is to think of the sound is two events, not one. See some rough pseudo code below

The idea is great, however implementation was anti-task scheduler :)

@KiraVerSace: you do not really need StatusRequest for this - since there is no external force that controls the beeper. Just this:

Task tBuzzer (BEEP_INTERVAL, 2, &beepCB, &ts, true);

void beepCB() {
 if ( tBuzzer.isFirstIteration() ) {
   turnBeeperOn();
} 
else {
  turnBeeperOff();
}

OR with OnEnable/OnDisable methods:

Task tBuzzer (BEEP_INTERVAL, TASK_ONCE, NULL, &ts, true, &beepOE, &beepOD);

bool beepOE() {
   turnBeeperOn();
  return true;
} 

void boopOD() {
  turnBeeperOff();
}

and you activate this option by calling delayed enable/restart: tBuzzer.enableDelayed() or tBuzzer.restartDelayed();

good luck!

Wow, your idea is so cool!, Thank you very much.

arkhipenko commented 3 years ago

May I suggest you start here: https://github.com/arkhipenko/TaskScheduler/wiki/Concept-of-Task-and-Cooperative-Task-Scheduling

arkhipenko commented 3 years ago

and this is for inspiration of doing things differently:

https://github.com/arkhipenko/TaskScheduler/blob/master/examples/Scheduler_example00_Blink/Scheduler_example00_Blink.ino

KiraVerSace commented 3 years ago

and this is for inspiration of doing things differently:

https://github.com/arkhipenko/TaskScheduler/blob/master/examples/Scheduler_example00_Blink/Scheduler_example00_Blink.ino

I try your method, but find a very intersting thing.

Here is my code run on wokwi https://wokwi.com/playground/task-scheduler


#define _TASK_SLEEP_ON_IDLE_RUN
#define _TASK_STATUS_REQUEST
#include <TaskScheduler.h>

Scheduler ts;

#define BUZZER_INTERVAL 40
void buzzerCB(void);
Task taskBuzzer(BUZZER_INTERVAL*TASK_MILLISECOND, 2, &buzzerCB, &ts, true);

void setup()
{
  Serial.begin(115200);
  Serial.println("HAHAHAHA");
}

void loop() {
  ts.execute();
}
void buzzerCB(void)
{
  if (taskBuzzer.isFirstIteration())
  {
    Serial.print("First  ");
    Serial.println(millis());
  }
  else if (taskBuzzer.isLastIteration())
  {
    Serial.print("Last  ");
    Serial.println(millis());
  }
  else
  {
    Serial.println("Happen");
  }
}

When the task create, the task will run immediately, and the print is right like this:

HAHAHAHA
First  0
Last  40

But when I run it in my program and download it to the board, something happend! In my thought, the buzzzer should beep at the beginning cause I enable the task, but the result is not this:

void buzzerCB(void)
{
    if (taskBuzzer.isFirstIteration())
    {
        digitalWrite(BUZZER, HIGH);
        Serial.print("First  ");
        Serial.println(millis());
    }
    else if(taskBuzzer.isLastIteration())
    {
        digitalWrite(BUZZER, LOW);
        Serial.print("LAST  ");
        Serial.println(millis());
    }
    else
    {
        Serial.print("happen  ");
        Serial.println(millis());
        digitalWrite(BUZZER, LOW);
    }
}

and this is my SecureCRT print

First  2143
happen  2144
happen  2146
LAST  2147

Do you know why? Why the buzzer do not beep(40ms) at the start?

Please.

KiraVerSace commented 3 years ago

and this is for inspiration of doing things differently:

https://github.com/arkhipenko/TaskScheduler/blob/master/examples/Scheduler_example00_Blink/Scheduler_example00_Blink.ino

and I find another way to do it. that is use the tone() function! But I want to know why in my last question.

chrisalbertson commented 3 years ago

On Sun, Oct 11, 2020 at 11:56 AM Anatoli Arkhipenko < notifications@github.com> wrote:

If I understand correctly, you would like to avoid the use of "delay()" in your code. The usual solution is to think of the sound is two events, not one. See some rough pseudo code below

The idea is great, however implementation was anti-task scheduler :)

I wanted to explain the idea, not a "cut and paste" solution. Hence the very informal code syntax. Usually, I find problems are conceptual and "cut and paste" does not help. --

Chris Albertson Redondo Beach, California

arkhipenko commented 3 years ago

@KiraVerSace too many "if's" and "else's"

#define BUZZER_INTERVAL 40
void buzzerCB(void);
Task taskBuzzer(BUZZER_INTERVAL*TASK_MILLISECOND, 2, &buzzerCB, &ts, false);

void setup()
{
  Serial.begin(115200);
  Serial.println("HAHAHAHA");
  taskBuzzer.enable();
}

void buzzerCB(void)
{
    if (taskBuzzer.isFirstIteration())
    {
        digitalWrite(BUZZER, HIGH);
        Serial.print("First  ");
               Serial.println(millis());
    }
    if(taskBuzzer.isLastIteration())
    {
        digitalWrite(BUZZER, LOW);
        Serial.print("LAST  ");
        Serial.println(millis());
    }
}

There are only two iterations, really, you can replace it with a single line:

void buzzerCB(void)
{
  digitalWrite(BUZZER, taskBuzzer.getRunCounter() & 1 ? HIGH : LOW);
}
KiraVerSace commented 3 years ago

@KiraVerSace too many "if's" and "else's"

#define BUZZER_INTERVAL 40
void buzzerCB(void);
Task taskBuzzer(BUZZER_INTERVAL*TASK_MILLISECOND, 2, &buzzerCB, &ts, false);

void setup()
{
  Serial.begin(115200);
  Serial.println("HAHAHAHA");
  taskBuzzer.enable();
}

void buzzerCB(void)
{
  if (taskBuzzer.isFirstIteration())
  {
      digitalWrite(BUZZER, HIGH);
      Serial.print("First  ");
             Serial.println(millis());
  }
  if(taskBuzzer.isLastIteration())
  {
      digitalWrite(BUZZER, LOW);
      Serial.print("LAST  ");
      Serial.println(millis());
  }
}

There are only two iterations, really, you can replace it with a single line:

void buzzerCB(void)
{
  digitalWrite(BUZZER, taskBuzzer.getRunCounter() & 1 ? HIGH : LOW);
}

Thank you for your reply. And thank you for your resolution! But my doubt is not that, Have you seen the two results in the same program but in different environment, one is run in my board and one is in the simulation. I think the right result is the buzzer will beep when I enable the task, but true is not, can you see my last post carefully again?

I am from China ,and not good at English, Thank you for your help!

arkhipenko commented 3 years ago

But my doubt is not that, Have you seen the two results in the same program but in different environment, one is run in my board and one is in the simulation. I think the right result is the buzzer will beep when I enable the task, but true is not, can you see my last post carefully again?

Could you please send me the EXACT code you ran on the board? It could be a simple delay() after you initialize Serial in the setup() method that causes a race for task scheduler to "catch up" with the schedule. Why does it start at 2143 milliseconds? what delays it?

KiraVerSace commented 3 years ago

But my doubt is not that, Have you seen the two results in the same program but in different environment, one is run in my board and one is in the simulation. I think the right result is the buzzer will beep when I enable the task, but true is not, can you see my last post carefully again?

Could you please send me the EXACT code you ran on the board? It could be a simple delay() after you initialize Serial in the setup() method that causes a race for task scheduler to "catch up" with the schedule. Why does it start at 2143 milliseconds? what delays it?

Thank you ! Here is my code!


/*
 * @Description  :
 * @Version      : 0.1
 * @Company      : V-Think Development Team
 * @Author       : KiraVerSace@yeah.net
 * @Date         : 2020-05-22 15:49:56
 * @LastEditTime : 2020-10-12 22:54:23
 * TODO: ADC add the connect(), improve the stable!
 */
#include <Arduino.h>
#include <XCommon.h>
#include <fml_fram.h>

#define _TASK_SLEEP_ON_IDLE_RUN
#define _TASK_STATUS_REQUEST
#include <TaskScheduler.h>
Scheduler taskScheduler;

#define XSHELL_INTERVAL     10
void xShellRunCB(void);
Task taskXShell(XSHELL_INTERVAL*TASK_MILLISECOND, TASK_FOREVER, &xShellRunCB, &taskScheduler, false);

#define SENSOR_INTERVAL     5000
void sensorSampleCB(void);
Task taskSensorSample(SENSOR_INTERVAL*TASK_MILLISECOND, TASK_FOREVER, &sensorSampleCB, &taskScheduler, false);

#define BUTTON_INTERVAL     10
void buttonCheckCB(void);
Task taskButton(BUTTON_INTERVAL*TASK_MILLISECOND, TASK_FOREVER, &buttonCheckCB, &taskScheduler, false);

#define BUZZER_INTERVAL     40
void buzzerCB(void);
Task taskBuzzer(BUZZER_INTERVAL*TASK_MILLISECOND, 2, &buzzerCB, &taskScheduler, false);

void setup(void)
{
    systemInitX();

    fram.begin(sizeof(LogRecordT));
    fram.saveParameter();
    fram.loadParameter();
    Serial.print("Limit Count: ");  Serial.println(fram.logLimit());
    Serial.print("Record Count: "); Serial.println(fram.logCount());

    taskXShell.enable();
    taskSensorSample.enable();
    taskButton.enable();
    taskBuzzer.enable();
}

void loop(void)
{
    taskScheduler.execute();
}

void xShellRunCB(void)
{
    xShell.run();
}

void sensorSampleCB(void)
{
    Serial.print("Creating Records... No:");
    Serial.print(logRecord.id, DEC);
    Serial.println();
    logRecord.temperature = sht20.temperature();
    Serial.println((String)logRecord.temperature + "°C");
    logRecord.humidity = sht20.humidity();
    Serial.println((String)logRecord.humidity + " %RH");
    logRecord.id++;

    Serial.print("Record Count: ");
    Serial.println(fram.logCount());
    fram.appendLog(EDB_REC logRecord);

    lv_task_handler();

    Serial.print("AIN0: ");
    Serial.println(adc.readADC_SingleEnded(0)*0.125F/120);
    Serial.print("AIN1: ");
    Serial.println(adc.readADC_SingleEnded(1)*0.125F/120);
    Serial.print("AIN2: ");
    Serial.println(adc.readADC_SingleEnded(2)*0.125F/120);
    Serial.print("AIN3: ");
    Serial.println(adc.readADC_SingleEnded(3)*0.125F/120);
    Serial.print("Voltage:");
    Serial.println(String(analogRead(SENSOR_V)*readVref()*101/40960));

    Serial.print("Current:");
    currentMonitor.add(analogRead(SENSOR_I)*readVref()/4096/43.11F);
    Serial.println(String(currentMonitor.get()));

    Serial.print("CPU-VRef: ");
    Serial.println(String(readVref()));
    Serial.print("CPU-Temperature: ");
    Serial.println(String(readTempSensor())+ "°C");
    Serial.print("RTC-VBattery: ");
    Serial.println(String(readBattery()));
    Serial.println(" ");

    digitalToggle(LED);
}

void buttonCheckCB(void)
{
    sButton.tick();
}

void buzzerCB(void)
{
    if (taskBuzzer.isFirstIteration())
    {
        digitalWrite(BUZZER, HIGH);
        Serial.print("First  ");
        Serial.println(millis());
    }

    if(taskBuzzer.isLastIteration())
    {
        digitalWrite(BUZZER, LOW);
        Serial.print("LAST  ");
        Serial.println(millis());
    }
}
arkhipenko commented 3 years ago

Is the problem still there now that you enable the tasks at the end of setup()?

KiraVerSace commented 3 years ago

Is the problem still there now that you enable the tasks at the end of setup()?

iShot2020-10-12 23 36 13

yes you can see it !

arkhipenko commented 3 years ago

How long does xShell.run(); take? The button callback is the last task in the chain. I am afraid by the time you get there the schedule it already behind.

Why is the first iteration of busser at 1809 ms?

if xShell.run() is a slow method, you will always have overruns. You can do this:

add this at the top:

define _TASK_SCHEDULING_OPTIONS

and right before you enable taskBuzzer in setup add this: taskBuzzer.setSchedulingOption(TASK_INTERVAL);

this changes the scheduling priority to always observing the interval between iterations, rather than original schedule.

KiraVerSace commented 3 years ago

I found that if I disable the // taskSensorSample.enable() it works fine,

First 1242 LAST 1281

but why? I don'r know.

KiraVerSace commented 3 years ago

define _TASK_SCHEDULING_OPTIONS

and if I do as your suggestion, it works fine too Could you explain it in detail, I am so stupid.

KiraVerSace commented 3 years ago

if I change the order of enable, it has no effect, problem is still. The button callback is the last task in the chain is not the key problem

arkhipenko commented 3 years ago

I am sorry, but you will have to experiment and read up on your own. I am afraid I do not have the time to support you at this level.

KiraVerSace commented 3 years ago

I am sorry, but you will have to experiment and read up on your own. I am afraid I do not have the time to support you at this level.

thank you all the same, I will work hard on it!