wait_for pin is unreliable
# help
j
I'm trying to register my homes power usage by reading each blink from my powermeter at the house. I'm using a photodiode and each time there is a blink the pin is pulled to 0. BLINK_DETECT_PIN.wait_for 0 The problem is that wait_for is not reliable, sometimes it catches each blink, sometimes it misses 1-3 blinks before catching one again. A simple toit example is attached, and also the equivalent arduino code. The arduino code catches every blink! Am I doing something wrong?
b
I would suggest shifting the
print
to the end, as it takes time ... my 2c 🙂
j
Have tried it without prints, same issue :/
s
Try to use pin.do inside a task instead, seems to work for me
j
will try that later!
f
If you just want to count pulses, I recommend the pulse_counter library: https://libs.toit.io/pulse_counter/library-summary
That said: do you know if the missed pulses are because of timing, or do we actually miss edge changes once we are solidly listening to them?
In other words, the
wait_for
(as well as the
print
...) might take some time to set up, and during that time edge transitions might be missed. However, once the
wait_for
is correctly set up, then we should not miss any edge transition. For example, if you have pulses that are once every millisecond, then it's not unlikely that you will miss some. However, if there is one every second, then that's bad and we need to investigate.
j
will look into the pulse counter also, have not yet testet the pin.do
The power meter gives 1000 blinks pr kwh used, so depending on how much power we use it varies, but during testing there was a blink every 7 seconds where it missed every 2-3 pulses. I dont think i can use the pulse counter since the timing between pulses is important to calculate the current power usage.
f
Thanks. I will see if I can reproduce the issue.
Are you running on the latest Jaguar release?
j
Version: v1.7.5 SDK version: v2.0.0-alpha.37 Build date: 2022-11-02T13:01:57Z
f
Do you have an idea of how long the pulses are? (So I can reproduce more easily)
And when the program failed to see the blink, would it simply ignore the whole pulse or would it get stuck on the waiting to go dark phase?
j
Just ran tests with the above examples, pin.do has same issue which is expected since it usew wait_for under the hood
Interestingly the counter implementation seems to work, in a loop with 5 ms sleep i check if the counter has been increased and it catches every pulse!
f
The counter is hardware based.
j
So it seems there is an issue with wait_for... The blink/pulse duration is about 20ms probably a bit lower
f
I recently "improved" the
wait_for
, but apparently I made a mistake.
Even added tests...
Investigating now what the difference could be
j
Test file used
f
The pulses there aren't strictly timed.
j
Output from counter test:
Copy code
DETECTED: 103 at: 1064443 elapsed: 1600
DETECTED: 120 at: 1066043 elapsed: 1599
DETECTED: 162 at: 1067633 elapsed: 1590
DETECTED: 177 at: 1069223 elapsed: 1589
DETECTED: 188 at: 1070813 elapsed: 1590
DETECTED: 204 at: 1072413 elapsed: 1599
DETECTED: 220 at: 1074013 elapsed: 1600
f
Will try now with 20ms pulses.
j
The counter seems to get many pulses at a time
The do test:
Copy code
DETECTED: 0 at: 1104575 elapsed: 1608
DETECTED: 1 at: 1104576 elapsed: 1
DETECTED: 0 at: 1106184 elapsed: 1607
DETECTED: 1 at: 1106185 elapsed: 1
DETECTED: 0 at: 1114224 elapsed: 8038
DETECTED: 1 at: 1114226 elapsed: 1
DETECTED: 0 at: 1119034 elapsed: 4808
DETECTED: 1 at: 1119036 elapsed: 1
DETECTED: 0 at: 1128674 elapsed: 9638
DETECTED: 1 at: 1128675 elapsed: 1
DETECTED: 0 at: 1135114 elapsed: 6438
DETECTED: 1 at: 1135116 elapsed: 1
DETECTED: 0 at: 1138334 elapsed: 3218
f
The pulse_counter might be too sensitive.
I think there is an option to tell it to ignore pulses that are too short.
hmm. Can't find it.
nvm. It's on the constructor of the
Unit
the
glitch_filter_ns
.
I just added tests for 5ms pulses and those seem to work too.
I'm wondering, though. If the pulse-counter sees more pulses, then the transition might be bouncy.
I didn't see it right now, but maybe the
wait_for
code looks at the current state of the pin when it wakes up from the interrupt, and ignores the wake-up if the pin value isn't what it expected.
You could try to add a capacitor together with a pull-up/down to debounce the signal.
I will continue looking in the code if that could be the reason.
j
is wait_for significantly different than the interrupt code from the arduino sketch?
f
I'm not that familiar with the Arduino sketch, but from the code it looks like the
blink
function is run in an interrupt.
-> It must be fast and short.
Internally, we should be doing something similar, but since
wait_for
is blocking your task, we have to schedule the event, so that the task gets woken up again.
So there is more work to do.
In the Arduino sketch, the counter is just incremented.
In Toit, the event is recorded, and put into a queue, so that the task can be woken up and react to it.
Looking at your code again, I'm actually surprised that it works.
I would have expected that you can't
printf
in an interrupt function.
Maybe
serial.print
is safe, though.
But these are the kind of things we avoid by having an event system. There isn't code that is special and must be careful not to do certain operations. For example, you would not want to run the garbage collector inside an interrupt handler.
j
ok, will try the capacitor trick later this week probably, i'm not that much into low level stuff coming from a Java background 🙂 if that fails i will rely on the counter instead and polling in a loop...
f
If you are curious, you could also try to see what the RMT peripheral returns.
That one can measure extremely short changes.
It could confirm the bouncy transitions.
j
how would i use the rmt?
f
I would need to test, but:
Copy code
import gpio
import rmt

main:
  pin := gpio.Pin ...
  channel := rmt.Channel pin --input --idle_threshold=32000 --no-enable_filter --memory_block_count=8
  signals := channel.read
  print signals
In that configuration the RMT would consider a signal to be idle when nothing happens for 32ms.
This is mostly from memory. Can't guarantee that it will work.
The RMT is designed to read infra-red signals (remote controls).
It waits for something to happens and records the levels, until the line goes stable/idle again.
It then gives you the recorded transitions.
With a pulse every few seconds we would expect to get a clean "transition from 1 to 0; 20ms; transition from 0 to 1". However, if the input signal is bouncy, then the RMT would record something like "transition from 1 to 0; 1us, transition from 0 to 1; 2us, ....".
Note that the RMT is configured for 1us here. If the transitions are faster than that, it would not see them.
There is a way to make it even more precise, but then we need to be careful that the idle_threshold still fits.
That said, it would probably still be enough to record a transition from 0 to 1 (especially if it's bouncy).
j
thanks
f
I have now rewritten the
wait_for
. Hopefully it will be more stable with the new approach. Will clean up my patch tomorrow and upload it for review. It will hopefully be in the next release.
j
great, looking forward to test it!
I just did the rmt test and get the following output:
Copy code
READ_CHANNEL...
SIGNALS: 0-2590 1-1 0-1 1-1 0-1 1-1 0-1 1-1 0-1 1-1 0-1 1-1 0-1 1-1 0-1 1-1 0-1 1-1 0-1 1-1 0-1 1-1 0-1 1-1 0-1 1-2 0-1 1-1 0-1 1-0
READ_CHANNEL...
SIGNALS: 0-2580 1-1 0-1 1-1 0-10 1-1 0-1 1-1 0-1 1-1 0-1 1-1 0-1 1-1 0-1 1-0
READ_CHANNEL...
SIGNALS: 0-2588 1-1 0-1 1-1 0-1 1-1 0-1 1-1 0-1 1-1 0-1 1-1 0-1 1-1 0-1 1-1 0-1 1-1 0-1 1-1 0-1 1-1 0-1 1-1 0-1 1-1 0-1 1-1 0-1 1-1 0-1 1-0
So it seems the signal is bouncy...
f
That explains why the pulse counter sees many transitions. However, the initial transition seems to be quite stable and should be detected by the
wait_for
. If I interpret this correctly it's relatively short though: 2.5ms This is exactly what my patch should fix.
k
Florian's patch for
pin.wait_for
is part of Toit v2.0.0-alpha.42 that ships in Jaguar v1.7.9, which is released everywhere except on Windows where we are waiting a day or two for a code signing issue. Almost there 🙂
Jaguar v1.7.9 is out on Windows.
j
Just tested and so far it catches every pulse! thanks for the quick fix!
f
Great news.
j
My energy monitor is back on track 🙂
b
@floitsch I notice the S2/3 firmware downloaded with the latest 1.7.9 How do I flash for the S2,
jag flash
knows nothing about the
--chip
argument ... cherrs
k
@bmentink and @floitsch: We're still missing something like https://github.com/toitlang/jaguar/pull/308.