1#!/usr/bin/lua 2-- 3-- weatherstation.lua - a simple web server intended to be run from tcpd to 4-- expose sensors from the BeagleBone Weather cape and serve the web app. 5-- 6 7-- List of files shared by this service. 8distfiles={} 9distfiles["/index.html"] = true 10distfiles["/style.css"] = true 11distfiles["/jquery.js"] = true 12distfiles["/processing.js"] = true 13distfiles["/spin.js"] = true 14distfiles["/weatherstation.js"] = true 15 16-- Base path for distfiles 17prefix="/usr/share/beaglebone/weather" 18 19-- Check that the filename is part of this demo. 20function filename_is_valid(filename) 21 return distfiles[filename] ~= nil 22end 23 24-- Check if the string 's' starts with 'starting' 25function starts_with(s, starting) 26 return string.sub(s, 1, string.len(starting)) == starting 27end 28 29-- Check if the string 's' ends with 'ending' 30function ends_with(s, ending) 31 return string.sub(s, -string.len(ending)) == ending 32end 33 34-- Return the content type of the file based only on the file extension. 35function get_content_type(filename) 36 37 if ends_with(filename, ".js") then 38 return "application/javascript" 39 elseif ends_with(filename, ".css") then 40 return "text/css" 41 elseif ends_with(filename, ".html") then 42 return "text/html" 43 elseif ends_with(filename, ".json") then 44 return "application/json" 45 else 46 return "text/plain" 47 end 48end 49 50-- Reads from STDIN until an empty or nil line is received. 51-- Returns the first line of the request. 52function read_request() 53 54 local i = 0 55 local lines = {} 56 57 repeat 58 local line = io.read("*line") 59 if line == nil or line == "\r" then 60 break 61 end 62 lines[i] = line 63 i = i + 1 64 until false 65 66 return lines[0] 67end 68 69-- Return the entire contents of a file 70function read_file(filename) 71 local f = io.open(filename, "rb") 72 73 if f == nil then 74 return nil 75 end 76 local content = f:read("*all") 77 f:close() 78 79 return content 80end 81 82-- Extract the value of the LABEL:VALUE pairs read from the device files. 83function extract_value(data, pat) 84 85 local x = string.match(data, pat) 86 87 if x == nil then 88 return "0" 89 end 90 return x 91end 92 93-- Read the sensor values and generate json output 94function generate_json() 95 local json = "" 96 97 local tsl2550 = read_file("/dev/tsl2550b3s39") 98 if tsl2550 == nil then 99 return nil 100 end 101 local illuminance = extract_value(tsl2550, "ILLUMINANCE: (%d+)") 102 103 local sht21 = read_file("/dev/sht21b3s40") 104 if sht21 == nil then 105 return nil 106 end 107 local temperature = extract_value(sht21, "TEMPERATURE: (%d+.%d+)") 108 local humidity = extract_value(sht21, "HUMIDITY: (%d+.%d+)") 109 110 local bmp085 = read_file("/dev/bmp085b3s77") 111 if bmp085 == nil then 112 return nil 113 end 114 local pressure = extract_value(bmp085, "PRESSURE: (%d+)") 115 116 json = json .. "{\n" 117 json = json .. "\"temperature\": " .. temperature .. ",\n" 118 json = json .. "\"humidity\": " .. humidity .. ",\n" 119 json = json .. "\"illuminance\": " .. illuminance .. ",\n" 120 json = json .. "\"pressure\": " .. pressure .. "\n" 121 json = json .. "}\n" 122 123 return json 124end 125 126function handle_request(req) 127 128 if req == nil then 129 response("404 Not Found", "text/plain", "Unknown Request") 130 return 131 end 132 133 -- Parse filename out of HTTP request 134 local filename = (string.match(req, " %S+ "):gsub("^%s+", ""):gsub("%s+$", "")) 135 if filename == "" or filename == "/" then 136 filename = "/index.html" 137 end 138 139 -- Check if the filename is known (i.e. it's a file in the web app) 140 if filename_is_valid(filename) then 141 local contents = read_file(prefix .. filename) 142 143 if contents ~= nil then 144 response("200 OK", get_content_type(filename), contents) 145 else 146 response("404 Not Found", "text/plain", "Failed to load known file") 147 end 148 -- Else maybe the user is requesting the dynamic json data 149 elseif starts_with(filename, "/weather.json") then 150 local json = generate_json() 151 if json == nil then 152 response("500 Internal Server Error", "text/plain", "Could not get sensor values") 153 else 154 response("200 OK", get_content_type("/weather.json"), json) 155 end 156 -- Else, the user is requesting something not part of the app 157 else 158 response("403 Forbidden", "text/plain", "File not allowed.") 159 end 160 161 return 162end 163 164-- Send the response to the HTTP request. 165function response(err_code, content_type, content) 166 io.write("HTTP/1.1 " .. err_code .. "\r\n") 167 io.write("Content-Type: " .. content_type .. "\r\n") 168 io.write("\r\n") 169 io.write(content) 170 io.write("\r\n") 171end 172 173-- Read the request and then handle it. 174handle_request(read_request()) 175 176