Repository navigation
Add option in DHT driver to do the measure without blocking interrupts - #6946
EddieParis wants to merge 2 commits into
Conversation
4fdf251 to
2021e34
Compare
This is done because ESP8266 has a PWM relying on interrupts. Doing the DHT measure with interruption blocking, sometimes makes glitches on the PWM.
|
To reproduce issue, assuming dht is on gpio 0 and led on gpio14 import dht You should see glitches on the leds change to d=dht.DHT22(Pin(0), False), no more glitches |
|
|
||
| STATIC mp_obj_t dht_readinto(mp_obj_t pin_in, mp_obj_t buf_in) { | ||
| STATIC mp_obj_t dht_readinto(mp_obj_t pin_in, mp_obj_t buf_in, mp_obj_t irq_lock) { | ||
| mp_uint_t irq_state=0; |
There was a problem hiding this comment.
This does not follow the code style in the file. Put spaces around '='. Also there seems to be no need for the 0 initialization.
There was a problem hiding this comment.
unfortunately you have to :-( That is why I added it.
+1: ../../drivers/dht/dht.c: In function 'dht_readinto':
./xtirq.h:48:5: error: 'irq_state' may be used uninitialized in this function [-Werror=maybe-uninitialized]
asm volatile ("wsr %0, ps; rsync" : : "a" (ps));
^
../../drivers/dht/dht.c:41:15: note: 'irq_state' was declared here
mp_uint_t irq_state;
^
cc1: all warnings being treated as errors
There was a problem hiding this comment.
Well, either I'm missing something or compiler is wrong about that but well, in any way, we want to make it happy.
| >>> while not measure_ok: | ||
| >>> try: | ||
| >>> d.measure() | ||
| >>> measure_ok = True |
There was a problem hiding this comment.
A minor comment, I think the more common way of handling it would be to get rid of this variable and use brake like so:
while True:
try:
d.measure()
break
except [...]
There was a problem hiding this comment.
yes ! good point.
| mp_hal_delay_ms(18); | ||
|
|
||
| mp_uint_t irq_state = mp_hal_quiet_timing_enter(); | ||
| if (mp_obj_is_true(irq_lock)) { |
There was a problem hiding this comment.
mp_obj_is_true() is a little involved function so it would be worth calling it only once instead if tree times. You should consider creating a bool that will "cache" this computation once, something like:
bool irq_lock_true = mp_obj_is_true(irq_lock);
There was a problem hiding this comment.
well, I considered the call to mp_obj_is_true() as almost free. I can do that if you prefer.
There was a problem hiding this comment.
Check its implementation here. For most cases it is, indeed, but if someone passes some object there, it does require quite some checks.
Ultimately it's not my decision, I'm just a random guy doing a review, but I do think its worth saving those few CPU cycles.
| def measure(self): | ||
| buf = self.buf | ||
| dht_readinto(self.pin, buf) | ||
| dht_readinto(self.pin, buf, self.irq_block) |
There was a problem hiding this comment.
We could consider putting a retry loop here in case self.irq_block is false to free the users from doing that. I would avoid the infinite loop, though.. so maybe some fixed amount of retries should be introduced?
There was a problem hiding this comment.
I prefer to let the user decide to manage that as he prefers. In my case, I have other things to do so I can't lock main loop for too long, so I retry later. DHT is already really slow, not worth adding more delays.
There was a problem hiding this comment.
Seems valid argument, indeed. I was just afraid that anybody has to implement this loop while using this argument anyways.
There was a problem hiding this comment.
With a DHT sensor you have to implement a try:except: retry.. loop anyway because they are not very stable.
|
As a general comment, the same issue exist with onewire module (as DHT protocol is very similiar to onewire bus) so if you are already putting a warning to the documentation, maybe it would make sense to mention onewire case as well? |
|
Yeah, why not. But in the onewire, bits are read one by one and the protocol is waaaaaay faster (locking is shorter), so I'm not sure it can produce visible effects. |
True. But if this (locking for ~70us) is not a problem then maybe for DHD it would also be ok to do a lock/unlock in every iteration of the loop (which takes 80-120us) and only for a machine_time_pulse_us(). This way we still can get a wrong result if entering into the critical region is delayed much more than 50us (the time of a low pulse before each high pulse we are measuring) but we might get a little better results than without any locking while the overall latency would be still be much smaller. DHT will send 26-28us pulse for "0" and 70us for "1" but dht.c is considering a "1" everything that is >48us so we already have a safety margin of around 22us in case our machine_time_pulse_us() starts late (when the 50us low singal is already over). To sum up, my understanding is this:
What do you think ? |
|
Theoretically you are right. But I think this is over complicated for a very little benefit. My software is running for almost a year with this patch and 3 channel PWM in parallel, I observed very few checksum errors, and no weird measures that could have gone through checksum verification. |
|
This is an automated heads-up that we've just merged a Pull Request See #13763 A search suggests this PR might apply the STATIC macro to some C code. If it Although this is an automated message, feel free to @-reply to me directly if |
|
I suggest to leave the code as-is and put this down as a limitation of the esp8266. Nowadays one can use an esp32/rp2x and it should work much better. |
This is done because ESP8266 has a PWM relying on interrupts.
Doing the DHT measure with interruption blocking, sometimes makes
glitches on the PWM.
fixes #6945