home-assistant / core

:house_with_garden: Open source home automation that puts local control and privacy first.
https://www.home-assistant.io
Apache License 2.0
71.68k stars 29.95k forks source link

Raspi GPIO binary_sensor produces unreliable responses ("Doorbell Scenario") #10498

Closed bahmrockk closed 3 years ago

bahmrockk commented 6 years ago

Make sure you are running the latest version of Home Assistant before reporting an issue.

Home Assistant release (hass --version): 0.57.2

Python release (python3 --version): Python 3.5.3

Component/platform: Binary Sensor GPIO on Raspberry Pi ( binary_sensor rpi_gpio)

Description of problem: The sensor does not trigger with very short connections. This sadly is the default case for the "Doorbell" Scenario. A self-made python script works as expected. Within hass, the first activation is always ignored. Afterwards, approximately 1/3 are not registered. An automation is not needed to reproduce.

Expected: Even a short hit on a button should trigger the binary sensor 100% of the time - hass.io should behave identically to manually implemented python scripts in that regard.

Problem-relevant configuration.yaml entries and steps to reproduce:

   - platform: rpi_gpio
     ports:
         18: hass_doorbell

Python that is working fine with the same hardware-setup can be found here: https://github.com/bahmrockk/homeassistantConfig/blob/master/doorbell.py

  1. Connect a button to the GPIO (e.g. http://www.instructables.com/id/Raspberry-Pi-IoT-Doorbell/ )
  2. Press that button
  3. Observe that it does not register 100% within the hass.io frontend (sometimes even only on release)

Additional info: I have tried a lot of different configurations, playing around with bouncetimes, inverted logic, etc etc. I'm now at a 80% confidence level that this is indeed a bug and not me being stupid :)

ziotibia81 commented 6 years ago

Hi! rpi_gpio should trigger an update event every time that the gpio input change state.

Try with a "long" debouce time (250ms) and press button for at least 1 second. If this work every time, you have to play around electrical connection and debouce time.

The default scan interval is 30 seconds. Try to change it to an high value (300s) to ensure that during your test the gpio update occours only during an edge. See Entity documentation.

Let me know.... bye!

bahmrockk commented 6 years ago

Hey,

Thank you for the awesome fast reply! Checked it and there's no change in behavior (bouncetime 250, scan_interval 300).

An error in the electrical circuit would cause the linked python script to not work as well though so I can rule that part out at least :) I played around with the pull modes as well, changing the python to mimic a pull_up instead of the physical resistor, but the result is the same - custom script works like a charm but Home Assistant ignores inputs regulary.

One interesting thing that stood out this test: The Home Assistant frontend shows the sensor hass_doorbell as "on" after boot - but as soon as the bell is triggered once, it switches to "off" and stays that way - even though the visual feedback (the circle next to it) on whether the button is pressed still works from time to time.

Thanks a lot!

ziotibia81 commented 6 years ago

The python script linked work in a different way than hass: Hass wait for an endge on input after debounce, the script read the input every 200ms without debouncing and, if the input is false, does something. For this reason you have to be sure of electrical contacts.

Post your question in the forum and we continue to discute in that place. We have tu be sure that your report is really a bug.

bahmrockk commented 6 years ago

ah okay! Perhaps that's the 20% I was missing - I'll check back on Sunday with a fresh breadboard instead of the already existing ones and write back.

As this seems to be only me I'm more convinced that it's an error outside of HASS.io!

MiKuBB commented 6 years ago

Hi, I have same problem. Using gpio.binary_sensor to track electricity high tarrif, over 230V relay. Succesfulnes of HA measurement is cca 50%.

In raspberry gpio python docs (https://sourceforge.net/p/raspberry-gpio-python/wiki/Inputs/) I read, that interrupt detection method should be RISING. but HA using both. Is there any possibility to change interrupt detection method?

thank you.

ziotibia81 commented 6 years ago

HA wait wait for a rising or falling interrupt, when it occour HA read the input. This logis is almost correct. Is more probably that the problem is related to Contact bounce. If you have an oscilloscope you can verify. You can also try an hardware debounce circuit (ex RC).

MiKuBB commented 6 years ago

Thank you for hint. pull up resistor helped me, but don't understand why rpi3 built in pull up resistors can't be used, or is there any possibility how to use it ?

ziotibia81 commented 6 years ago

Default HA setup for rpi gpio as input is with internal pull up and it can be change in configuration.

dmcilvaney commented 6 years ago

I'm seeing some possibly related behaviours in my project and got frustrated enough to start some digging.

I have two toggle buttons, confirmed to work using a multi-meter. Unlike most cases these buttons latch until pushed again. They are often getting stuck out of sync with HA. I copied components/binary_sensor/rpi_gpio.py into my custom_components folder and added some logging.

As expected given polling is disabled, the update() function is never being called. The edge detect callback seems to usually trigger reliably but three issues arise:

1) If an edge is missed for any reason HA never updates the state properly. What I think may be happening for some users: If a button gets stuck in the 'on' state, but is actually physically off, there will be no state change when the next edge detect occurs since the change is from 'on' to 'on'.

2) Probably more worrying, the edge detect callback is sometimes reporting the wrong reading from the input eg: currently high, button goes low, edge detect triggers, input still reads as high. Maybe something is messing up RPi.GPIO?

3) More than once I've seen edge detects trigger Low->High->Low. Each time the change occurs HA schedules an update, but sometimes they take time to run. If the first update takes long enough the second edge detection will have triggered and changed the state back to Low. The two updates will then both report the switch is Low. (Maybe a FIFO queue is needed here to guarantee all detected edges are passed to HA in order?)

I'm relatively new to HA so I'm not sure what the best way to debug this further is, but I figured I would share what I've found so far.

tomlut commented 6 years ago

Yeah there is definitely an issue with the gpio binary sensor. It triggers perfectly but often misses the edge on return to the off state. No amount of messing with the debug time helps (I tried from the default 50ms up to 1000ms). Neither does using internal or external pull-ups.

ziotibia81 commented 6 years ago

At this point we need help from rpi-gpio maintainer. HA set both edge detection on a gpio. When there are an edge it read the input and update the state. Between edge and read there are twoo callbacks and other code that take time.....

Rpi-gpio pass to callback only the input where edge occours but not the reason of interrupt. This is enough for rising or falling detection, but not for both.

The best thing could be a patch to C code of rpi-gpio and report to callback the channel number and the channel value.

tomlut commented 6 years ago

That sounds like a good solution. I had a look at the code ( https://github.com/home-assistant/home-assistant/blob/dev/homeassistant/components/binary_sensor/rpi_gpio.py ) it’s python not C but still beyond my ability to fix.

ziotibia81 commented 6 years ago

There is a open ticket in raspberry-gpio-python library. It might be useful to refer this thread in that ticket.

matkor commented 6 years ago

Hoping to hear if my fix (https://sourceforge.net/p/raspberry-gpio-python/tickets/157/) helps in yours use case scenario, like it worked for me.

jkobie commented 6 years ago

I was having a lot of issues with consistency with HA and gpio inputs. Finally hooked scope up to gpio. Lots of bouncing around :

https://i.imgur.com/iD3xGTb.png

Added a decoupling capacitor to the gpio pin. The decoupling capacitor cleaned up the switch input on the gpio pin.

https://i.imgur.com/yaVrKs3.png

tomlut commented 6 years ago

This is not a debounce issue. I too have a DSO and can check this. My input is a clean 0/1/0 transition lasting about 0.5 sec. The problem is that short pulses on the gpio port are triggered reliably but often do not register a return to the inactive state. So far the only solution I have found is hardware. I stretch the pulse length with a 555 time IC in monostable mode. 2 seconds works reliably.

jkobie commented 6 years ago

Adding a debounce capacitor on the switch resolved the issue that I was having with gpio not tracking properly for me. YMMV.

I'm new to HA, BUT, I highly doubt that HA running python scripts, on a raspberry pi, with no interrupt driven events, is a great place to to expect real time events. It sounds like you came up with a good workaround.

tomlut commented 6 years ago

HA does use edge driven interrupts on the gpio. Just not very well.

jkobie commented 6 years ago

HA uses a call back based on hass.bus.listen_once event. It is not a real time interrupt, as described here: https://www.socallinuxexpo.org/sites/default/files/presentations/Steven_Doran_SCALE_13x.pdf

tomlut commented 6 years ago

Is the callback not generated by an edge detect interrupt?:


        def read_gpio(port):
            """Read state from GPIO."""
            self._state = rpi_gpio.read_input(self._port)
            self.schedule_update_ha_state()

rpi_gpio.edge_detect(self._port, read_gpio, self._bouncetime)

https://github.com/home-assistant/home-assistant/blob/dev/homeassistant/components/binary_sensor/rpi_gpio.py

Edit/ also it does not have to be real-time. FIFO would be fine. It just has to register both edges reliably. Currently it only does the first edge reliably.

jkobie commented 6 years ago

How I understand the code, the setup sets a listener via hass.bus.listen_once.
rpio_gpio.edge_detect calls the callback when hass.buss changes state (up or down).

The event is at the mercy of hass.buss. What happens if hass.buss is busy off performing some other tasks?

tomlut commented 6 years ago

I know what happens. It misses the falling edge. It should be buffered or something. I'm just not able to produce a solution because my coding skill is wanting.

matkor commented 6 years ago

It misses the falling edge

In my case edge detection works reliable. What is not working is reading pin state inside edge detected handler. After pin change state, inside handler, wrong value as before change is read and set as state: self._state = rpi_gpio.read_input(self._port)

balloobbot commented 6 years ago

There hasn't been any activity on this issue recently. Due to the high number of incoming GitHub notifications, we have to clean some of the old issues, as many of them have already been resolved with the latest updates.

Please make sure to update to the latest Home Assistant version and check if that solves the issue. Let us know if that works for you by adding a comment :+1:

tomlut commented 6 years ago

I'm not in a position to test this until I can update to 0.70.0 (waiting on some custom panel updates). I'm currently on 0.69.1 and there is still a problem with the GPIO. Please do not close.

balloobbot commented 5 years ago

There hasn't been any activity on this issue recently. Due to the high number of incoming GitHub notifications, we have to clean some of the old issues, as many of them have already been resolved with the latest updates.

Please make sure to update to the latest Home Assistant version and check if that solves the issue. Let us know if that works for you by adding a comment :+1:

tomlut commented 5 years ago

This issue still exists and is rather important. Using a pi without being able to reliably use GPIO inputs is a major failing of HA. Please fix it.

Gompka commented 5 years ago

I am having this same issue with doorbell scenario as well, it always catches the on state but often fails to turn back off unless i hold for 1+ seconds.

andyculmer commented 5 years ago

I've just re-tested this issue having updated to 0.85.1 (Hass.io 142 - released on the 15th Jan 19) and unfortunately the problem still persists for me. I believe this includes the update of RPi.GPIO to 0.6.5; if so, it's a shame that's not addressed it.

CasperBonde commented 5 years ago

This is clearly a bug (or several actualy) in the binary_sensor code: https://github.com/home-assistant/home-assistant/blob/dev/homeassistant/components/rpi_gpio/binary_sensor.py

Issue 1: The code adds an edge detection to the GPIO pin, but uses the same call-back for both rising and falling edge, hence cannot destinguish between a callback caused by a rising and falling edge. Thefore it needs to read the actual state of the pin. (which leads to issue 2)

Issue 2: The code assumes the state of the GPIO have changed when the interrupt handler is called - but if (de)bounch is active, it might not have changed when it is read, as the call most likely will happen within the debounce timeout period - hence before the debounce times out and updates the state.

Issue 3: The state is read both in the interrupt handler and when update() is called from the HASS system. This means that if there is a delay due to other processing, the HASS system might miss either the rising or the falling event. I plan to use the GPIO's to controll a dimmer, that starts dimming when the button is pushed, and stops when it is released, hence it shall not miss either of the state changes.

Solution: The underlaying GPIO python library could be rewritten to provide a new interface to get a callback after the debounce timeout, which would make the binary_sensor.py callback code to work( but not fix all the issues). Alternatively a low debounch value could be used, and a delay could be added before actually reading the state and notifying the state-change - pseudo-code:

    def read_gpio(port):
        """Read state from GPIO."""
        delay_ms(self._bouncetime) <-- Change
        self._state = rpi_gpio.read_input(self._port)
        self.schedule_update_ha_state()

But as the HASS system works asynchronously, where the is_on() is read an arbitrary amount of time after a schedule_update_ha_state() have been issued, the only way to avoid missing an rising or falling edge event will be to queue the events and pop one of the queue for each call to is_on(). If the queue is empty, the last state should be returned:

    def read_gpio(port):
        """Read state from GPIO."""
        delay_ms(self._bouncetime) <-- change - ensure the delay function and _bouncetime is the same unit
        self._state_queue.put(rpi_gpio.read_input(self._port)) <-- change to add to queue
        self.schedule_update_ha_state()

    def update(self):
        """Update the GPIO state."""
        #self._state = rpi_gpio.read_input(self._port) <-- delete
        pass <-- Add

     def is_on(self):
        if !self._state_queue.empty(): <-- Add
            self._state = self._state_queue.get() <-- Add
        """Return the state of the entity."""
        return self._state != self._invert_logic

Unfortunately I have just started to use HASS, hence I neither know how to test the change, nor how to contribute it - hence the above is review comments.

Mikrofarad commented 5 years ago

Home Assistant 0.88.1 (Hass.io (docker) and it's still not working reliable :-( So I will use my (perfectly working with interrupt and debounce) python scripts and (try to) add publishing the state via mqtt in the future.

shyne99 commented 5 years ago

Hello, I've experienced this today. I was trying to use a small 3.3V power supply as a power loss sensor (since the rpi is backed by a UPS). But since no current was consumed by the PI, the discharge and the voltage drop was very slow, and the binary_sensor did not catch the change in state.

I've find a lot of threads in the forum and here on GitHub about people having issue with event not being fired reliably.

It took me 10 minutes to create a custom component that uses polling instead of change state event. (it is ugly, and I feel dirty just watching it, but it works for my use case)

To the maintainers : For those that don't want full real time response, and are good with polling with reasonable poll interval, it could be a solution to offer this as a potential setting. If this solution is acceptable to you, I can work make it clean and submit a PR if you want and think it makes sense

costaraculios commented 5 years ago

Applying a capacitor between GND and GPIO pin of binary sensor solves the problem. I putted a 1uF electrolytic capacitor or a SMD capacitor and with both works very stabil. Before that I had unreliable responses as well.

binary_sensor:
    platform: rpi_gpio
    ports:
      4: pibutton
stale[bot] commented 4 years ago

There hasn't been any activity on this issue recently. Due to the high number of incoming GitHub notifications, we have to clean some of the old issues, as many of them have already been resolved with the latest updates. Please make sure to update to the latest Home Assistant version and check if that solves the issue. Let us know if that works for you by adding a comment 👍 This issue now has been marked as stale and will be closed if no further activity occurs. Thank you for your contributions.

ve6rah commented 4 years ago

This problem still exists with the most recent version of HA. all the discussion here so far had been workarounds, not fixes.

greg2001 commented 4 years ago

Just posted a pull request which (hopefully) fixes the problem. No idea whether it'll be accepted or not, so, if you don't want to wait, create the "config/custom_components/rpi_gpio2" directory in your HA installation, extract the attached zip into, change the domain of your gpio binary sensors from "rpi_gpio" to "rpi_gpio2" and restart. It works fine for me; let me know if it does for you.

rpi_gpio2.zip

pergolafabio commented 4 years ago

will it also fix : https://github.com/home-assistant/home-assistant/issues/27724

?

ve6rah commented 4 years ago

looking at the description of your pull request, it appears that a very short trigger would still be missed (correct me if I'm wrong here). Could it possibly be feasible to assume that any edge trigger is a change of state (as you can't have an edge without a change) and mitigate the issue for all durations of trigger? Alternatively, could we introduce a second way of reading gpio events so people can trigger their automations based on an edge trigger, even if the state doesn't change?

greg2001 commented 4 years ago

looking at the description of your pull request, it appears that a very short trigger would still be missed (correct me if I'm wrong here)

Nothing is missed here. The very first edge is detected, the following are discarded for a couple of milliseconds ("bouncetime") - which is a desired behaviour (debouncing). If your trigger is not a mechanical contact and so doesn't bounce, you can disable debouncing by setting "bouncetime" to a very low value like "1" (unfortunately, setting it to "0" is currently not supported by HA's rpi_gpio) - in this case you'll detect every edge.

megapearl commented 4 years ago

Just posted a pull request which (hopefully) fixes the problem. No idea whether it'll be accepted or not, so, if you don't want to wait, create the "config/custom_components/rpi_gpio2" directory in your HA installation, extract the attached zip into, change the domain of your gpio binary sensors from "rpi_gpio" to "rpi_gpio2" and restart. It works fine for me; let me know if it does for you.

rpi_gpio2.zip

Finally something that is working, looking for a couple of years for a working solution, just installed it as custom_component and it isn't missing any triggers anymore! thank you!

TheFax commented 4 years ago

It's incredible that years later, such a trivial problem has not yet been officially solved.

muscrats commented 4 years ago

Just posted a pull request which (hopefully) fixes the problem. No idea whether it'll be accepted or not, so, if you don't want to wait, create the "config/custom_components/rpi_gpio2" directory in your HA installation, extract the attached zip into, change the domain of your gpio binary sensors from "rpi_gpio" to "rpi_gpio2" and restart. It works fine for me; let me know if it does for you.

rpi_gpio2.zip

Thank you Greg2001- This fixed my problem I’ve been stumped on..

TheFax commented 4 years ago

Since I need this functionality, during last three days I worked for a pseudo-solution. First of all sorry for my english: this is not my mother tongue. I'm here to collaborate.

The problem

Often Home Assistant reports a wrong state for GPIO boolean sensors.

From what I understood, there are more factors that causes the issue. I'll try to list them sorted by importance:

  1. if HomeAssistant "lose" an event (I'm talking about a rising or falling edge on a GPIO pin), the state of the binary sensor is not reliable, and nobody will try to update it until a new event (i.e. a new callback) will be received.

  2. everytime we have a callback from GPIO.add_event_detect(...) there is no reason to expect that the level of the GPIO when the callback has been executed has any relationship to the level which caused the callback.

  3. RPi.GPIO debouncing is not a real debouncing. The RPi.GPIO module does not really debounce switches. The "debounce" parameter is used to ignore all subsequent changes for the bouncetime after the first change is reported. A correct implementation would ignore all changes which were within bouncetime of each other (which would mean the first change would not be reported either).

  4. some users reported on the internet that the RPi.GPIO library is not flawless.

Possible solutions

1. Electronic way

It is always good thing to properly filter the signals input to a microcontroller. So the problem can be mitigated/reduced in an electronic way, adding an RC filter on every GPIO input. I have not tested this solution because the problem is very big from my point of view: when the input pin was OFF, HomeAssistans shows me it was ON, and this is totally unacceptable, with or without RC filter. I can not trust a software that report me a wrong information.

2. Polling

Switch from an event/edged driven component to a polling component. This is what I made. I know that this solution is not the best because it add more work to the processor. But actually it is the only way to keep up-to-date the status of the binary sensor. Note that a lot of work-arounds used by the users actually are based to polling solutions. To implement this solution is enough to modify a single line on the source code: File: https://github.com/home-assistant/core/blob/dev/homeassistant/components/rpi_gpio/binary_sensor.py

72    @property
73    def should_poll(self):
74        """No polling needed."""
75        return False

Change the line 75 to:

75        return True

We can also set "scan_interval: 10" on our config.yaml so the status will be always correct at least one time every 10 seconds.

3. Wait before read logic level

Perhaps reading the logic level of the pin for some times, intervalled by 200, 400, 600ms after an event occourred. I have not tried this solution yet, because I don't know HomeAssistant source code so well to implement that.

4. Switch to gpiozero

Changing the library used to interfacing with GPIO pins. The current component is using RPi.GPIO. Probably gpiozero is better. I have not tested this way, but many people on interner reports that gpiozero is better than RPi.GPIO.

Related and useful links

StackExchange question: debouncing buttons with rpi gpio - too many events detected

RPi.GPIO guide

Hassio Community: Problems reading gpio pins

Hassio Community: a workaround for GPIO issues

Possible related to: #31788

aukedejong commented 4 years ago

Just posted a pull request which (hopefully) fixes the problem. No idea whether it'll be accepted or not, so, if you don't want to wait, create the "config/custom_components/rpi_gpio2" directory in your HA installation, extract the attached zip into, change the domain of your gpio binary sensors from "rpi_gpio" to "rpi_gpio2" and restart. It works fine for me; let me know if it does for you.

rpi_gpio2.zip

Works for me. Thanks. Any chance your pull request will be merged?

TheFax commented 4 years ago

I'm not sure this solution will be merged because (like somebody says in #31788) it is based on threading and "Let's not use a thread but instead schedule it on the event loop". I think a solution not based on threading has already been prepared. I am also waiting for merging. And I'm happy that somebody keep this issue "alive".

hqmatics commented 4 years ago

How can I apply the solution from the zip file? I am running HA in docker. Only detects low state, never goes up again.

megapearl commented 4 years ago

I'm still using this solution for a couple of months now, and is working quite reliable, sometimes a gpio pin gets stuck (once a month or so) and is missing any trigger, and I need to restart homeassistant to get it working again. Before I used this solution I tried the 'polling' solution but that didn't work out for me, it was still unreliable and it misses most of the triggers.

EDIT: Since Homeassistant v0.110.0 an additional warning is logged:

BinarySensorDevice is deprecated, modify RPiGPIOBinarySensor to extend BinarySensorEntity

rarroyo6 commented 4 years ago

Just posted a pull request which (hopefully) fixes the problem. No idea whether it'll be accepted or not, so, if you don't want to wait, create the "config/custom_components/rpi_gpio2" directory in your HA installation, extract the attached zip into, change the domain of your gpio binary sensors from "rpi_gpio" to "rpi_gpio2" and restart. It works fine for me; let me know if it does for you.

rpi_gpio2.zip

Thanks, this worked for me. It's incredible that an issue this old, and this simple, has not been fixed yet. I was going to use the GPIO pins for some alarm inputs, and found out that it did not work with mechanical switches. It was frustrating, because this should have been straight forward. There is even a bouncetime setting in the yaml, but it does not work.

HugoFJ commented 4 years ago

Can't thank you enough for creating rpi_gpio2, I've been pulling my hair out!!

alexis-via commented 3 years ago

After many hours struggling desperately with my new push button connected on the GPIO pins of my raspberry PI, questioning the fundamentals of electricity and thinking that all this was irrational, I found this bug report! My first reaction was : "great, I found the cause of my problem... at last !". My second reaction was "I can't believe that something as basic as reading a dry contact via the GPIO of a raspberry is buggy, and the bug has been open for almost 3 years!" My third reaction was "OK... should I try the proposed workarounds, or should I go away from Home Assistant and select another home automation software ?"

What is strange when you read this bug report: it seems technical solutions are identified, but the few PR have been rejected and it not clear if there are current attemps at fixing this horrible bug in the official HA source tree. It seems that the HA maintainers are not interested in solving this bug... maybe they don't use raspberry PIs ?

How can I help to move forward on this ? Should we maintain rpi_gpio2 as a community addon and make it a well-known solution ? Should we try a new PR ?