Enviro pHat and daemonizing


#1

I am running into a problem using my enviro pHat from a daemon process. I am hoping that someone here can help me figure out what I am missing. I am trying to read the light sensor. The daemon dies w/ the following error:

pi@enviro0:~/src cat /var/tmp/tst03.err Traceback (most recent call last): File "tst03", line 32, in <module> createDaemon() File "tst03", line 29, in createDaemon doIt() File "tst03", line 15, in doIt lux = light.light() File "/usr/lib/python2.7/dist-packages/envirophat/tcs3472.py", line 71, in light return self.raw()[CH_CLEAR] File "/usr/lib/python2.7/dist-packages/envirophat/tcs3472.py", line 83, in raw c = self.i2c_bus.read_word_data(ADDR, REG_CLEAR_L) IOError: [Errno 25] Inappropriate ioctl for device pi@enviro0:~/src

The source is this:

pi@enviro0:~/src $ cat tst03
#!/usr/bin/env python

import daemon, sys, datetime
from envirophat import light

def doIt():
file = open (’/var/tmp/tst03.out’, ‘w+’)

while True:
lux = 0
tstamp = datetime.datetime.now().isoformat()
file.write("%30s %5d\n" % (tstamp, lux))
file.flush()
tstamp = datetime.datetime.now().isoformat()
lux = light.light()
file.write("%30s %5d\n" % (tstamp, lux))
file.flush()
time.sleep(10)

file.close()

def createDaemon():
dae = daemon.DaemonContext()
dae.stdout = open (’/var/tmp/tst03.out’, ‘w+’)
dae.stderr = open (’/var/tmp/tst03.err’, ‘w+’)
with dae:
doIt()

if name==“main”:
createDaemon()
pi@enviro0:~/src $

I have no problems if I do not try to daemonize the process; the pHat works as expected. What is it about my daemon process that causes the read failure from the i2c bus? FWIW, I am running jessie and obviously using python 2.7.

pi@enviro0:~/src lsb_release -a No LSB modules are available. Distributor ID: Raspbian Description: Raspbian GNU/Linux 8.0 (jessie) Release: 8.0 Codename: jessie pi@enviro0:~/src uname -a
Linux enviro0 4.9.35+ #1014 Fri Jun 30 14:34:49 BST 2017 armv6l GNU/Linux
pi@enviro0:~/src $

Thanks,


#2

You can surround your code in backticks to preserve formatting:

```
like so
```

It’ll help us read it!

Honestly I don’t know what’s going on in this case- I tend to write my own fork code, for better or worse. I wonder if you’re ending up with two processes trying to access the device simultaneously?


#3

Apologies for making the code hard to read

#!/usr/bin/env python

import daemon, sys, datetime
from envirophat import light

def doIt():
  file = open ('/var/tmp/tst03.out', 'w+')

  while True:
    lux = 0
    tstamp = datetime.datetime.now().isoformat()
    file.write("%30s %5d\n" % (tstamp, lux))
    file.flush()
    tstamp = datetime.datetime.now().isoformat()
    lux = light.light()
    file.write("%30s %5d\n" % (tstamp, lux))
    file.flush()
    time.sleep(10)

  file.close()

def createDaemon():
  dae = daemon.DaemonContext()
  dae.stdout = open ('/var/tmp/tst03.out', 'w+')
  dae.stderr = open ('/var/tmp/tst03.err', 'w+')
  with dae:
    doIt()

if __name__=="__main__":
  createDaemon()

I would like to avoid writing my own fork code b/c that means writing all of the signal processing, etc., etc., and I think the whole idea behind the daemon library is to do the right thing with defaults as well as allow customization of events as necessary.

With regards to multiple processes trying to access the device simultaneously: if that was happening then I would expect the return code to be a EBUSY or EAGAIN not an “Inappropriate ioctl for device” message (EIO?). The line throwing the error is a read of the sensor:

  File "/usr/lib/python2.7/dist-packages/envirophat/tcs3472.py", line 83, in raw
    c = self.i2c_bus.read_word_data(ADDR, REG_CLEAR_L)
IOError: [Errno 25] Inappropriate ioctl for device

REG_CLEAR_L is effectively an integer (if I am reading this correctly; I’m new to python but this looks like a bitwise or)

ADDR = 0x29

REG_CMD = 0b10000000
REG_CMD_AUTO_INC = 0b00100000
REG_CLEAR_L = REG_CMD | REG_CMD_AUTO_INC | 0x14
REG_RED_L = REG_CMD | REG_CMD_AUTO_INC | 0x16
REG_GREEN_L = REG_CMD | REG_CMD_AUTO_INC | 0x18
REG_BLUE_L = REG_CMD | REG_CMD_AUTO_INC | 0x1A

so my guess here is that ADDR is not being passed correctly resulting in an IO operation (read) from a non-readable address on the sensor or access to the i2c bus is not properly setup by the library b/c it was relying on inheritance from the calling process to manage setting up access to the i2c bus, and that is not provided when using CreateDaemon. I am sorta stuck on how to figure this out.


#4

Is this Python 2 or 3?

Not having much luck in 3:

pi@raspberrypi:~ $ python3 dodiz.py
Traceback (most recent call last):
  File "dodiz.py", line 3, in <module>
    import daemon, sys, datetime
  File "/usr/local/lib/python3.5/dist-packages/daemon.py", line 70
    os.umask(022)   # Don't allow others to write
               ^
SyntaxError: invalid token

#5

Hmm, Python 2.x at least loads the module, but:

pi@raspberrypi:~ $ python dodiz.py
['__builtins__', '__doc__', '__file__', '__name__', '__package__', 'basic_daemonize', 'checkPID', 'daemonize', 'errno', 'os', 'sys', 'writePID']
Traceback (most recent call last):
  File "dodiz.py", line 32, in <module>
    createDaemon()
  File "dodiz.py", line 25, in createDaemon
    dae = daemon.DaemonContext()
AttributeError: 'module' object has no attribute 'DaemonContext'

Have I got the wrong “daemon”?


#6

Apparently I did =/

pi@raspberrypi:~ $ cat /var/tmp/tst03.err
Traceback (most recent call last):
  File "dodiz.py", line 32, in <module>
    createDaemon()
  File "dodiz.py", line 29, in createDaemon
    doIt()
  File "dodiz.py", line 17, in doIt
    lux = light.light()
  File "/usr/lib/python2.7/dist-packages/envirophat/tcs3472.py", line 71, in light
    return self.raw()[CH_CLEAR]
  File "/usr/lib/python2.7/dist-packages/envirophat/tcs3472.py", line 83, in raw
    c = self.i2c_bus.read_word_data(ADDR, REG_CLEAR_L)
IOError: [Errno 25] Inappropriate ioctl for device

At least now I can replicate your problem!


#7

Okay it’s an issue with context, I believe. I don’t know why it results in the error it does, but you need to move:

from envirophat import light

into def doIt(): outside of the while loop.

That should get you up and running!


#8

Python 2.7

I’m traveling right now but will respond in full when home.


#9

Python 2.7 to be exact :-)


#10

So… maybe I need a little edumacation ;-) on scope and python. I assumed (wrongly?) that since I am importing “globally” a child process would inherit everything from the parent process. Is that not the case in python or is this a oddity with daemon?

TIA.


#11

Hmm… didn’t work… same error.

Here’s code + output

pi@enviro0:~/src $ cat tst03

#!/usr/bin/env python

import daemon, sys, datetime
from envirophat import light

def doIt():
  from envirophat import light
  file = open ('/var/tmp/tst03.out', 'w+')

  while True:
    lux = 0
    tstamp = datetime.datetime.now().isoformat()
    file.write("%30s %5d\n" % (tstamp, lux))
    file.flush()
    tstamp = datetime.datetime.now().isoformat()
    lux = light.light()
    file.write("%30s %5d\n" % (tstamp, lux))
    file.flush()
    time.sleep(10)

  file.close()

def createDaemon():
  dae = daemon.DaemonContext()
  dae.stdout = open ('/var/tmp/tst03.out', 'w+')
  dae.stderr = open ('/var/tmp/tst03.err', 'w+')
  with dae:
    doIt()

if __name__=="__main__":
  createDaemon()

pi@enviro0:~/src $ rm -rf /var/tmp/tst03*

pi@enviro0:~/src $ python tst03

pi@enviro0:~ $ tail /var/tmp/tst03*
==> /var/tmp/tst03.err <==
createDaemon()
File “tst03”, line 28, in createDaemon
doIt()
File “tst03”, line 16, in doIt
lux = light.light()
File “/usr/lib/python2.7/dist-packages/envirophat/tcs3472.py”, line 71, in light
return self.raw()[CH_CLEAR]
File “/usr/lib/python2.7/dist-packages/envirophat/tcs3472.py”, line 83, in raw
c = self.i2c_bus.read_word_data(ADDR, REG_CLEAR_L)
IOError: [Errno 25] Inappropriate ioctl for device

==> /var/tmp/tst03.out <==
2017-11-07T04:14:03.183298 0
pi@enviro0:~ $

Would you mind posting your working code so that I may try it on my system?


#12

You might need to also drop from envirophat import light from the main part of the code.

I’ll try my code again and see if I wan’t imagining things.


#13

Ah, that was the problem. Begs interesting questions about inheritance but this is probably not the right forum for such questions. :-)

Thanks for the help folks.


#14

Aye, I’d love to know the answers if you get them.

I don’t quite know how the forking works- despite implementing my own step-by-step fork for phatbeatd, but it’s clear some things are not coming along for the ride.

This is obviously something to watch out for in future, and a good caveat to know!


#15

What I know about forking comes from programming in c. In c the child process inherits a complete copy of the parent process memory/stack but has its own set of file descriptors for stdin, stdout, and stderr at the time of the fork call. To share those file descriptors between parent and child(ren) you need to play games with closing/opening/duplicating them prior to calling fork. I expect python to behave in a similar fashion but that may not be an entirely valid expectation. I agree that some things are not coming along for the ride, this particular incident is evidence of it, and I suspect this is really a bug and not a feature; to be a feature would have nasty implications on python as a language; as a programmer do you really want to have to import a subset of libs in your 1000s of defined routines because those libs can not be imported globally? I doubt it.