Hello, I try to create a single pulse function in a "fire and forget" fashion. It's purpose is to control a bi-stable relay.
The code below intends to create a single pulse (duration 50ms on GPIO02 in this example). The pulse width is fine, however the output produces two pulses before it stops. How to resolve?
#define SingleShotOutput 2
#define PWMchannel 1
void IRAM_ATTR pwmstop()
{
ledcWrite(PWMchannel, 0);
}
void setup()
{
ledcAttachPin(SingleShotOutput, PWMchannel);
pinMode(SingleShotOutput, OUTPUT);
ledcSetup(PWMchannel, 10, 8); // 10 Hz PWM (100ms), 8-bit resolution
attachInterrupt(digitalPinToInterrupt(SingleShotOutput), pwmstop, FALLING);
}
void loop()
{
ledcWrite(PWMchannel, 128); //50% duty cycle to achieve 50ms on-time
delay(2000); // Wait a long time to restart the single pulse again
}
Idahowalker & Greg: thanks for your suggestions. However, my search is to find a solution without the use of a timer or delay function as I want to use it in a complex pattern of multiple relays to be operated (which may take 500ms to execute and I do not want the loop to wait for that).
My code is basically suitable, however the extra pulse seems to be a library bug. Considering the 50ms between the two pulses I cannot believe it is related to latency.
Whilst experimenting I discovered the following: I changed the line in the interrupt handler from ledcWrite(PWMchannel, 0); into ledcWrite(PWMchannel, 65) and noticed that the duty cycle also changes after two pulses. That brings me to the idea that the code behind ledcwrite is actually a hidden TASK that needs time to schedule and execute. Could this hypothesis be true?
executing a few lines with a timer-interrupt is finished in less than **0,**1 milliseconds.
Another approach would be to setup a timerinterrupt that checks a flag-variable.
if a latency of up to 50 milliseconds is OK this would be simply
if flag is set to true => IOn-pin HIGH
set flag false
on next call flag is false => set IO-pin LOW
at a timer-interrupt-frequency of 20 Hz the pulse would be 50 ms long
on first call of timer-interrupt set IO-pin HIGH
on second call set IO-pin LOW
==> 50 ms pulse.
inside loop do
generatePulse = true;
and you are done
inside the timer-ISR
if (generatePulse) { // wenever flag is true
generatePulse = false; // reset flag to false
digitalWrite(myPulsePin,HIGH);
}
else { // whenever flag is false
digitalWrite(myPulsePin,LOW);
}
If a latency of 50 ms is too long
you could setup a much higher frequency and then store a timestampe when createPulse is true
and you would have to check for elapsed time >= 50 ms
checking for elapsed time is completely different from a delay()
delay() waits until time is over
an if-condition like
if (millis() - StartOfPulse >= 50)
is executed very fast and very often.
this checking is repeated inside the timer-ISR until condition becomes true
I am delighted to report that the issue is solved. A very recent update of the ESP32 library solved my issue. So, the code at the top of this topic may serve as an example of a very easy and straightforward one-shot function. Thanks for your support!
I have to update my previous post. The solution was not the library update (i tried that as well) but to remove the line "pinMode(SingleShotOutput, OUTPUT);"