175 lines
4.3 KiB
Lua
175 lines
4.3 KiB
Lua
|
#!/usr/bin/lua
|
||
|
--
|
||
|
-- weatherstation.lua - a simple web server intended to be run from tcpd to
|
||
|
-- expose sensors from the BeagleBone Weather cape and serve the web app.
|
||
|
--
|
||
|
|
||
|
-- List of files shared by this service.
|
||
|
distfiles={}
|
||
|
distfiles["/index.html"] = true
|
||
|
distfiles["/style.css"] = true
|
||
|
distfiles["/jquery.js"] = true
|
||
|
distfiles["/processing.js"] = true
|
||
|
distfiles["/spin.js"] = true
|
||
|
distfiles["/weatherstation.js"] = true
|
||
|
|
||
|
-- Base path for distfiles
|
||
|
prefix="/usr/share/beaglebone/weather"
|
||
|
|
||
|
-- Check that the filename is part of this demo.
|
||
|
function filename_is_valid(filename)
|
||
|
return distfiles[filename] ~= nil
|
||
|
end
|
||
|
|
||
|
-- Check if the string 's' starts with 'starting'
|
||
|
function starts_with(s, starting)
|
||
|
return string.sub(s, 1, string.len(starting)) == starting
|
||
|
end
|
||
|
|
||
|
-- Check if the string 's' ends with 'ending'
|
||
|
function ends_with(s, ending)
|
||
|
return string.sub(s, -string.len(ending)) == ending
|
||
|
end
|
||
|
|
||
|
-- Return the content type of the file based only on the file extension.
|
||
|
function get_content_type(filename)
|
||
|
|
||
|
if ends_with(filename, ".js") then
|
||
|
return "application/javascript"
|
||
|
elseif ends_with(filename, ".css") then
|
||
|
return "text/css"
|
||
|
elseif ends_with(filename, ".html") then
|
||
|
return "text/html"
|
||
|
elseif ends_with(filename, ".json") then
|
||
|
return "application/json"
|
||
|
else
|
||
|
return "text/plain"
|
||
|
end
|
||
|
end
|
||
|
|
||
|
-- Reads from STDIN until an empty or nil line is received.
|
||
|
-- Returns the first line of the request.
|
||
|
function read_request()
|
||
|
|
||
|
local i = 0
|
||
|
local lines = {}
|
||
|
|
||
|
repeat
|
||
|
local line = io.read("*line")
|
||
|
if line == nil or line == "\r" then
|
||
|
break
|
||
|
end
|
||
|
lines[i] = line
|
||
|
i = i + 1
|
||
|
until false
|
||
|
|
||
|
return lines[0]
|
||
|
end
|
||
|
|
||
|
-- Return the entire contents of a file
|
||
|
function read_file(filename)
|
||
|
local f = io.open(filename, "rb")
|
||
|
|
||
|
if f == nil then
|
||
|
return nil
|
||
|
end
|
||
|
local content = f:read("*all")
|
||
|
f:close()
|
||
|
|
||
|
return content
|
||
|
end
|
||
|
|
||
|
-- Extract the value of the LABEL:VALUE pairs read from the device files.
|
||
|
function extract_value(label, data, pat)
|
||
|
|
||
|
local x, y, z = string.find(data, label .. "%s+: (" .. pat .. ")")
|
||
|
if x == nil or y == nil or z == nil then
|
||
|
return "0"
|
||
|
end
|
||
|
return z
|
||
|
end
|
||
|
|
||
|
-- Read the sensor values and generate json output
|
||
|
function generate_json()
|
||
|
local json = ""
|
||
|
|
||
|
local tsl2550 = read_file("/dev/tsl2550b3s39")
|
||
|
if tsl2550 == nil then
|
||
|
return nil
|
||
|
end
|
||
|
local illuminance = extract_value("ILLUMINANCE", tsl2550, "%d+")
|
||
|
|
||
|
local sht21 = read_file("/dev/sht21b3s40")
|
||
|
if sht21 == nil then
|
||
|
return nil
|
||
|
end
|
||
|
local temperature = extract_value("TEMPERATURE", sht21, "%d+.%d+")
|
||
|
local humidity = extract_value("HUMIDITY", sht21, "%d+.%d+")
|
||
|
|
||
|
local bmp085 = read_file("/dev/bmp085b3s77")
|
||
|
if bmp085 == nil then
|
||
|
return nil
|
||
|
end
|
||
|
local pressure = extract_value("PRESSURE", bmp085, "%d+")
|
||
|
|
||
|
json = json .. "{\n"
|
||
|
json = json .. "\"temperature\": " .. temperature .. ",\n"
|
||
|
json = json .. "\"humidity\": " .. humidity .. ",\n"
|
||
|
json = json .. "\"illuminance\": " .. illuminance .. ",\n"
|
||
|
json = json .. "\"pressure\": " .. pressure .. "\n"
|
||
|
json = json .. "}\n"
|
||
|
|
||
|
return json
|
||
|
end
|
||
|
|
||
|
function handle_request(req)
|
||
|
|
||
|
if req == nil then
|
||
|
response("404 Not Found", "text/plain", "Unknown Request")
|
||
|
return
|
||
|
end
|
||
|
|
||
|
-- Parse filename out of HTTP request
|
||
|
local filename = (string.match(req, " %S+ "):gsub("^%s+", ""):gsub("%s+$", ""))
|
||
|
if filename == "" or filename == "/" then
|
||
|
filename = "/index.html"
|
||
|
end
|
||
|
|
||
|
-- Check if the filename is known (i.e. it's a file in the web app)
|
||
|
if filename_is_valid(filename) then
|
||
|
local contents = read_file(prefix .. filename)
|
||
|
|
||
|
if contents ~= nil then
|
||
|
response("200 OK", get_content_type(filename), contents)
|
||
|
else
|
||
|
response("404 Not Found", "text/plain", "Failed to load known file")
|
||
|
end
|
||
|
-- Else maybe the user is requesting the dynamic json data
|
||
|
elseif starts_with(filename, "/weather.json") then
|
||
|
local json = generate_json()
|
||
|
if json == nil then
|
||
|
response("500 Internal Server Error", "text/plain", "Could not get sensor values")
|
||
|
else
|
||
|
response("200 OK", get_content_type("/weather.json"), json)
|
||
|
end
|
||
|
-- Else, the user is requesting something not part of the app
|
||
|
else
|
||
|
response("403 Forbidden", "text/plain", "File not allowed.")
|
||
|
end
|
||
|
|
||
|
return
|
||
|
end
|
||
|
|
||
|
-- Send the response to the HTTP request.
|
||
|
function response(err_code, content_type, content)
|
||
|
io.write("HTTP/1.1 " .. err_code .. "\r\n")
|
||
|
io.write("Content-Type: " .. content_type .. "\r\n")
|
||
|
io.write("\r\n")
|
||
|
io.write(content)
|
||
|
io.write("\r\n")
|
||
|
end
|
||
|
|
||
|
-- Read the request and then handle it.
|
||
|
handle_request(read_request())
|
||
|
|