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

Do you have an example about pulseIn() function? #120

Closed jatu-studiobox closed 3 years ago

jatu-studiobox commented 3 years ago

Hi,

I have some problem about using TaskSchedule with pulseIn() function.

Below code is normal process. I cannot imagine apply TaskSchedule with it. It keep pulseIn() LOW duration signal every loop() function, until 5 minutes, then it calculate AQI value from keep pulseIn() LOW duration.

void setup() { // put your setup code here, to run once: Serial.begin(115200); pinMode(DUST_SENSOR_DIGITAL_PIN_PM10, INPUT); pinMode(DUST_SENSOR_DIGITAL_PIN_PM25, INPUT);

// wait 60s for DSM501 to warm up for (int i = 1; i <= 60; i++) { delay(1000); // 1s Serial.print(i); Serial.println(" s (wait 60s for DSM501 to warm up)"); }

Serial.println("Ready!");

AQI.starttime = millis();

timer.setInterval(sampletime_ms); }

void loop() { AQI.lowpulseoccupancyPM10 += pulseIn(DUST_SENSOR_DIGITAL_PIN_PM10, LOW); AQI.lowpulseoccupancyPM25 += pulseIn(DUST_SENSOR_DIGITAL_PIN_PM25, LOW);

if (timer.isReady()) { updateAQI(); timer.reset(); } }

refer: https://diyprojects.io/calculate-air-quality-index-iaq-iqa-dsm501-arduino-esp8266

Please advice me.

Thank you very much

arkhipenko commented 3 years ago

pulseIn() is a blocking function (similar to delay()) and could NOT be used with TaskScheduler without affecting the scheduling. You have to use pin interrupts instead if you would like to continue using cooperative multitasking.

An implementation of such for ultrasound distance sensing was done here: https://github.com/arkhipenko/pumpkin/blob/master/Pumpkin_Firmware.ino#L296

jatu-studiobox commented 3 years ago

Hi arkhipenko,

Thank you very much for your advise.

Should I use interupts for keeping duration, instead of pulseIn function?

I am not sure that. Because of UltraSonic sensor can control start time. So we can call digitalWrite(TrigPin, HIGH).

But DSM501A dust sensor, the signal come from sensor. So I wait the start time from sensor [HIGH to LOW for start time -> pulseIn(PIN, LOW) ].

If I misunderstand, please advise me.

arkhipenko commented 3 years ago

You can always use pin change interrupts to detect start time. There is no need to use a blocking function that holds all other processes.

jatu-studiobox commented 3 years ago

Hi arkhipenko,

Thank you very much for your advise. Now I can use interrupt and not use blocking function, pulseIn() function.

But my next problem is, the summary duration from my task and interrupt for gathering LOW pulse duration, which does not same value which is from pulseIn() function. The duration have exceed from pulseIn() duration.

Below code is loop function for gathering summary duration in time 5 minutes from pulseIn() 'LOW' signal function, and if it is complete time 5 minutes then go to calculate AQI value. ` void loop() { AQI.lowpulseoccupancyPM10 += pulseIn(DUST_PM10_PIN, LOW);

if (millis() - AQI.starttime >= 300000) {
    calculateAqi();
}

} `

Below code is TaskSchedule for gathering summary duration in time 5 minutes from my custom task schedule 'LOW' signal function. and if it is complete time 5 minutes then go to calculate AQI value.

taskMeasureDsm uses interval 100 milliseconds for working.

#define DSM_INTERVAL 100    // milliseconds
#define DSM_TIMEOUT 10  // milliseconds
#define DSM_DURATION 300000 // milliseconds (5 * 6 * 10000)
.....
Task taskMeasureDsm(DSM_INTERVAL, -1, &measureDsmPM10);
Task taskDsmTimeout(DSM_TIMEOUT, 1, &doDsmTimeoutPM10);
Task taskCalcDsm(0, 1, &calculateAqiPM10);
......
void measureDsmPM10() {
    if (AQIPM10.busy) return;  // Break for below operation
    AQIPM10.busy = true;
    AQIPM10.pulseStart = 0;
    if (taskMeasureDsm.isFirstIteration()) {  // if start task, then keep start time
        Serial.println(F("Set PM10 start time"));
        AQIPM10.starttime = millis();
        AQIPM10.lowpulseoccupancy = 0;
    }
    attachInterrupt(digitalPinToInterrupt(DUST_PM10_PIN), &dsmPM10TrigStartStopClock, CHANGE);
}

void doDsmTimeoutPM10() {
    Serial.println(">doDsmTimeoutPM10...RUN");
    AQIPM10.lowpulseoccupancy = 0;
    taskCalcDsm.restart();
}

ICACHE_RAM_ATTR void dsmPM10TrigStartStopClock() {
    bool trigValue = digitalRead(DUST_PM10_PIN);
    if (trigValue == LOW) {
        AQIPM10.pulseStart = micros();
    } else {
        AQIPM10.pulseStop = micros();
        AQIPM10.lowpulseoccupancy += AQIPM10.pulseStop - AQIPM10.pulseStart;
        AQIPM10.busy = false;
        if (AQIPM10.starttime + sampletime_ms <= millis()) {
            detachInterrupt(digitalPinToInterrupt(DUST_PM10_PIN));
            taskDsmTimeout.disable();
            taskCalcDsm.restart();
        }
    }
}

I am not sure. Do I have to tune task INTERVAL time (current is 100 milliseconds), so that the summary duration value is the same as pulseIn() function?

Thank you so much in advance.