Wednesday, March 20, 2013

Raspberry Pi and snmp polling using ds18b20 temp sensors

Finally, after visiting the end of the internet and talking with countless people about hacking code up, I think I have put together a decent little system for polling temp readings off of a digital DS18B20 probe. Ill attempt to map out everything I have done from start to finish. Here goes:



Current Setup:

I got the Wheezy build from here and followed their instructions on how to put it on the SD card in the paragraphs above the download section. Depending on what OS you are using, there are obviously different programs to use.

I hooked the Pi up to a screen, keyboard, mouse and network, stuck the SD card in and, after a little initial setup, i was off and running. I gave it a static by adding these lines to the /etc/network/interfaces file:

iface eth0 inet static
        address IP ADDRESS
        netmask NETMASK
        gateway GW
        dns-nameservers DNS

I commented out the line that said "iface eth0 inet dhcp" because we don't need it for a static. Either one or the other. Now, either restart the Pi or restart the daemon for the network settings to take effect. I've had more luck rebooting....

Following directions from here

Step One: Updating the Kernel
  • The first step is to change where our Pi updates from, by editing a text file. We need to open up the file /etc/apt/sources.list.d/raspi.list as root, so type:
    sudo leafpad /etc/apt/sources.list.d/raspi.list
  • Now change the line in this file so that it reads "deb http://archive.raspberrypi.org/debian/ wheezy main untested", then save and close the file.
  • Next, do the following commands:
    apt-get update
    apt-get upgrade
Next comes wiring the probe and connecting it to the Pi. Using the directions here, I wired it to look similar to this pic:

Obviously, it can be set up any way but it must be connected correctly. I'm talking about using different pins on the breadboard and placement and such.

After that is done and the ribbon cable is connected, I powered the device on. You should feel the probe during this initial power-up. If it gets hot, which it will within seconds, turn it off and check your wiring. 

Running these two commands:

Sudo modprobe w1-gpio
sudo modprobe w1-therm

will fire up the probe. These will need to be put in a start-up file upon boot if ever the device is restarted. After those two commands complete, it will be dumping data into a file located at 

/sys/bus/w1/devices/SERIAL OF PROBE/w1_slave

The serial of the probe can be found by doing an "ls" inside the /sys/bus/w1/devices folder. In the pic at the top, I found what that number was and made a yellow label for it since that serial will never change. Easier to label them rather than trying to remember! :)

Running "cat w1_slave" inside that serial folder will produce two lines of output. the very last few digits will be the temperature in Celsius. Its at the thousands place so the python code further down that links page will tell us how to divide by 1k.

The following code is taken from that page with a little tweak at the end to convert it to Fahrenheit:

# Open the file that we viewed earlier so that python can see what is in it. Replace the serial number as before.

tfile = open("/sys/bus/w1/devices/28-00000474e0b1/w1_slave")

# Read all of the text in the file.

text = tfile.read()

# Close the file now that the text has been read.

tfile.close()

# Split the text with new lines (\n) and select the second line.

secondline = text.split("\n")[1]

# Split the line into words, referring to the spaces, and select the 10th word (counting from 0).

temperaturedata = secondline.split(" ")[9]

# The first two characters are "t=", so get rid of those and convert the temperature from a string to a number.

temperature = float(temperaturedata[2:])

# Put the decimal point in the right place and display it.

temperature = temperature / 1000

# Convert Temperature to Fahrenheit

temperature = (temperature * 9) / 5 + 32

# Print Temperature

print int(temperature)

The 28-00000474e0b1 part in that first line is the serial of MY probe. That will be different for you, of course. Copy and paste that, using your serial number, into a file called temp.py and place it wherever. to run it, type:

python temp.py

and it should return a rounded-like (because of the int() statement) integer that is the Celsius degrees converted to Fahrenheit. The probe works and the temp is being converted. That step is done.

Once we have all that done, we can install SMNPD and get it to respond to queries from a remote machine. This proved to be the most difficult for me because, up until this point, I always piggybacked on an existing SNMP install with the OIDs already there. Building this from scratch was definitely tricky for an SNMP nub like me.

So, taking directions from a lot of places, Ill post links to the most helpful places here.

Following directions from here, I installed SNMPD by running:

sudo apt-get update
sudo apt-get install snmpd
sudo apt-get install snmp

Im not 100% sure if i needed the client portion, but I installed it anyway during troubleshooting steps. 

Please be aware that this is for a totally internal setup with no outward facing access therefore its pretty wide open. You'll want to check up on securing your SNMP install if you choose to have it facing the internet. 

You'll need to edit your snmpd.conf file by typing:

sudo nano /etc/snmp/snmpd.conf

Here is where it got tricky. I had a line in there that read:

agentAddress  udp:127.0.0.1:161

which i commented out and added this line below it:

agentAddress udp:161

which I'm assuming from what I read allows for any machine with explicit access to get queries. Then, further down in that config, I found the rocommunity lines. I added these lines:

rocommunity public IP OF REMOTE MACHINE
rocommunity public localhost

not sure if that last one was there or not to begin with. Then commented out:

rocommunity public  default    -V systemonly

just for good measure! :) it looked like it might have caused a problem. Not sure if it was needed but, again, SNMP newb here. Make sure you restart the daemon to make your changes stick:

sudo /etc/init.d/snmpd restart

once those are done, from the local machine, you should be able to type:

snmpwalk -v 1 -c public localhost

and it will throw a ton of stuff at you from the console. you can also try that same command from a remote machine instead putting in the IP of the Pi rather than localhost and it should do the same thing.

From this moment on it gets a touch more tricky. Getting an OID to match to the temp probe output. remember the temp probe? yeah its been a while....

Continuing to follow the instructions here, you must do a few things which I will list:
  • make a script to call the python file we created in the first steps above
  • config a new OID inside the snmpd.conf to point to this new script
Easy. Kinda. If you originally knew what you were doing....

I grabbed the executable from that link above and changed it a bit:

#!/bin/bash
if [ "$1" = "-g" ]
then
echo .1.3.6.1.2.1.25.1.8.0
echo gauge
python /usr/local/bin/temp.py
fi
exit 0

copy and paste that above into a file called snmp-temp. Then run this:

sudo chmod 755 snmp-temp

which will make it executable and try running it:

./snmp-temp -g

Please notice that I changed the directories where these files are located which you will need to do as well for this to work. If they are all in the same place calling the same files, you should be ok. SNMP likes them to be in that /usr/local/bin directory. i was NOT able to run them out of my home directory where i was originally building these files. i moved both the python scripts and the shell scripts to that location and everything is working fine.

once you run that file, you should get output in three lines:

1.3.6.1.2.1.25.1.8.0
gauge
OUTPUT TEMP FROM PROBE

Now that that is working, we need to add a line to our snmpd.conf file to join that script with that OID. So... Edit the snmpd.conf:

sudo nano /etc/snmp/snmpd.conf

and add this line down where the other "PASS" lines are:

pass .1.3.6.1.2.1.25.1.8.0      /bin/sh         /usr/local/bin/snmp-temp -g

Again, please remember that I moved files around to that /usr/local/bin directory.

So.... Now if we run this (either locally or from the remote machine - ill use localhost for this example):

snmpget -v 1 -c public localhost .1.3.6.1.2.1.25.1.8.0

it should return:

iso.3.6.1.2.1.25.1.8.0 = Gauge32: CURRENT TEMP FROM PROBE

So for understanding, this is what happens:
  1. snmpget goes out via version 1 looking for the public community on localhost for the OID labeled .1.3.6.1.2.1.25.1.8.0
  2. Once it finds that OID in the snmpd.conf, it runs the associated script called snmp-temp inside /usr/local/bin
  3. That script calls the python script that actually does all the work; grabbing the output of the probe, parsing all the gibberish and converting it to Fahrenheit.
  4. snmp-get returns the results of the temp.py into a nicely formatted snmp query that can then be put into any program.
I use nagios to poll a ton of other devices so this was an easy setup inside nagios once the output was generated and the ability to query against it was done.

15 comments:

  1. Exactly what I was looking for. I have an extra raspi and it seems a simple SNMP temperature probe is $100 minimum. Why do that when I can build one for a lot less!

    Thanks for the great write-up!

    ReplyDelete
  2. My thoughts when I started doing this. We have other temperature devices around our buildings and they cost in upwards of $400 without probes.

    ReplyDelete
  3. How to interface an Arduino PIC with a Hurimel HS1101 temperature and humidity sensor. Simple 4 wire interface and code makes it easy and low cost.Great post...thanks for the reminder to blog about the everyday things that people want to read. As a real estate agent, I too struggle with what to blog about. Thanks!Temperature Sensors

    ReplyDelete
  4. Alright chap, I've followed your above tutorial but I'm having an issue.

    After adding the pass line to snmpd.conf
    it seems to break the SNMP output.
    So when you do a 'snmpwalk -v 1 -c public localhost' after restarting the snmpd it gets down as far as our OID and then outputs.

    iso.3.6.1.2.1.25.1.7.0 = INTEGER: 0
    iso.3.6.1.2.1.25.1.8.0 = Gauge32: 25
    iso.3.6.1.2.1.25.1.8.0 = Gauge32: 25
    Error: OID not increasing: iso.3.6.1.2.1.25.1.8.0
    >= iso.3.6.1.2.1.25.1.8.0

    It seems like the pass command is outputting the OID twice...
    Have you had any trouble like this?
    Or any idea what's up?

    ReplyDelete
    Replies
    1. Remove the "-g" in the line "pass [...] /usr/local/bin/snmp-temp -g".
      Simply put, the script should only output data when smptd calls it with the parameter "-g" (for details, see the man page for snmpd.conf). With the "pass" configuration line in the article, it will _always_ output data, which causes the problem you are experiencing.

      Delete
    2. I know i had an issue with those lines. that is why I put the "-g" in there. Ill have to re-read through my notes as to why that was in there originally.

      You are saying it is safe to take it out?

      Delete
  5. How to Solve this error:
    iso.3.6.1.2.1.25.1.7.0 = INTEGER: 0
    iso.3.6.1.2.1.25.1.8.0 = Gauge32: 25
    iso.3.6.1.2.1.25.1.8.0 = Gauge32: 25
    Error: OID not increasing: iso.3.6.1.2.1.25.1.8.0
    >= iso.3.6.1.2.1.25.1.8.0

    without Remove the "-g" in the line "pass [...] /usr/local/bin/snmp-temp -g".
    because if you remove the -g in the snmpwalk you can't see your OID

    ReplyDelete
  6. Hi DJ,
    I want to read GPIO-pins via snmp. So I looked through your scripts and adapted them. If I run the shell-script on the Pi everything looks good. I get three lines with the OID, the type (gauge) and an integer (17 for closed and 99 for open) depending on the GPIO that was pulled to ground.
    Only when I read the OID via snmp (locally or over the network) I get the OID, the type, but 0 as the value.
    Do you have any ideas what I am missing?
    regards, Karl

    ReplyDelete
    Replies
    1. [solved] It works now, even if I remove user snmp from group gpio. My problem in testing was, that I did not wait between two tests. I found out, that snmpd on the RasPi caches the result of a get for 30 seconds. Only if you wait that long, you get the new result! Thanks to all

      Delete
  7. This comment has been removed by the author.

    ReplyDelete
  8. Hi JD,
    Thanks for a great example.

    I can get the temp of the PI ok via snmp.

    I have done everything similar to what you mentioned but get a zero when I use snmp to get the status of a gpio pin.

    See my bash code below

    #!/bin/bash
    if [ "$1" = "-g" ]
    then
    echo .1.3.6.1.2.1.25.1.9.0
    echo gauge
    pigs r 5
    fi
    exit 0


    When I run the above I get:

    pi@raspberrypi:~ $ /home/pi/myFlask/GetStatus_01 -g
    .1.3.6.1.2.1.25.1.9.0
    gauge
    1

    But when I used the snmp command I get:

    pi@raspberrypi:~ $ snmpget -v 1 -c public localhost .1.3.6.1.2.1.25.1.9.0
    iso.3.6.1.2.1.25.1.9.0 = Gauge32: 0

    For the temperature it works ook as below:

    pi@raspberrypi:~ $ snmpget -v 1 -c public localhost .1.3.6.1.2.1.25.1.8.0
    iso.3.6.1.2.1.25.1.8.0 = Gauge32: 50464

    Am I missing something? Please Advice

    ReplyDelete
    Replies
    1. Did you add a new address .1.3.6.1.2.1.25.1.9.0 in the SNMP config?

      Delete
    2. I have been following the manual but always end up with :

      /usr/bin/snmpget -v2c -c public localhost .1.3.6.1.2.1.25.1.8.0
      iso.3.6.1.2.1.25.1.8.0 = Gauge32: 0

      My conf is :
      pass .1.3.6.1.2.1.25.1.8.0 /usr/local/bin/snmp-temp -g

      Running /usr/local/bin/snmp-temp -g :
      .1.3.6.1.2.1.25.1.8.0
      gauge
      25.0

      So that works well..

      What am I missinge here.....?

      Chrs..


      Delete
  9. Hi,
    everything works fine. Only one problem exist:
    When I execute the snmp-temp the result is 22.3

    ./snmp-temp -g
    .1.3.6.1.2.1.25.1.8.0
    gauge
    22.3

    When I execute the "snmpget -v1 -c public localhost .1.3.6.1.2.1.25.1.8.0"
    iso.3.6.1.2.1.25.1.8.0 = Gauge32: 22

    Here the result ist 22 not 22.3

    Any idea where the .3 get lost?

    Chers Jens

    ReplyDelete