tags:

views:

586

answers:

3

I bought this temperature sensor logger kit: http://quozl.netrek.org/ts/. It works great with the supplied C code, I like to use python because of its simplicity, so I wrote a script in python that displays the output from the microcontroller. I only have one temperature sensor hooked up to the kit. I want the temperature to be displayed on a web page, but can't seem to figure it out, I'm pretty sure it has something to do with the output from the micro having a \r\n DOS EOL character and linux web servers do not interpret it properly. The book I have says "Depending on the web server you are using, you might need to make configuration changes to understand how to serve CGI files." I am using debian and apache2 and basic cgi scripts work fine.

Here is my code for just displaying the sensor to the console (this works fine):

import serial

ser = serial.Serial('/dev/ttyS0', 2400)

while 1:
   result = ser.readline()
   if result:
      print result

Here is my test.cgi script that works:

#!/usr/bin/python
print "Content-type: text/html\n"
print "<title>CGI Text</title>\n"
print "<h1>cgi works!</h1>"

Here is the cgi script I have started to display temp (doesn't work - 500 internal server error):

#!/usr/bin/python
import sys, serial

sys.stderr = sys.stdout

ser = serial.Serial('/dev/ttyS0', 2400)

print "Content-type: text/html\n"

print """
<title>Real Time Temperature</title>

   <h1>Real Time Temperature:</h1>
"""

#result = ser.readline()
#if result:
print ser.readline()

If i run python rtt.cgi in the console it outputs the correct html and temperature, I know this will not be real time and that the page will have to be reloaded every time that the user wants to see the temperature, but that stuff is coming in the future.. From my apache2 error log it says: malformed header from script. Bad header= File "/usr/lib/cgi-bin/rtt.c: rtt.cgi

+2  A: 

I'm guessing that the execution context under which your CGI is running is unable to complete the read() from the serial port.

Incidentally the Python standard libraries have MUCH better ways for writing CGI scripts than what you're doing here; and even the basic string handling offers a better way to interpolate your results (assuming you code has the necessary permissions to read() them) into the HTML.

At least I'd recommend something like:

#!/usr/bin/python
import sys, serial

sys.stderr = sys.stdout
ser = serial.Serial('/dev/ttyS0', 2400)

html = """Content-type: text/html

<html><head><title>Real Time Temperature</title></head><body>
<h1>Real Time Temperature:</h1>
<p>%s</p>
</body></html>
"""  % ser.readline()   # should be cgi.escape(ser.readline())!
ser.close()
sys.exit(0)

Notice we just interpolate the results of ser.readline() into our string using the % string operator. (Incidentally your HTML was missing <html>, <head>, <body>, and <p> (paragraph) tags).

There are still problems with this. For example we really should at least import cgi wrap the foreign data in that to ensure that HTML entities are properly substituted for any reserved characters, etc).

I'd suggest further reading: [Python Docs]: http://docs.python.org/library/cgi.html

Jim Dennis
I still get a 500 internal server error with the above code. I also imported cgi and did cgi.escape as in the comments. Should I add cgi.escape to the ser.close()?
Did you verify that ser.readline() has sufficient privileges to operate when it's being executed by your web server? That was the first part of my suggestion and the only part that's really relevant to your 500 error. The rest was just suggestions on better CGI scripting in general.Remember that your web server probably runs CGI scripts as some sort of "nobody" (unless you're using some sort of suExec feature) and it may be using a `chroot` system call ... which would prevent access to /dev/ttyS*, etc).I think those are the source of your problem.
Jim Dennis
I am using a debian install, and used apt to install apache2. I am not using mod suExec that I can know, but there is a suexec.load in the mods_enabled folder. The apache user is www-data, I think (its the default). I tried chmod o+rwx /dev/ttyS0 and for the hell of it chmod g+rwx /dev/ttyS0 and I still get the 500 error. Here is my addition to the apache2.conf file to allow execution of cgi scripts:# Added to allow execution of cgiScriptAlias /cgi-bin/ /var/www/cgi-bin/AddHandler cgi-script .cgi .py .pl<Directory /var/www>Options +ExeccgiAddHandler cgi-script .cgi .py .pl</Directory>
whoops..<code># Added to allow execution of cgiScriptAlias /cgi-bin/ /var/www/cgi-bin/AddHandler cgi-script .cgi .py .pl<Directory /var/www>Options +ExeccgiAddHandler cgi-script .cgi .py .pl</Directory></code>
I think I am confused as to how to determine whether or not ser.readline() has sufficient privileges when executed by the web server.
A: 

one more time:

# Added to allow cgi-bin to execute cgi, python and perl scripts
ScriptAlias /cgi-bin/ /var/www/cgi-bin/
AddHandler cgi-script .cgi .py .pl
<Directory /var/www>
Options +Execcgi
AddHandler cgi-script .cgi .py .pl
</Directory>
A: 

Michael,

It looks like the issue is definitely permissions, however, you shouldn't try to make your script have the permission of /dev/ttyS0. What you will probably need to do is spawn another process where the first thing you do is change your group to the group of the /dev/ttyS0 device. On my box that's 'dialout' you're may be different.

You'll need to import the os package, look in the docs for the Process Parameters, on that page you will find some functions that allow you to change your ownership. You will also need to use one of the functions in Process Management also in the os package, these functions spawn processes, but you will need to choose one that will return the data from the spawned process. The subprocess package may be better for this.

The reason you need to spawn another process is that the CGI script need to run under the Apache process and the spawn process needs to access the serial port.

If I get a chance in the next few days I'll try to put something together for you, but give it a try, don't wait for me.

Also one other thing all HTTP headers need to end in two CRLF sequences. So your header needs to be:

print "Content-type: text/html\r\n\r\n"

If you don't do this your browser may not know when the header ends and the entity data begins. Read RFC-2616

~Carl