WelcomeWelcome | FAQFAQ | DownloadsDownloads | WikiWiki

Author Topic: Busybox-httpd + GPIO  (Read 7777 times)

Offline gavinmc42

  • Sr. Member
  • ****
  • Posts: 301
Busybox-httpd + GPIO
« on: September 14, 2015, 02:04:42 AM »
Have not figured out how to control GPIO with the Micropython webserver example yet.
Meanwhile here is a method that uses busybox-httpd

html

<a href="/cgi-bin/ledon.cgi"><button>LED on</button></a>
<a href="/cgi-bin/ledoff.cgi"><button>LED off</button></a>


put a script file like this in /www/cgi-bin


#!/bin/sh
echo "Content-type: text/html"
echo ""
echo "LED 17 is on"
echo 1 > /sys/class/gpio/gpio17/value


If this can run shell script it should be able to run Micropython and Lua as well
Probably also has lots of security holes?


Offline gavinmc42

  • Sr. Member
  • ****
  • Posts: 301
Re: Busybox-httpd + GPIO
« Reply #1 on: September 14, 2015, 02:42:34 AM »
Use this trick so the you stay on the same page

#!/bin/sh
echo 1 > /sys/class/gpio/gpio17/value
echo "HTTP/1.1 303 Found"
echo "Location: /index.html"

Offline xyz-worx

  • Jr. Member
  • **
  • Posts: 69
Re: Busybox-httpd + GPIO
« Reply #2 on: September 14, 2015, 04:24:13 AM »
Hi gavinmc42,

some time ago I wanted to control an external device via a web interface.
Here's an alternative approach which has - imho - the advantage, that you
can call it from any html-page without quoting the location where to return.

Code: [Select]
#!/usr/bin/perl
use strict;

my $now = localtime(time);

#-----------------------------------------------
#
# do something useful here
#
#-----------------------------------------------

# send an 'empty' html-file to the browser
print "HTTP/1.0 204 No Content\n\n";


# do some protocol for instance
open(DAT,">>","/opt/var/log/bug.log");
print DAT $now,"\n";
close(DAT);
exit(0);


The script is written in perl, but can be done in any other language of course.
The only important thing is the double line feed at the end of the HTTP-line.

Using this frame I can control nearly any hardware which can be accessed via a serial port.

best regards
     xyz-worx

Offline gavinmc42

  • Sr. Member
  • ****
  • Posts: 301
Re: Busybox-httpd + GPIO
« Reply #3 on: September 14, 2015, 09:01:57 PM »
Thanks, you just doubled my entire CGI knowledge ;)

Now trying to figure out how to use query strings to pass values.


Offline gavinmc42

  • Sr. Member
  • ****
  • Posts: 301
Re: Busybox-httpd + GPIO
« Reply #4 on: September 15, 2015, 12:53:53 AM »
Ouch, this took too long to figure out.
Only a few lines of code and I still barely know what I am doing.
But now I can turn leds on and off and get data from a browser back to the Pi.
Data goes into the query string, use xxx.cgi file to extract it.
Just need to get Wifi working and then I can make a web controlled Pibot to take over the world. 8)

HTML code

<button onclick="save()" >Save Settings</button>

<script>
function save(){
    document.location="/cgi-bin/testsettings.cgi" + "?Degree=" + degrees.value + "&Rate=" + rates.value;

}
</script>

Offline Greg Erskine

  • Sr. Member
  • ****
  • Posts: 404
Re: Busybox-httpd + GPIO
« Reply #5 on: September 15, 2015, 01:37:48 AM »
Code: [Select]
# send an 'empty' html-file to the browser
print "HTTP/1.0 204 No Content\n\n";

hi xyz-worx,

Thanks for posting this code. I have been playing around with it and like the result in most browsers, except IE keeps displaying the rotating refresh icon until you hit Stop or esc and Safari on my iPad displays a blank page instead of leaving the page contents untouched. So SODS law is verified again!

Do you have any advice making it work on all browsers?

I am using a sh cgi script, tried echo, printf and cat using all the tricks I know but to no avail.

I think IE is getting the blank line but ignoring it and Safari on the iPad just treats 204 incorrectly.

regards
Greg

Offline Greg Erskine

  • Sr. Member
  • ****
  • Posts: 404
Re: Busybox-httpd + GPIO
« Reply #6 on: September 15, 2015, 01:45:15 AM »
I use this method to run the script and jump back to the referring page. This works on all browsers but doesn't look as nice because there is a flicker.

Code: [Select]
Normal html header

echo '<body onload="javascript:location.href=document.referrer;">'

sh script commands.

echo '</body>'
echo '</html>'




Offline Greg Erskine

  • Sr. Member
  • ****
  • Posts: 404
Re: Busybox-httpd + GPIO
« Reply #7 on: September 15, 2015, 01:57:47 AM »
hi gavinmc42,

Here's the $QUERY_STRING method that I use to pass variables between scripts. These are test scripts not fully implemented. From memory setting the GPIOs was done externally, manually. Basically the scripts turn relays on and off. The relay board has 4 relays but I only coded  for 2.

I think there is an example in the TC manual showing this method.

Code: [Select]
#!/bin/sh

# Version: 0.01 2014-11-07 GE
# Original.

status() {
echo Status: $1
}

#sudo sh -c 'echo 17 > /sys/class/gpio/export'
#sudo sh -c 'echo "out" > /sys/class/gpio/gpio17/direction'
#sudo sh -c 'echo "0" > /sys/class/gpio/gpio17/active_low'

#cat /sys/class/gpio/gpio17/direction
#cat /sys/class/gpio/gpio17/value

. pcp-functions
pcp_variables
pcp_html_head "Relay" "GE"
pcp_banner

echo '<table border="1" bgcolor="d8d8d8" cellspacing="0" cellpadding="0" width="960">'

for i in 1 2 3 4
do
echo '  <tr>'
echo '    <td>'
echo '      <p>Relay '$i'</p>'
echo '    </td>'
echo '    <td>'
echo '      <form name="relay'$i'-on" action="relay_controls.cgi" method="get" id="relay'$i'-on">'
echo '        <input type="hidden" name="RELAY" value="RLY'$i'"/>'
echo '        <input type="hidden" name="ACTION" value="on"/>'
echo '        <input type="submit" value="On" />'
echo '      </form>'
echo '    </td>'
echo '    <td>'
echo '      <form name="relay'$i'-off" action="relay_controls.cgi" method="get" id="relay'$i'-off">'
echo '        <input type="hidden" name="RELAY" value="RLY'$i'"/>'
echo '        <input type="hidden" name="ACTION" value="off"/>'
echo '        <input type="submit" value="Off" />'
echo '      </form>'
echo '    </td>'
echo '    <td>'
echo '      <p>'$(status $i)'</p>'
echo '    </td>'
echo '  </tr>'
done

echo '</table>'

pcp_footer

echo '</body>'
echo '</html>'



Code: [Select]
#!/bin/sh

# Version: 0.01 2014-11-07 GE
# Original.

. pcp-functions
pcp_variables
. $CONFIGCFG

pcp_html_head "Relay Control" "GEx"

[ $DEBUG = 1 ] && echo '<body>' || echo '<body onload="javascript:location.href=document.referrer;">'

pcp_running_script

pcp_httpd_query_string

#=========================================================================================
# Initialise GPIO for replays
#-----------------------------------------------------------------------------------------

# echo "17" > /sys/class/gpio/export
# sudo sh -c 'echo 17 > /sys/class/gpio/export'

# echo "in" > /sys/class/gpio/gpio17/direction
# echo "1" > /sys/class/gpio/gpio17/active_low
# PRESSED=$(cat /sys/class/gpio/gpio17/value)
# echo "17" > /sys/class/gpio/unexport

#=========================================================================================
# Map relay to GPIO
#-----------------------------------------------------------------------------------------
case $RELAY in
RLY1)
GPIO=gpio17
;;
RLY2)
GPIO=gpio27
;;
esac

if [ $DEBUG = 1 ]; then
echo '<p class="debug">[ DEBUG ] $RELAY: '$RELAY'<br />'
echo '                 [ DEBUG ] $ACTION: '$ACTION'<br />'
echo '                 [ DEBUG ] $GPIO: '$GPIO'</p>'
fi

case $ACTION in
on)
echo "0" > /sys/class/gpio/$GPIO/value
;;
off)
echo "1" > /sys/class/gpio/$GPIO/value
;;
esac

[ $DEBUG = 1 ] && pcp_go_back_button || pcp_footer

echo '</body>'
echo '</html>'

Code: [Select]
pcp_httpd_query_string() {
# Process the $QUERY_STRING from httpd
eval $(echo "$QUERY_STRING" | awk -F'&' '{ for(i=1;i<=NF;i++) { print $i} }')
[ $DEBUG = 1 ] && echo '<p class="debug">[ DEBUG ] $QUERY_STRING: '$QUERY_STRING'</p>'
}

regards
Greg

Offline gavinmc42

  • Sr. Member
  • ****
  • Posts: 301
Re: Busybox-httpd + GPIO
« Reply #8 on: September 15, 2015, 03:46:12 AM »
Thanks Greg,

Had forgotten about the corebook, page 104 for CGI, knew I had seen it somewhere before.
I had that demo working and then one month later forgot how I did it.

Page 103 for httpd webserver.

Got a fridge monitor that has been up for 139 days.
One of the fixes needed was to have the user set the temperature alarm thresholds via the web page.
Been hard coded, now I can go back and fix it.
The great thing about Linux + shell scripting is remotely fixing things without rebooting:)

Might try your forms method, don't think I have used html forms before.

Regards
Gavin

Offline xyz-worx

  • Jr. Member
  • **
  • Posts: 69
Re: Busybox-httpd + GPIO
« Reply #9 on: September 15, 2015, 05:17:19 AM »
HI,

@gavinmc42:
Nice to hear that I could double your CGI knowledge!

@Greg:

Quote
Do you have any advice making it work on all browsers?

The template I posted did work for my needs to control an
external µController board via RS232 using buttons and sliders
as a graphical user interface. For this I used Firefox and SlimBoat
as browsers.
In fact I did not verify, if this approach works with different
browsers. Especially I do not have any access to apple-like ones.

I general, it was quite difficult for me to find the solution described
on the web. Main difficulty was to express the right search topic.
Doing this in English and German I found three different approaches:

1.
The one I posted.

2.
The action taken by the CGI call should not return HTML code but an
image. The image returned consists only of a single and transparent
GIF image like this: http://www.netzmafia.de/skripten/perl/1pix.gif.

This is done by the browser:

Code: [Select]
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Headline</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>

<body>
    <h1 align="center"><b>Topline</b></h1>
    <center>
        <img src="/cgi-bin/bug.cgi">
    </center>
</body>
</html>


The CGI-script invoked (bug.cgi):

Code: [Select]
#!/usr/bin/perl
use strict;

# HTTP-Header
print "Content-Type: image/gif\n";
print "\n";

# GIF Image
open(DAT,"/opt/var/www/lge/httpdocs/img/1pix.gif");
print while (<DAT>);
close(DAT);

exit(0);

3.
Omitting  the whole <body>...</body> block in the CGI-return. But this
I have not tested up to now.

best regards
  xyz-worx




Offline Greg Erskine

  • Sr. Member
  • ****
  • Posts: 404
Re: Busybox-httpd + GPIO
« Reply #10 on: September 15, 2015, 05:25:46 PM »
hi xyz-worx,

Thanks for the help.

I will have to work thru this to get an understanding of #2.

#3 seems to obvious to work.  ;D

I spent quite a few hours searching the web and didn't find much either. I did see the 1 pixel gif method somewhere.

regards
Greg

Offline xyz-worx

  • Jr. Member
  • **
  • Posts: 69
Re: Busybox-httpd + GPIO
« Reply #11 on: September 16, 2015, 04:14:33 AM »
Hi Greg,

some notes to solution #2:

In fact this is nothing more than a workaround. With the <img>-tag the
browser expects some graphics returned from the server. This is done not
directly by delivering a gif-, png- or jpg-file. First the cgi-script is invoked
at the server side. Within this script anything useful can be done to your
needs.
At the end and to satisfy the calling browser with the demanded graphic
a dummy-graphic - the none visible transparent single pixel - is returned
to the browser.
Doing it that way neither the original html-page (which contains the cgi-call)
is disturbed nor an empty page is displayed.

This technique was (or is) used to track if html mails have been read by
the addressee. This is why mail programs like Thunderbird do suppress the
automatic loading of images in html-mails.

If you are interested in a comprehensive article covering cgi-scripts in perl
I can provide you with a link to it. But I must confess, this is written in German.

best regards

   xyz-worx

Offline gavinmc42

  • Sr. Member
  • ****
  • Posts: 301
Re: Busybox-httpd + GPIO
« Reply #12 on: September 17, 2015, 02:08:26 AM »
Been having fun, sort of.
Starting to like Javascript, also starting to understand how the web + CGI stuff works.

HTML code

   <div>
   <button onclick="switchon()" id="onoff"  style="font-weight:bold; font-size:250%; background-color:grey" >On</button>
   <button onclick="switchoff()" id="offon"  style="font-weight:bold; font-size:250%; background-color:red" >Off</button>
   <button  id="stepstatus"  style="font-size:250%; background-color:yellow" >stopped</button>
   </div>
....
<script>
   function switchon(){
       console.log("on");
       document.getElementById("offon").style.background = "grey";
            document.getElementById("onoff").style.background = "lightgreen";
            document.getElementById("stepstatus").innerHTML = 'Running';
       document.location="/cgi-bin/poweron.cgi";
   }
         
   function switchoff(){
       console.log("off");
       document.getElementById("onoff").style.background = "grey";
           document.getElementById("offon").style.background = "red";
           document.getElementById("stepstatus").innerHTML = 'Stopped';
       document.location="/cgi-bin/poweroff.cgi";
   }



poweron.cgi

#!/bin/sh
echo "HTTP/1.1 204 No Content"
echo ""

echo 1 > /sys/class/gpio/gpio17/value


By calling different xx.cgi files I don't need to read the query string just to turn a GPIO on and off.

console.log is useful for debugging, right click browser window "Inspect element", in Chrome anyway.
Could fancy it up by using image buttons etc.
Think I now have a good idea on how Webiopi might work, still seems like a lot of code to do the same thing?


Offline xyz-worx

  • Jr. Member
  • **
  • Posts: 69
Re: Busybox-httpd + GPIO
« Reply #13 on: September 17, 2015, 04:55:40 AM »
Hi gavinmc42,

Quote
still seems like a lot of code to do the same thing

I fully agree with this. And if you are scripting in perl you can
do one and the same thing in different ways once more!

One note to your approach to call different cgi-scripts for different tasks.
This works quite well for simple tasks and is the quickest way to do.
If you have to handle more sophisticated user input, the 'post' or 'get'
method to pass information to the server might come into play.
In this situation it might be better to use a 'higher level' script
language like perl or python. This is because there are many ready to
use solutions to evaluate user parameters passed with calling the script.
And I think python is like an integral part of the raspberry pi.

But I also know after shell-scripting and HTML and JavaScript this means
to learn another programming language ...

best regards

  xyz-worx

Offline Greg Erskine

  • Sr. Member
  • ****
  • Posts: 404
Re: Busybox-httpd + GPIO
« Reply #14 on: September 17, 2015, 07:03:27 AM »
Been having fun, sort of.
Starting to like Javascript, also starting to understand how the web + CGI stuff works.

HTML code

   <div>
   <button onclick="switchon()" id="onoff"  style="font-weight:bold; font-size:250%; background-color:grey" >On</button>
   <button onclick="switchoff()" id="offon"  style="font-weight:bold; font-size:250%; background-color:red" >Off</button>
   <button  id="stepstatus"  style="font-size:250%; background-color:yellow" >stopped</button>
   </div>
....
<script>
   function switchon(){
       console.log("on");
       document.getElementById("offon").style.background = "grey";
            document.getElementById("onoff").style.background = "lightgreen";
            document.getElementById("stepstatus").innerHTML = 'Running';
       document.location="/cgi-bin/poweron.cgi";
   }
         
   function switchoff(){
       console.log("off");
       document.getElementById("onoff").style.background = "grey";
           document.getElementById("offon").style.background = "red";
           document.getElementById("stepstatus").innerHTML = 'Stopped';
       document.location="/cgi-bin/poweroff.cgi";
   }



poweron.cgi

#!/bin/sh
echo "HTTP/1.1 204 No Content"
echo ""

echo 1 > /sys/class/gpio/gpio17/value


By calling different xx.cgi files I don't need to read the query string just to turn a GPIO on and off.

console.log is useful for debugging, right click browser window "Inspect element", in Chrome anyway.
Could fancy it up by using image buttons etc.
Think I now have a good idea on how Webiopi might work, still seems like a lot of code to do the same thing?

Hi gavinmc42,

Good work! There is no "right" answer to your questions.

If you want to keep your image small, then you need to just use sh, html5, css and javascript as you have done. This is Steen's and my philosophy is developing piCorePlayer. If you are developing an application then lots of small files end up being a pain to manage. If you are just controlling a couple of inputs or outputs then is does not matter one way or the other.

I have used perl for scripting for the last 25 years, but in the piCore environment it is relatively big and bulky. So it may not be an option.

Python seems to be the preferred language for the Raspberry Pi. Just about everything example of code you ever see for the Raspberry Pi is python. Why? I must have been asleep for a decade or two, never noticed python becoming so popular.

For embedded systems lua seems to be the go. It is used on a lot of modems for their configuration environment. Have a look at dd-wrt etc.

As far as bulky code goes, it doesn't really matter. Once you work out the method, you just make it a routine and called it over and over. Here's a snippet of pcp-functions.

Code: [Select]
#=========================================================================================
# Banner, navigation, footer and controls html
#-----------------------------------------------------------------------------------------

#-----------------------------------------------------------------------------------------
# $1 = title, $2 = author, $3 = seconds, $4 = url
#-----------------------------------------------------------------------------------------
pcp_html_head() {
echo 'Content-Type: text/html'
echo ''
echo '<!DOCTYPE html>'
echo '<!-- Start of pcp_html_head -->'
echo '<html>'
echo '<head>'
echo '  <meta charset="UTF-8">'
echo '  <meta http-equiv="Cache-Control" content="no-cache">'
echo '  <meta http-equiv="Pragma" content="no-cache">'
echo '  <meta http-equiv="Expires" content="0">'
if [ $DEBUG = 0 ]; then
if [ x"" != x"$3" ] && [ x"" != x"$4" ]; then
echo '  <meta http-equiv="Refresh" content="'$3'; url='$4'">'
fi
fi
echo '  <title>pCP - '$1'</title>'
echo '  <meta name="author" content="'$2'">'
echo '  <meta name="description" content="'$1'">'
echo '  <link rel="stylesheet" type="text/css" href="../css/piCorePlayer.css">'
echo '  <link rel="icon" href="../images/pCP.png" type="image/x-icon" />'
echo '  <script src="../js/piCorePlayer.js"></script>'
echo '</head>'
echo ''
echo '<body>'
echo '<!-- End of pcp_html_head -->'
}

pcp_html_head_no_content() {
echo -n "HTTP/1.1 204 No Content\n\n"
#cat <<EOF
#HTTP/1.1 204 No Content
#
#EOF
# printf "%s\n\n" "HTTP/1.1 204 No Content"
}

pcp_banner() {
echo '<!-- Start of pcp_banner -->'
echo '<table class="bgblack">'
echo '  <tr>'
echo '    <td height="148">'
echo '      <p class="banner">'
echo '        <a href="about.cgi" title="pCP Logo"><img src="../images/banner.png" alt="piCorePlayer" /></a>'
echo '      </p>'
echo '    </td>'
echo '  </tr>'
echo '</table>'
echo '<!-- End of pcp_banner -->'
}

piCorePlayer is now over 11,000 lines of mainly shell scripts. Developing a new script, is just  a matter of cutting and pasting segments of existing code, then modifying it to suit. The stage you are going through is the difficult and tedious stage, working everything out for the first time. I am enjoying your thread as I am always looking out for better or easier to managed ways of doing things, especially as it is based on piCore, busybox-httpd, sh, html5, css and javascript, exactly the decisions Steen and I have made.

regards
Greg