Tiny Core Linux

Tiny Core Base => Raspberry Pi => Topic started by: gavinmc42 on September 13, 2015, 11:04:42 PM

Title: Busybox-httpd + GPIO
Post by: gavinmc42 on September 13, 2015, 11:04:42 PM
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?

Title: Re: Busybox-httpd + GPIO
Post by: gavinmc42 on September 13, 2015, 11:42:34 PM
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"
Title: Re: Busybox-httpd + GPIO
Post by: xyz-worx on September 14, 2015, 01: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
Title: Re: Busybox-httpd + GPIO
Post by: gavinmc42 on September 14, 2015, 06:01:57 PM
Thanks, you just doubled my entire CGI knowledge ;)

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

Title: Re: Busybox-httpd + GPIO
Post by: gavinmc42 on September 14, 2015, 09:53:53 PM
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>
Title: Re: Busybox-httpd + GPIO
Post by: Greg Erskine on September 14, 2015, 10:37:48 PM
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
Title: Re: Busybox-httpd + GPIO
Post by: Greg Erskine on September 14, 2015, 10:45:15 PM
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>'



Title: Re: Busybox-httpd + GPIO
Post by: Greg Erskine on September 14, 2015, 10:57:47 PM
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
Title: Re: Busybox-httpd + GPIO
Post by: gavinmc42 on September 15, 2015, 12: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
Title: Re: Busybox-httpd + GPIO
Post by: xyz-worx on September 15, 2015, 02: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 (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



Title: Re: Busybox-httpd + GPIO
Post by: Greg Erskine on September 15, 2015, 02: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
Title: Re: Busybox-httpd + GPIO
Post by: xyz-worx on September 16, 2015, 01: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
Title: Re: Busybox-httpd + GPIO
Post by: gavinmc42 on September 16, 2015, 11:08:26 PM
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?

Title: Re: Busybox-httpd + GPIO
Post by: xyz-worx on September 17, 2015, 01: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
Title: Re: Busybox-httpd + GPIO
Post by: Greg Erskine on September 17, 2015, 04: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
Title: Re: Busybox-httpd + GPIO
Post by: gavinmc42 on September 17, 2015, 06:33:31 PM
Hi Greg,

No right answer, with Linux the right answer is the first one that works, from a few million methods?
I do prefer the one or a few lines of code solution compared to libraries that must be compiled with dependencies.

Don't forget SVG:)
I use it for plotting data on the web pages.
Inline SVG makes for bigger html files but with Awk and Sed easy to modify.
Er, big here is relative, 2k becomes a 8k html file size.
Just don't expect fast updates ;) once per minute no problem.
Once per second with shell, updating is a problem.
Micropython/lua/Luajit, SVG plotting problem solved?

11,000 lines of script? ouch.

I only started Linux with the Raspberry Pi 3 yrs ago.
Did the simple Raspbian, Python, Pygame, Pyserial, python-smbus, gnuplot etc.
4GBs of OS and files and libraries etc. Keeping it updated was a pain.
Just backing up the SD Card version 8GB every time, lots of versions = lots of HD space.
Turns out not so simple, but it only took 8 days to port the python code from a mini ITX PC box to to Raspbian on Pi.
Original code on the Xbuntu PC took 8 months.

Then I found TinyCore on Pi, started playing with it around version 4.x-5?
This week I am finally happy to say nearly every previous project could have been done with just those methods.
piCore, HTML, Shell, CCS, CGI, SVG, JS and the only tcz's, OpenSSH, MC, i2c-tools, busybox-httpd.

Now with Bela making Micropthon part of the standard piCore and with Lua and Luajit, some higher speed scripting can be done.
Might need to compile C one day, but can still do lots with just the above.
Some of these tools are 25+ years old, Bash 1989, HTTP < 1991, CGI 1993.
Yet we are running it on a linux kernel Bela compiled last week or on 7.0Alpha 5 tomorrow?
Also with this low level way of doing stuff you feel more in control and know how everything works.
With lots of library stuff you need to rely on someone else's example code to figure how things work.

Regards
Gavin