Adafruit_CircuitPython_DHT: Unable to set line 4 to input

I’m trying to make a Raspberry Pi 3 REST API that provides temperature and humidity with DHT22. The whole code:

from flask import Flask, jsonify, request
from sds011 import SDS011
from adafruit_dht import DHT22
import board
import os
import time

app = Flask(__name__)
dht = DHT22(board.D4)

def get_dht_data():
    while True:
        try:
            temperature, humidity = dht.temperature, dht.humidity
            print(temperature, humidity)
            if temperature is not None and humidity is not None:
                return temperature, humidity
            else:
                raise
        except:
            time.sleep(0.5)

@app.route('/', methods=['GET'])
def status():
    temperature, humidity = get_dht_data()

    return jsonify({
        'temperature': temperature,
        'humidity': humidity
    })

if __name__ == '__main__':
    app.run(debug=True)

However, when I start server, it shows message

Unable to set line 4 to input

and temperature and humidity is always None. If I don’t run flask app but just DHT code, it works.

About this issue

  • Original URL
  • State: open
  • Created 5 years ago
  • Reactions: 2
  • Comments: 46 (2 by maintainers)

Most upvoted comments

…it seems to work fine when I first boot up the raspberry pi and I can run the test code once and it works… After that if I try to run to the test code a second time I get the same error…

Not even using Flask - just this script, is enough to make the RPI4 hang:

import time
import board
import adafruit_dht

dhtDevice = adafruit_dht.DHT22(board.D4)

try killing any ‘libgpiod_pulsein’ processes

The solution that I found was: dhtDevice = adafruit_dht.DHT22(board.D4, use_pulseio=False)

Acording to the group comments, this declaration use_pulseio=False disable the PulseIn functionality.

In my case, a program executes a python script parallels. I got same error from this simple example.

time seq 1 5 | xargs -I ZZ -P 5 python3 /home/pi/dht_simpletest.py

dht_simpletest.py means below. https://github.com/adafruit/Adafruit_CircuitPython_DHT/blob/master/examples/dht_simpletest.py

pulseio does not work for my situation. So I changed code like below.

# dhtDevice = adafruit_dht.DHT22(board.YourPin)
dhtDevice = adafruit_dht.DHT22(board.YourPin, False)

It works for me.

First of all, if you don’t have the pulseio module available (for whatever reason), adafruit_dht will use BitBanging and this bug will not appear.

I’m also affected by this bug as my temperature logger script (5min cronjob) suddenly stopped working after 23 hours, so I did some analysis:

Following the string

The output string Unable to set line X to input is generated by the executable libgpiod_pulsein at

  // set to an input
  if (gpiod_line_request_input(line, consumername) != 0) {
    printf("Unable to set line %d to input\n", offset);
    exit(1);
  }

adafruit/libgpiod_pulsein:/src/libgpiod_pulsein.c#L237-L241.

This executable is called in PulseIn.__init__() by self._process = subprocess.Popen(cmd) adafruit/Adafruit_Blinka:/src/adafruit_blinka/microcontroller/bcm283x/pulseio/PulseIn.py#L65

PulseIn ?

A quick look into PulseIn.py reveals that PulseIn provides the __enter__() and __exit__() functions in order to be used as a context manager using the with statement.

    def deinit(self):
        """Deinitialises the PulseIn and releases any hardware and software
        resources for reuse."""
        # Clean up after ourselves
        self._process.terminate()
        procs.remove(self._process)
        self._mq.remove()
        queues.remove(self._mq)

    def __enter__(self):
        """No-op used by Context Managers."""
        return self

    def __exit__(self, exc_type, exc_value, tb):
        """Automatically deinitializes the hardware when exiting a context."""
        self.deinit()

PulseIn usage in adafruit_dht

As adafruit_dht is not using PulseIn as a context manager, I guess it should implement the functionality of __exit__() itself by calling PulseIn.deinit() at the approriate moment (e.g. in DHTBase.__exit__()? This would ensure that there is no long-running libgpiod_pulsein process left behind after the python script exited.

Best wishes, kerel

PS:

# We don't use a context because linux-based systems are sluggish
# and we're better off having a running process
if _USE_PULSEIO:
    self.pulse_in = PulseIn(self._pin, 81, True)

adafruit/Adafruit_CircuitPython_DHT:adafruit_dht.py#L66-L67 Now I’m not sure if the running process was maybe intentional (but I can’t see why this would be a good idea…)?

In my case, pgrep libgpiod_pulsein returned nothing. Reboot didn’t change anything either.

Fortunately, changing the PIN to D18 worked.

Still no idea what is blocking GPIO4 though.

Did you enabled 1-wire interface when using GPIO4? Try disable 1-wire interface from raspi-config when using gpio4 with adafruit python lib.

Reverted to the old lib just now, that one’s working a treat.

Yeah it is happening even to their sample code. Just try to run the code. Stop it. They run it again. On May 3, 2020, 7:50 AM +0530, Alexandre Lemaire notifications@github.com, wrote:

Not even using Flask - just this script, is enough to make the RPI4 hang: import time import board import adafruit_dht

dhtDevice = adafruit_dht.DHT22(board.D4) — You are receiving this because you commented. Reply to this email directly, view it on GitHub, or unsubscribe.

Thank you, ropering! I cannot try it now, but will soon. In the meantime, I have found a workaround, which I will resort asap.

for proc in psutil.process_iter():
        if proc.name() == 'libgpiod_pulsein' or proc.name() == 'libgpiod_pulsei':
            proc.kill()

it works for me

In my case, a program executes a python script parallels. I got same error from this simple example.

time seq 1 5 | xargs -I ZZ -P 5 python3 /home/pi/dht_simpletest.py

dht_simpletest.py means below. https://github.com/adafruit/Adafruit_CircuitPython_DHT/blob/master/examples/dht_simpletest.py

pulseio does not work for my situation. So I changed code like below.

# dhtDevice = adafruit_dht.DHT22(board.YourPin)
dhtDevice = adafruit_dht.DHT22(board.YourPin, False)

It works for me.

It works for me to. But I don’t know why.

This issue will be fixed by Pull Request #46 with implementing what is described in #27 (comment).

I didn’t see the PR, thanks. The dhtDevice.exit() method solved the issue.

So this is a super hacky work around to this problem, all I’ve done is force an ImportError when the adafruit_dht.py script attempts to import PulseIn. Once it fails to import PulseIn it will attempt to use bitbang instead, this works for me but I guess it depends on your specific use case whether or not it will work for you

Anyway, I modified line 37 in the adafruit_dht.py file located in dist-packages (remember to sudo here as this file is protected). I changed “from pulseio import PulseIn” to “from pulseio import PulseIn1” this then causes an ImportError and then PulseIo is not used which means the pin won’t be blocked after exiting the code

As I said, it’s hacky but for now it’s a work around to this problem

I tried using the old repo but for some strange reason it wouldn’t work on my RPi 4, it kept trying to run the BeagleBone code which obviously does not work.

Hopefully the Adafruit team can solve this properly in a future release

Short answer

Remove debug=True and enjoy. 😃

What was going on?

This problem drove me nuts but I think I found the root cause! In my case, I was encapsulating the DHT22 object in another object, like so:

...
class DHT22Custom:
    def __init__(self):
        print("**** BUILDING by {0}!".format(threading.currentThread().getName()))
        self.dht_device = adafruit_dht.DHT22(board.D17)
...

My main.py looked like:

import RPi.GPIO as GPIO
from sensor.config.app_config import create_app

if __name__ == '__main__':
    app = create_app()         # builds DHT22Custom inside
    app.run(debug=True)
    print("Cleaning up GPIO before exiting...")
    GPIO.cleanup()

And look what an interesting output I got:

root@05600e5:/app# python -m sensor.main
**** BUILDING by MainThread!
 * Serving Flask app "FlaskWS" (lazy loading)
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: on
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
 * Restarting with stat
**** BUILDING by MainThread!
Unable to set line 17 to input

The MainThread was initializing my object twice! How was that? Well, if you look at the documentation of Flask’s run(), you’ll see the following:

If the :attr:`debug` flag is set the server will automatically reload
 for code changes and show a debugger in case an exception happened.

So, it seems that Flask just relaunches the application or something like this. Not clear to me, to be honest. But well, if you just remove debug, you’ll see something like:

root@05600e5:/app# python -m sensor.main
**** BUILDING by MainThread!
 * Serving Flask app "FlaskWS" (lazy loading)
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: off
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

I hope this helps. Happy coding!