timofurrer / w1thermsensor

A Python package and CLI tool to work with w1 temperature sensors like DS1822, DS18S20 & DS18B20 on the Raspberry Pi, Beagle Bone and other devices.
MIT License
493 stars 113 forks source link

Beaglebone Black compatibility (Debian, 3.8 Kernel) #16

Closed cautiousCentaur closed 7 years ago

cautiousCentaur commented 8 years ago

I've gotten this library to work on my beaglebone, but I had to manually load a custom device tree overlay before importing this library. Otherwise, the way this module checks for properly loaded module silently fails. This module checks for existence of a folder as a proxy for module being loaded, but on beaglebone, the folder exists regardless, so this is why it silently does not work. Manually loading your own dto will solve problem and code works fine after that. I can supply additional details if anyone wants, but I'm too busy at the moment. I didn't have experience with device tree overlays before this, so it was quite the learning experience, but honestly the fix is easy now that I know what to do.

timofurrer commented 8 years ago

Are you talking about overlay filesystems? Like aufs and overlayfs?

If you could provide some more information about how you solved it would be awesome. The thing though is, that I don't have a beaglebone to test - Can you provide a fix? Or I can implement it on a different branch and you could test it from there before I release.

cautiousCentaur commented 8 years ago

I'm not talking aufs or overlayfs. Concept description: https://learn.adafruit.com/introduction-to-the-beaglebone-black-device-tree/overview

I could provide a solution for pin P9_15 which would be useful for probably 99% of BBB users because they will be able to choose other pins for their other devices.(Beaglebone Black has like 100 different gpio pins to choose from).

Creating an overlay for a specific pin is not adequately documented anywhere, so I was lucky to guess the correct pin "name" for P9_15. Trust me there is not a formula or logic from what I can see.

I'll upload the solution this weekend, I have 6 month old baby, time is hard to come by. In theory you could generate a solution for every pin, but unless there is a better documentation, you'd have to pay infinite chimps on infinite Beaglebones to brute force the correct .

I may be able to provide the actual fix to the library, but I don't have experience with how python installs libraries. because the fix will include placing a driver file in the firmware folder on the BBB, and then loading it on import. That first part will require some research on my part into the python setup.py install process.

cautiousCentaur commented 8 years ago

Solution for using w1thermsensor on pin P9_15 of Beaglebone Black running stock Debian, 3.8 Kernel, as of December 2015 : 1 The attached file needs to have it's extension changed from .txt to .dtbo (thanks GitHub) row_P9_15-00A0.txt and be copied to /lib/firmware This only needs to be done once, so I suggest during install of library, or anyone reading can do it manually.

2 Then every time the library is imported, run this code (I'm saying include this code in library init) Note: This code can be run entirely upstream of the normal import, so I'd put it first if you include it. Rest of module is compatible with this

import glob, os
dirname="/sys/bus/w1/devices"   
filespec="w1_bus_master*"
if not(glob.glob(os.path.join(dirname, filespec))): # if overlay has not been applied :. bus not running
    os.system("echo row_P9_15:00A0 > /sys/devices/bone_capemgr.*/slots") #  load overlay (an other name for driver)
BAThomp24 commented 8 years ago

Kory, I attempted following your steps on my beaglebone black but am still getting the NoSensorFoundError. I believe I'm either doing the second part wrong or it just isn't working. Running Debian with kernel 3.8 on the BBB.

Mind clarifying where you're adding the code you posted? I've added it to the init.py file under w1thermsensor folder then re-ran the setup install but with no luck. (sorry I'm new to python and the BBB).

cautiousCentaur commented 8 years ago

Try removing the python code from init, and putting it in your program, above the import. Also, in the future, paste any errors you get with your question.

BAThomp24 commented 8 years ago

I believe this worked. thank you!

timofurrer commented 8 years ago

As I don't have a beaglebone at the moment it would be very helpful if someone of you could integrate it and submit a PR. Would this be possible?

timofurrer commented 8 years ago

Could you post the entire traceback? Because it's not something from this package. It might be in click..

jstoner commented 8 years ago

Very interested in this. I had a more basic problem installing on my BBB, pip seemed to choke on the install:

(env) [ root@rc0003:/opt/reactor_controller/src ]
2s $> pip install w1thermsensor
Collecting w1thermsensor
  Using cached w1thermsensor-0.3.0.tar.gz
    Complete output from command python setup.py egg_info:
    Traceback (most recent call last):
      File "<string>", line 1, in <module>
      File "/tmp/pip-build-jqxrFZ/w1thermsensor/setup.py", line 22, in <module>
        install_requires=list(x.name for x in parse_requirements("requirements.txt")),
      File "/tmp/pip-build-jqxrFZ/w1thermsensor/setup.py", line 22, in <genexpr>
        install_requires=list(x.name for x in parse_requirements("requirements.txt")),
      File "/opt/reactor_controller/env/local/lib/python2.7/site-packages/pip/req/req_file.py", line 79, in parse_requirements
        "parse_requirements() missing 1 required keyword argument: "
    TypeError: parse_requirements() missing 1 required keyword argument: 'session'

    ----------------------------------------
Command "python setup.py egg_info" failed with error code 1 in /tmp/pip-build-jqxrFZ/w1thermsensor/

I have the current pip installed (8.1.1 at this writing). @kory44 how did you get past this? I suppose I could hack setup.py for now but long term I will be installing to a lot of BBBs, and I will need something easy to automate.

EDIT: as I think about it does this install on RasPi with current pip? This looks like it should be cross platform.

timofurrer commented 8 years ago

I know about this issue from other packages. The parse_requirements method of newer pip versions have this required session argument. You can do the following:

parse_requirements(..., session=False) - This should also be backward compatible. I'll fix it as soon as possible - or you can create a PR if you want to!

jstoner commented 8 years ago

Hm. There is some (broken?) magic going on in the setup process. Or I am an idiot, which is entirely possible.

when I look at the relevant line in your setup.py, I see

install_requires=["click"],

but the error would lead me to expect to see

install_requires=list(x.name for x in parse_requirements("requirements.txt")),

I could do the parse_requirements() call elsewhere, ala

install_reqs = parse_requirements('requirements.txt', session=False)

and then pass that in instead, but I'm also seeing pip maintainers say 'don't use requirements.txt that way:'

You really don't want to do this. Speaking as a pip maintainer pip does not support being called as an API like this at all. In fact pip 1.6 (next version at this time) moves this function. – Donald Stufft Mar 26 '14 at 0:59

Looks like the way it's written is the Right Way (TM), but when we run it it comes out the Wrong Way (TM). Weirdness.

Or I'm missing something. Seems likelier.

EDIT: silly me, cloning master instead of looking at your tags. Looks like you already have a fix.

timofurrer commented 8 years ago

Oh yeah, I remember I've already fixed that. Thanks for posting the pip maintainer's quote!

cautiousCentaur commented 8 years ago

Why is everyone using pip? I've never used pip. Just do the install from source.

timofurrer commented 8 years ago

Because you can install it directly from PyPI without having to clone the git repository. Or you could even use pip to install from a remote git repository.

jstoner commented 8 years ago

It's easier to automate. It's one thing to deploy code to one machine, very much another to do a hundred.

cautiousCentaur commented 8 years ago

Can't you just code one sd card and then use the built in tool to clone it to infinite BBB's? Only need to code once

jstoner commented 8 years ago

That works to install the OS and various things... but if I'm updating code to control processes, and that changes a lot, and I need to update libraries... yeah, the sd cards are not going to do it.

Actually, depending on the libraries, I might have to use --download and --find-links to pull from a local place. I am learning to love Ansible.

On Fri, Apr 15, 2016 at 9:55 PM kory44 notifications@github.com wrote:

Can't you just code one sd card and then use the built in tool to clone it to infinite BBB's? Only need to code once

— You are receiving this because you are subscribed to this thread. Reply to this email directly or view it on GitHub https://github.com/timofurrer/w1thermsensor/issues/16#issuecomment-210722941

jstoner commented 8 years ago

OK, now when my code hits the line

os.system("echo row_P9_15:00A0 > /sys/devices/bone_capemgr.*/slots")

I get this mysterious message:

sh: 1: cannot create /sys/devices/bone_capemgr.*/slots: Directory nonexistent

misleading, because that exists. When I look at its contents I see this:

 root@rc19-1-2:/opt/reactor_controller/src ] sensors
0s 👍  #> cat /sys/devices/bone_capemgr.9/slots
 0: 54:PF---
 1: 55:PF---
 2: 56:PF---
 3: 57:PF---
 4: ff:P-O-L Bone-LT-eMMC-2G,00A0,Texas Instrument,BB-BONE-EMMC-2G
 5: ff:P-O-L Bone-Black-HDMI,00A0,Texas Instrument,BB-BONELT-HDMI
 7: ff:P-O-L Override Board Name,00A0,Override Manuf,cape-bone-iio

When I look at dmesg I see this:

[ root@rc19-1-2:/opt/reactor_controller/src ] sensors
0s 👍  #> dmesg | tail
[93220.975019] bone-capemgr bone_capemgr.9: slot #9: Requesting part number/version based 'row_P9_15-00A0.dtbo
[93220.975069] bone-capemgr bone_capemgr.9: slot #9: Requesting firmware 'row_P9_15-00A0.dtbo' for board-name 'Override Board Name', version '00A0'
[93221.011054] bone-capemgr bone_capemgr.9: failed to load firmware 'row_P9_15-00A0.dtbo'
[93243.803060] bone-capemgr bone_capemgr.9: part_number 'row_P9_15', version '00A0'
[93243.803247] bone-capemgr bone_capemgr.9: slot #10: generic override
[93243.803295] bone-capemgr bone_capemgr.9: bone: Using override eeprom data at slot 10
[93243.803346] bone-capemgr bone_capemgr.9: slot #10: 'Override Board Name,00A0,Override Manuf,row_P9_15'
[93243.803609] bone-capemgr bone_capemgr.9: slot #10: Requesting part number/version based 'row_P9_15-00A0.dtbo
[93243.807695] bone-capemgr bone_capemgr.9: slot #10: Requesting firmware 'row_P9_15-00A0.dtbo' for board-name 'Override Board Name', version '00A0'
[93243.868060] bone-capemgr bone_capemgr.9: failed to load firmware 'row_P9_15-00A0.dtbo'

I do have the dto in the right place.

[ root@rc19-1-2:/opt/reactor_controller/src ] sensors
1s 👍  #> ls -l /lib/firmware/row_P9_15-00A0.dto
-rw-r--r-- 1 root root 988 Apr 29 13:42 /lib/firmware/row_P9_15-00A0.dto

I am running the last Debian I have that is compatible with the Adafruit BBIO libraries:

[ root@rc19-1-2:/opt/reactor_controller/src ] sensors
0s 👍  #> cat /etc/dogtag
BeagleBoard.org Debian Image 2015-11-03
[ root@rc19-1-2:/opt/reactor_controller/src ] sensors
0s 👍  #> uname -a
Linux rc19-1-2 3.8.13-bone79 #1 SMP Tue Oct 13 20:44:55 UTC 2015 armv7l GNU/Linux

If anyone has any ideas about this I'd appreciate them. @kory44 can you share the device tree source for row_P9_15-00A0.dto? If I go to Robert C Nelson for answers about this he'll probably want to see it.

timofurrer commented 8 years ago

@jstoner This has nothing to do with w1thermsensor right?

Does your python code run with the right permissions to read/write in /sys/devices/ ?

jstoner commented 8 years ago

No, it fails to load the device tree overlay with the message I pasted above. I'm running as root, so I don't know what permissions I'd be lacking.

Maybe I am screwing something else up that's unrelated, but I don't know that yet.

timofurrer commented 8 years ago

Okay. Let's see what @kory44 can say about it.

cautiousCentaur commented 8 years ago

Here is the file used to generate the compiled overlay. You'll need to remove the ".txt" extension, github doesn't allow .dts. row_P9_15.dts.txt

background on device tree compiling:

https://learn.adafruit.com/introduction-to-the-beaglebone-black-device-tree/overview

I believe the command to compile is this:

dtc -O dtb -o row_P9_15.dtbo -b 0 -@ row_P9_15.dts

Here's a note to go along with this code, from the link above:

Let's break down the options used to compile the overlay.

To start with we're using the device tree compiler (dtc). Everything required to compile DT overlays are included with the latest Angstrom distribution.

-O dtb is the output format. We're outputting device tree binaries. -o is the output filename. -b 0 is setting the physical boot CPU. (a zero) -@ generates a symbols node as part of the dynamic DT loading of the overlay

You'll know if you don't have a new enough version of dtc if the compiler complains about the missing -@ flag. You can attempt an upgrade of dtc by executing the following (this may need to be done on Ubuntu for now):

wget -c https://raw.githubusercontent.com/RobertCNelson/tools/master/pkgs/dtc.sh
chmod +x dtc.sh
./dtc.sh
rsalveti commented 8 years ago

The device tree file is always required, independently of the device, since that is how the kernel can understand and use the right GPIO for the w1 kernel module (that implements the protocol and knows how to talk with w1 based devices).

Even for Raspberry Pi that is used, but since it is available in the Debian image by default, all the user needs to do is to load it by giving dtoverlay=w1-gpio to /boot/config.txt.

Example of the overlay used by Rpi: https://github.com/raspberrypi/linux/blob/rpi-4.4.y/arch/arm/boot/dts/overlays/w1-gpio-overlay.dts . You will notice that pin 4 is hardcoded, but any other pin can be used.

Now for Beagle, you can find other pins by checking at https://github.com/jadonk/bonescript/blob/master/src/bone.js

One example using P9_12:

$ cat w1-gpio-00A0.dts
/dts-v1/;
/plugin/;

/ {
    compatible = "ti,beaglebone", "ti,beaglebone-black";
    part-number = "w1-gpio";
    version = "00A0";

    /* state the resources this cape uses */
    exclusive-use =
        "P9.12",     /* can be anything, just useful to avoid conflicts with other overlays using the same string */
        "gpio1_28";  /* can be anything, just useful to avoid conflicts with other overlays using the same string */

    fragment@0 {
               target = <&am33xx_pinmux>;
               __overlay__ {
                    dallas_w1_pins: pinmux_dallas_w1_pins {
                        pinctrl-single,pins = < 0x078 0x37 >;
                    };
               };
    };

    fragment@1 {
               target = <&ocp>;
               __overlay__ {
               onewire@0 {
                   compatible      = "w1-gpio";
                   pinctrl-names   = "default";
                   pinctrl-0       = <&dallas_w1_pins>;
                   status          = "okay";

                   gpios = <&gpio2 28 0>;
               };
         };
    };
};

Then just build it with dtc:

$ dtc -O dtb -o w1-gpio-00A0.dtbo -b 0 -@ w1-gpio-00A0.dts

And load it (kernel 4.1+):

$ sudo cp w1-gpio-00A0.dtbo /lib/firmware
$ sudo sh -c "echo 'w1-gpio' > /sys/devices/platform/bone_capemgr/slots"

To see if it was loaded correctly:

$ cat /sys/devices/platform/bone_capemgr/slots
5: P-O-L-   0 Override Board Name,00A0,Override Manuf,w1-gpio

You can also find several other overlays example at the PyBBIO project: https://github.com/graycatlabs/PyBBIO/tree/master/tools/overlays

jstoner commented 8 years ago

OK, so I made the mistake of renaming the device tree overlay to .dto instead of .dtbo . Gah! I hate that kind of thing.

Now I can get a temperature reading... if I do echo row_P9_15:00A0 > /sys/devices/bone_capemgr.*/slots from bash.

If I try to invoke it from python as @kory44 specified, I'm back to

sh: 1: cannot create /sys/devices/bone_capemgr.*/slots: Directory nonexistent

doing some googling I see

"...POSIX says that a non-interactive shell must not generate pathnames for a redirection; an interactive shell may do so, provided there is exactly one match.." *

and subprocess seems to be replacing os.system, so I end up with...

import glob, os
from subprocess import call

dirname="/sys/bus/w1/devices"
filespec="w1_bus_master*"
if not(glob.glob(os.path.join(dirname, filespec))): # if overlay has not been applied :. bus not running
    slots = glob.glob('/sys/devices/bone_capemgr.*/slots')[0]
    fh = open(slots, 'w')
    call(['echo', 'row_P9_15:00A0'], stdout=fh, shell=True) #  load overlay (an other name for driver)

And now it still fails to load the device tree overlay, but silently. Gah again.

But yes, this is not going wrong on a path through w1thermsensor code. It's before any invocation of that code.

cautiousCentaur commented 8 years ago

Did you not actually try using os.system? If you open a file with 'w' think ypu.over write it... not sure thats what you want... Also can you try shell=false if you still want to use your code..

jstoner commented 8 years ago

I did both. Neither caused any changes to slots.

Ah, but this did work:

import glob, os
dirname="/sys/bus/w1/devices"
filespec="w1_bus_master*"
if not(glob.glob(os.path.join(dirname, filespec))): # if overlay has not been applied :. bus not running
    slots = glob.glob('/sys/devices/bone_capemgr.*/slots')[0]
    os.system("echo row_P9_15:00A0 > {}".format(slots)) #  load overlay (an other name for driver)

Victory! Thanks for everyone's help and patience.

cautiousCentaur commented 8 years ago

I was doing all this with python 2.7, i hope jstoner's problem wasn't just a 2 to 3 problem...

jstoner commented 8 years ago

I am using 2.7.3 on debian wheezy. I think jessie has 2.7.11 or something. I'm not sure why globbing in the shell would work at all based on what I've read. I'd investigate more if I had time.

cautiousCentaur commented 8 years ago

I just want everyone who may come across this thread to not be thrown off by jstoner. My original code posted above works for me on my pretty standard beaglebone image I got from the beagleboard website in December of 2015.

import glob, os
dirname="/sys/bus/w1/devices"        

filespec="w1_bus_master*"
if not(glob.glob(os.path.join(dirname, filespec))): # if overlay has not been applied :. bus not running
    os.system("echo row_P9_15:00A0 > /sys/devices/bone_capemgr.*/slots") #  load overlay (an other name for driver)
import w1thermsensor