jperkin / node-rpio

Raspberry Pi GPIO library for node.js
857 stars 123 forks source link

Weird bug with polling #106

Closed alexbjorlig closed 4 years ago

alexbjorlig commented 4 years ago

I have used most of the day trying to hunt down a problem with polling. I am currently on this setup:

If I deploy code that uses the polling example, the process stops and the raspberry is dead. I am forced to create a new SD card image. I can't get any logs, I have tried 🤷🏻‍♂️

So after using most of the day hunting down the reason for the crash, the solution was easy fixed with Rxjs:

const { interval } = require('rxjs');
const { map, filter, bufferCount } = require('rxjs/operators');

// Reading from the pin every 50ms
const button1$ = interval(50).pipe(
    map(() => rpio.read(buttonPlay1)),
    bufferCount(3),
    // In this case I am looking for a long button press
    filter(v => v.every(x => x === 0)),
    map(() => 'button1 pressed!')
)

With this polling solution, everything works. So if this is strictly speaking a bug with node-rpio - or only caused when used on Balena? I don't know - but leaving this here if others should have the same problem.

KryQ commented 4 years ago

i can confirm that even simplest poll from examples freezes whole raspberry. no matter pu_dn pu_up

Rpi 3+ Node v12

jperkin commented 4 years ago

I can't reproduce this on my RPi3 with Node v12, the button.js example works as expected.

A crash sounds like you might have your wiring incorrect, or overpowering the pin. What is your schematic?

For reference, using the button.js example, you'd expect to have a 3.3V going via a 10kohm resistor into one side of the switch, and then the other side plugged into pin 11. You'd then see something like:

$ sudo node examples/button.js 
Button event on P11 (button currently pressed)
Button event on P11 (button currently released)
Button event on P11 (button currently pressed)
Button event on P11 (button currently released)
jperkin commented 4 years ago

For reference:

$ npm ls | grep rpio
rpio@1.2.3 /home/pi/git/node-rpio

$ node --version
v12.11.1

$ cat /proc/device-tree/model 
Raspberry Pi 2 Model B Rev 1.1

I was mistaken saying RPi3 in my previous comment - I will try on the RPi3 in a bit just in case there is some hardware difference in the new bcm2835 update.

jperkin commented 4 years ago

RPi3 fine too:

$ npm ls | grep rpio
rpio@1.2.3 /home/pi/git/node-rpio

$ node --version
v12.12.0

$ cat /proc/device-tree/model 
Raspberry Pi 3 Model B Rev 1.2

$ sudo node examples/button.js
Button event on P11 (button currently pressed)
Button event on P11 (button currently released)
Button event on P11 (button currently pressed)
Button event on P11 (button currently released)

I unfortunately don't have any 64-bit installs or RPi4 to test on (yet), so would love to have it confirmed either way if they are ok.

alexbjorlig commented 4 years ago

@jperkin - could very much be incorrect schematics - I used the internal pull up resistor of the Pi. I am however not really experienced with schematics and physical computing. I will provide a full reproducible example with the schematics (preferably without Balena, and running on a raspberry pi 3), as soon as I have the time 😅

Thanks for taking your time to look into it :)

jperkin commented 4 years ago

Hmm, if you're using the internal resistor there might be a difference in the RPi4 looking at https://github.com/jperkin/node-rpio/blob/master/src/bcm2835.c#L585-L638. I'll need to take a look at implementing that properly.

KryQ commented 4 years ago

from what i can tell my schematic was ok. pin was internally pulled up and button was in series with 1k res connected to GND. But i wasn't using sudo for polling. is it necessary?

alexbjorlig commented 4 years ago

I actually ended up using a RPi3 I had laying around, because my case did not take into account the massive amount of heat the RPi4 generates. I will come back later today with the schematics. Do you have the schematic for examples/button.js?

jperkin commented 4 years ago

Here's an unbuilt and untested patch:

diff --git a/src/bcm2835.c b/src/bcm2835.c
index 2d1f6c8..5f4c2cf 100644
--- a/src/bcm2835.c
+++ b/src/bcm2835.c
@@ -630,9 +630,10 @@ void bcm2835_gpio_set_pud(uint8_t pin, uint8_t pud)
     } else
     {
     bcm2835_gpio_pud(pud);
-    delayMicroseconds(10);
+    /* rpio patched to avoid requiring root access */
+    usleep(10);
     bcm2835_gpio_pudclk(pin, 1);
-    delayMicroseconds(10);
+    usleep(10);
     bcm2835_gpio_pud(BCM2835_GPIO_PUD_OFF);
     bcm2835_gpio_pudclk(pin, 0);
 }
diff --git a/src/rpio.cc b/src/rpio.cc
index 45159c0..3bf6f23 100644
--- a/src/rpio.cc
+++ b/src/rpio.cc
@@ -124,18 +124,7 @@ NAN_METHOD(gpio_pud)
    uint32_t pin(Nan::To<uint32_t>(info[0]).FromJust());
    uint32_t pud(Nan::To<uint32_t>(info[1]).FromJust());

-   /*
-    * We use our own version of bcm2835_gpio_set_pud as that uses
-    * delayMicroseconds() which requires access to the timers and
-    * therefore /dev/mem and root.  Our version is identical, except for
-    * using usleep() instead.
-    */
-   bcm2835_gpio_pud(pud);
-   usleep(10);
-   bcm2835_gpio_pudclk(pin, 1);
-   usleep(10);
-   bcm2835_gpio_pud(BCM2835_GPIO_PUD_OFF);
-   bcm2835_gpio_pudclk(pin, 0);
+   bcm2835_gpio_set_pud(pin, pud);
 }

 NAN_METHOD(gpio_event_set)
jperkin commented 4 years ago

There's also possibly a timing issue with setting up input pins, as I'm configuring the pud prior to enabling the pin, this diff might help that too (again untested and unbuilt):

diff --git a/lib/rpio.js b/lib/rpio.js
index b644c7a..ea10d45 100644
--- a/lib/rpio.js
+++ b/lib/rpio.js
@@ -522,9 +522,10 @@ rpio.prototype.open = function(pin, mode, init)

    switch (mode) {
    case rpio.prototype.INPUT:
+       bindcall2(binding.gpio_function, gpiopin, rpio.prototype.INPUT);
        if (init !== undefined)
            bindcall2(binding.gpio_pud, gpiopin, init);
-       return bindcall2(binding.gpio_function, gpiopin, rpio.prototype.INPUT);
+       return;
    case rpio.prototype.OUTPUT:
        if (init !== undefined)
            bindcall2(binding.gpio_write, gpiopin, init);
jperkin commented 4 years ago

So after experimenting a little, it looks like accessing the pud memory area requires root, and I think the crashes are because the puds were silently never enabled and thus the voltages are going to be incorrect. It's also possible even when running as root to run into problems, for example if you try to hit a switch before the setup is complete (the example doesn't have any indication that it's ready).

I'll modify the library to warn about this.

jperkin commented 4 years ago

Ok I figured this out, it's an old issue described in https://github.com/raspberrypi/linux/issues/2550 and https://github.com/stianeikeland/go-rpio/issues/35 and elsewhere. Basically the tl;dr is you need to add:

dtoverlay=gpio-no-irq

to /boot/config.txt and reboot. Once you've done that it should work perfectly, and as non-root (that was a red herring). I've tested this out and it works great for me.

I'll put together a final patch for the RPi4 issue and will update the README to recommend users add that line regardless (I don't know of any drawbacks).

jperkin commented 4 years ago

This should hopefully all be fixed in v1.3.0. If there are still problems on the RPi4 let me know and we'll take a look in a new issue.