joan2937 / pigpio

pigpio is a C library for the Raspberry which allows control of the General Purpose Input Outputs (GPIO).
The Unlicense
1.45k stars 406 forks source link

DHT22.py sample code returns: AttributeError: 'NoneType' object has no attribute 'send' #16

Closed HeatfanJohn closed 9 years ago

HeatfanJohn commented 9 years ago

I'm trying to use your DHT22.py sample code to return a single temperature and humidity reading from an AM2302 sensor. When I modified your code to only return one reading and then exit I received the following errors:

52.9 24.1 0.18 0 0 0 0
Error in atexit._run_exitfuncs:
Traceback (most recent call last):
  File "/usr/lib/python2.7/atexit.py", line 24, in _run_exitfuncs
    func(*targs, **kargs)
  File "./get_temp_DHT22.py", line 239, in cancel
    self.pi.set_watchdog(self.gpio, 0)
  File "/usr/local/lib/python2.7/dist-packages/pigpio.py", line 1410, in set_watchdog
    self.sl, _PI_CMD_WDOG, user_gpio, int(wdog_timeout)))
  File "/usr/local/lib/python2.7/dist-packages/pigpio.py", line 806, in _pigpio_command
    sl.s.send(struct.pack('IIII', cmd, p1, p2, 0))
AttributeError: 'NoneType' object has no attribute 'send'
Error in sys.exitfunc:
Traceback (most recent call last):
  File "/usr/lib/python2.7/atexit.py", line 24, in _run_exitfuncs
    func(*targs, **kargs)
  File "./get_temp_DHT22.py", line 239, in cancel
    self.pi.set_watchdog(self.gpio, 0)
  File "/usr/local/lib/python2.7/dist-packages/pigpio.py", line 1410, in set_watchdog
    self.sl, _PI_CMD_WDOG, user_gpio, int(wdog_timeout)))
  File "/usr/local/lib/python2.7/dist-packages/pigpio.py", line 806, in _pigpio_command
    sl.s.send(struct.pack('IIII', cmd, p1, p2, 0))
AttributeError: 'NoneType' object has no attribute 'send'

Here is my main which only makes one sensor reading.

if __name__ == "__main__":
   import time
   import pigpio

   pi = pigpio.pi()

#  s = DHT22.sensor(pi, 22, LED=16, power=8)
   s = sensor(pi, 4 )   # My AM2302 is on GPIO Pin #4

   s.trigger()
   time.sleep(0.2)

   print("{} {} {:3.2f} {} {} {} {}".format(
         s.humidity(), s.temperature(), s.staleness(),
         s.bad_checksum(), s.short_message(), s.missing_message(),
         s.sensor_resets()))

   s.cancel()
   pi.stop()

I made the following change to the cancel method of your DHT22 sensor class. I added an if to ensure that sl.s wasn't None before call set_watchdog. This appears to work, but I'm sure that this violates all the principles of object-oriented programming.

   def cancel(self):
      """Cancel the DHT22 sensor."""

      if self.pi.sl.s is not None:
         self.pi.set_watchdog(self.gpio, 0)

      if self.cb != None:
         self.cb.cancel()
         self.cb = None

Is there a better way to do this?

Also, what actions should I take to ensure that I got a valid reading?

joan2937 commented 9 years ago

Leave the class as it originally was but remove the import atexit and the atexit call.

Change the main to look like the following.

   import pigpio

   import DHT22

   pi = pigpio.pi()

   s = DHT22.sensor(pi, 4)

   s.trigger()

   time.sleep(0.2)

   print("{} {} {:3.2f} {} {} {} {}".format(
      s.humidity(), s.temperature(), s.staleness(),
      s.bad_checksum(), s.short_message(), s.missing_message(),
      s.sensor_resets()))

   s.cancel()

   pi.stop()

The reading will be good provided bad_checksum(), short_message(), missing_message(), and sensor_resets() are all zero.

HeatfanJohn commented 9 years ago

That resolved the problem. Thank you!

Is the atexit code a safety mechanism to ensure that cancel is always performed?

What would happen if the program exited without calling cancel?

Also, I have noticed that on my gen 1 Pi model B that pigpiod always uses around 8% CPU as reported by TOP.

Is that normal?

On Tue, Jul 28, 2015 at 5:30 PM, joan2937 notifications@github.com wrote:

Leave the class as it originally was but remove the import atexit and the atexit call.

Change the main to look like the following.

import pigpio

import DHT22

pi = pigpio.pi()

s = DHT22.sensor(pi, 4)

s.trigger()

time.sleep(0.2)

print("{} {} {:3.2f} {} {} {} {}".format( s.humidity(), s.temperature(), s.staleness(), s.bad_checksum(), s.short_message(), s.missing_message(), s.sensor_resets()))

s.cancel()

pi.stop()

The reading will be good provided bad_checksum(), short_message(), missing_message(), and sensor_resets() are all zero.

— Reply to this email directly or view it on GitHub https://github.com/joan2937/pigpio/issues/16#issuecomment-125759028.

joan2937 commented 9 years ago

The atexit in the class was really to make sure the watchdog was switched off if the main program terminated (the expectation being that most often the class would be used in a forever loop). If the watchdog was left switched on it might confuse a later script which just expected to get level changes.

8% CPU usage is about right on a non-Pi2. That's the base level for the overhead of the 200 thousand gpio reads per second made by the daemon. However that's a fixed overhead regardless of how many gpios are used for PWM or servo pulses and is very quickly more efficient than trying to service anywhere near that number of gpio interrupts.