Saturday, January 10, 2015

Web CGI Based Raspberry Pi GPIO LED Toggler

Finally, a setup that works, and it was a joy to go to mypi.freedns.org:NONCOXBLOCKEDPORT/cgi-bin/script.pl on my phone and see the LED toggle, and refresh the page and see it toggle again, and again and again...

So, on to how :-

What you need :
  • pigpio library (RPIO and RPi.GPIO don't cut it because they need root access)
  • lighttpd (other solutions possible, but this recipe is based on lite) an account with a free DNS service that will give you a static URL (you can't do 192.168.0.114 on your phone unless you're on the WiFi - and it's wimpy anyway)
  • Simple perl script that will call the python GPIO script (Yes, I tried calling the python script through CGI and gave up on that) 


1) Install lighttpd - consult online tutorials for that.
2) Once lighttpd is installed, enable CGI and perl by editing /etc/lighttpd/lighttpd.conf to have the relevant portions look like :
server.modules = (
        "mod_access",
        "mod_alias",
        "mod_compress",
        "mod_cgi",
        "mod_redirect",
#       "mod_rewrite",
)
and
$HTTP["url"] =~ "^/cgi-bin/" {
        cgi.assign = ( ".py" => "/usr/bin/python" ,
                        ".pl" => "/usr/bin/perl")
}
And, of course, if your ISP (Eg. Cox) is blocking port 80, you can change server.port to something else.
3) In the /var/www directory, create a cgi-bin directory (ownership can be root, no problem).
4) Install the pigpio library by following these steps.
5) Put the line "sudo /usr/local/bin/pigpiod &" in your /etc/rc.local so this daemon runs @ boot.
6) In a convenient place, put this script which will set port 17 (CPU view, not Board View) as an output port (name it meaningfully - I use caution_set_out_17.py :
#!/usr/bin/python
import RPIO as GPIO
# to use Raspberry Pi board pin numbers
GPIO.setmode(GPIO.BOARD)
GPIO.setup(11, GPIO.OUT)

7) Run this script using "sudo /path/to/caution_set_out_17.py". It's named that way because it's not a good idea to blindly set a port to output - what if some app had set it as input and a voltage source was driving it on the board? Yes, until you know what you're doing with your Pi, it's not a bad idea to run this command manually :)
8) Put this script in a convenient place, which actually does the toggling of the logic-level on Port 17 :
#!/usr/bin/python
import pigpio
pi = pigpio.pi()
if 1 ==pi.get_mode(17) :
        pi.write(17, not pi.read(17))
9) Put this script in your /var/www/cgi.bin, name it conveniently
#!/usr/bin/perl
print "Content-type: text/html\n\n";
system("/home/pi/projects/pgpio_toggle_17.py");
print "<html>Hello, World!\n</html>";
10) Ensure you do a "chmod +x" on all the scripts you just finished installing
11) Using your breadboard, connect the LED longer wire to pin 11 (header view, google it), shorter wire to 330 ohm, and other terminal of the resistor to GND on the header (you'd want to do a sanity check by first connecting the LED, through 330 ohm of course) between 3V and 5V)
12) That's it, you should first try each of the scripts standalone - just run them from the command line and ensure the effect is as desired, then you can take your smartphone and go to yourpi.dnsserver.domain:80XX/cgi-bin/script.pl and you should see the LED toggle. 

Troubleshooting - running each script standalone should show get you there - obviously you edit the script I gave you to put in the actual name of the script you created, etc.


Joan Crawford pigpio Hello World

pi@raspberrypi ~/projects $ sudo pigpiod
pi@raspberrypi ~/projects $ python
Python 2.7.3 (default, Mar 18 2014, 05:13:23)
[GCC 4.6.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import pigpio
>>> pi = pigpio.pi()
>>> mode = pi.get_mode(17)
>>> mode
1L
>>> pi.write(17,0)
0L

Friday, January 9, 2015

Toggle a GPIO LED on Raspberry Pi Through the Web

Going to try the CGI thing.

First order of biz is to ensure "mod_cgi" is part of server.modules in /etc/lighttpd/lighttpd.conf

This one seems pretty authoritative : http://raspberrypi.stackexchange.com/questions/1346/how-to-get-python-to-work-with-lighttpd

Add to the .conf :
$HTTP["url"] =~ "^/cgi-bin/" {
        cgi.assign = ( ".py" => "/usr/bin/python" )
}
And restart lighttpd with sudo service lighttpd force-reload.

Then, documentation is so crappy on RPIO and RPi.GPIO - horrible, I want to know what the port can be other than GPIO.OUT. I try all kinds of searches, finally ask where easy_install puts files and get some luck there (/usr/local/lib). So, there, the python3.2 is nothing - why all this confusion? Why can't the authors maintain some decent documentation?

GPIO_FUNCTIONS = {0: "OUTPUT", 1: "INPUT", 4: "ALT0", 7: "-"}

Is what I find in /usr/local/lib/python2.7/dist-packages/RPIO-0.10.0-py2.7-linux-armv6l.egg/RPIO/_RPIO.py
*ds!

Now, what is the default for a port? Ans : 1 - input - good to know - so, it's not a good idea to change that to an OUT arbitrarily if you don't know what you're doing..

Now, that being done, the CGI thing isn't working for running a python script in /var/www/cgi-bin. Seems like that thing doesn't run as root - which is needed to access GPIO. So, what can we do?

My workaround seems to be on the right lines - I was thinking of running a daemon that would look for requests to toggle the pin 17. Others online suggest the use of pigpio which doesn't require root, but, then you're again having to run the pigpio daemon, so it's the same concept.. Hmm... Why such crappiness?

Raspberry Pi Toggle An LED on GPIO Howto

With RPi.GPIO :

#!/usr/bin/python

import RPi.GPIO as GPIO

# to use Raspberry Pi board pin numbers
GPIO.setmode(GPIO.BOARD)

# set up GPIO output channel
GPIO.setup(11, GPIO.OUT)

GPIO.output(11, not GPIO.input(11) )

Don't run GPIO.cleanup at the end and then complain that it doesn't work :). Note that the above one sets the port as output. That's not what you want to do - you want to check that the port is already an output and only then toggle. If the port was configured as input from another app and is connected to a voltage source on the board, you don't want to pull it to the opposite voltage.

So, if you upgrade to RPIO using

sudo apt-get install python-setuptools
sudo easy_install -U RPIO

Then, you can do

#!/usr/bin/python

import RPIO as GPIO

# to use Raspberry Pi board pin numbers
GPIO.setmode(GPIO.BOARD)

if GPIO.gpio_function(11) is GPIO.OUT :
        # this is stupid, but just to trick RPIO
        GPIO.setup(11, GPIO.OUT)
        GPIO.output(11, not GPIO.forceinput(11))

Note how amateurish that is - finding out that something is already an output and then telling the same library to set as output - no surprise considering Pi was targeted at kids. If you didn't, you get :

Traceback (most recent call last):
  File "./nu_toggle_17.py", line 12, in
    GPIO.output(11, not GPIO.forceinput(11))
RPIO.Exceptions.WrongDirectionException: The GPIO channel has not been set up as an OUTPUT

..

Raspberry Pi LED Hello World

Most tutorials/blogs are raising more questions than they answer.. I've lost (?) a day reading Bunnie Huang's excellent reverse engineering adventure on the Xbox and am about to have lunch with my mentor and complain about how diffused all my efforts have been the last few weeks - just because the tools I want don't exist.

Anyhow, one facet of my idea stream is based on the Raspberry Pi. And I've done absolutely nothing with it - except hook up a cheezy surveillance system that takes a picture every minute and makes it available through a free DNS.

Now, before lunch, I want to do the LED blink example.

First, I just put in the LED (with 330 ohm) between 3V and GND and that works. Now, I've hooked it up between Pin 11 and GND in anticipation of following the steps on Rahul Kar's page.

Says import RPI.GPIO. What if that doesn't work? Man this is a crappy tutorial? Says do "sudo blah blah" - but that makes me nervous.

Now, I putty into the Pi and try :

#!/usr/bin/python

import RPi.GPIO as GPIO
from time import sleep

# to use Raspberry Pi board pin numbers
GPIO.setmode(GPIO.BOARD)

def blink(pin):
        GPIO.output(pin,GPIO.HIGH)
        time.sleep(1)
        GPIO.output(pin,GPIO.LOW)
        time.sleep(1)
        return
# set up GPIO output channel
GPIO.setup(11, GPIO.OUT)
# blink GPIO17 50 times
for i in range(0,50):
        blink(11)
GPIO.cleanup()

And get :

pi@raspberrypi ~/projects $ ./rahul_kar_led.py
Traceback (most recent call last):
  File "./rahul_kar_led.py", line 16, in
    GPIO.setup(11, GPIO.OUT)
RuntimeError: No access to /dev/mem.  Try running as root!
pi@raspberrypi ~/projects $ sudo !$
sudo ./rahul_kar_led.py
Traceback (most recent call last):
  File "./rahul_kar_led.py", line 19, in
    blink(11)
  File "./rahul_kar_led.py", line 11, in blink
    time.sleep(1)
NameError: global name 'time' is not defined

Scary? :)

I was trying to be smart and keep what I had from (I think) Jake Beningo's course a few monats ago : "from time import sleep" - which means I should then use just sleep(1). Instead, I decide to be a student again and change to "import time". And, works!

pi@raspberrypi ~/projects $ sudo ./rahul_kar_led.py
./rahul_kar_led.py:17: RuntimeWarning: This channel is already in use, continuing anyway.  Use GPIO.setwarnings(False) to disable warnings.
  GPIO.setup(11, GPIO.OUT)

And the Pi takes a picture while the LED is ON..

Okay, that's done. Now, I want to run a command each time the webserver serves this image so that I toggle the LED each time. Wouldn't that be cool?

And the realization : the R Pi world is being run by a bunch of !@#$s - I tried about 10 searches - "how to read state of GPIO output on raspberry pi" - nothing!