DB "canary" added - with fail/restart when connection dropped
[weathermon.git] / bin / weathermon-iio
1 #!/usr/bin/lua
2
3 require "uci"
4 cur = uci.cursor()
5
6 lfs = require "lfs"
7 json = require "json"
8 socket = require "socket"
9
10 require "wm_util"
11
12 io.stdout:setvbuf('no')
13
14 function dump(o)
15    if type(o) == 'table' then
16       local s = '{ '
17       for k,v in pairs(o) do
18          if type(k) ~= 'number' then k = '"'..k..'"' end
19          s = s .. '['..k..'] = ' .. dump(v) .. ','
20       end
21       return s .. '} '
22    else
23       return tostring(o)
24    end
25 end
26
27 function get_device_list(config_name)
28
29   local devices
30   devices = {}
31
32   cur.foreach(config_name, "device", function(s)
33     devices[#devices+1] = s[".name"]
34   end)
35
36   return devices
37
38 end
39
40 function get_device(config_name,device_name)
41
42   local device
43   device = {}
44   
45   cur.foreach(config_name, "device", function(s)
46     if s[".name"] == device_name then
47       device = s
48     end
49   end)  
50   
51   return device
52   
53 end
54
55 function find_device(name,subsystem)
56
57   local search_base
58
59   if subsystem == "iio" then
60     search_base = "/sys/bus/iio/devices"
61     for file in lfs.dir(search_base) do
62       if get_file_content(search_base.."/"..file.."/name") == name then
63         return search_base.."/"..file
64       end
65     end
66   elseif subsystem == "hwmon" then
67     search_base = "/sys/class/hwmon"
68     for file in lfs.dir(search_base) do
69       if get_file_content(search_base.."/"..file.."/device/name") == name then
70         return search_base.."/"..file.."/device"
71       end
72     end  
73   end
74   
75   return nil
76
77 end
78
79 function get_parameter(record)
80   return tonumber(get_file_content(record["path"])) * record["scale"] + record["correction"]
81 end
82
83 function string.fromhex(str)
84   return (str:gsub('..', function (cc)
85     return string.char(tonumber(cc, 16))
86   end))
87 end
88
89 function string.tohex(str)
90   return (str:gsub('.', function (c)
91     return string.format('%02X', string.byte(c))
92   end))
93 end
94
95 function get_mhz(record)
96   local p = record["rs232"]  
97   p:read(9,100)
98   p:write(string.fromhex("ff0186000000000079"))
99   local e, s = p:read(9,1000)
100   if (e == 0) and (s:len() == 9) and (s:byte(1) == 255) then
101     local crc = 0
102     for i = 2, 8 do
103       crc = crc + s:byte(i)
104       if (crc>=256) then
105         crc = crc - 256
106       end  
107     end  
108     crc = 255 - crc
109     crc = crc + 1;
110     if crc == s:byte(9) then
111       return s:byte(3)*256+s:byte(4)
112     end  
113   end
114   
115   return nil
116    
117 end
118
119 function search_rs232_const(rs232,prefix,value)
120
121   for k,v in pairs(rs232) do
122   
123     if k == prefix..value:upper() then
124       return v
125     end  
126   
127   end
128
129   return nil
130
131 end
132
133 function init_serial_device(device,subsystem,parameters)
134
135   rs232 = require("luars232")
136
137   pcall(function ()
138
139     local e, port = rs232.open(device["port"])
140
141     local baud = device["baud"]; if baud == nil then baud = 9600; end
142     local bits = device["bits"]; if bits == nil then bits = 8; end
143     local stop_bits = device["stop_bits"]; if stop_bits == nil then stop_bits = 1; end
144     local parity = device["parity"]; if parity == nil then parity = "NONE"; end 
145     local flowctl = device["flowctl"]; if flowctl == nil then flowctl = "OFF"; end
146     
147     assert(port:set_baud_rate(search_rs232_const(rs232,"RS232_BAUD_",baud)) == rs232.RS232_ERR_NOERROR)
148     assert(port:set_data_bits(search_rs232_const(rs232,"RS232_DATA_",bits)) == rs232.RS232_ERR_NOERROR)
149     assert(port:set_parity(search_rs232_const(rs232,"RS232_PARITY_",parity)) == rs232.RS232_ERR_NOERROR)
150     assert(port:set_stop_bits(search_rs232_const(rs232,"RS232_STOP_",stop_bits)) == rs232.RS232_ERR_NOERROR)
151     assert(port:set_flow_control(search_rs232_const(rs232,"RS232_FLOW_",flowctl)) == rs232.RS232_ERR_NOERROR)
152     
153     getparameter = {}
154
155     getparameter["rs232"] = port
156   
157     if subsystem == "mhz" then
158     
159       getparameter["function"] = get_mhz 
160       getparameter["name"] = "CO2PPM"
161       getparameter["sensor"] = "MHZ19"
162       parameters[#parameters+1] = getparameter
163
164     end
165
166     parameters[#parameters+1] = getparameter
167     
168   end)
169
170 end
171
172 function init_i2c_device(device,subsystem,parameters)
173
174   if not i2c_bus then
175     i2c_bus = 0
176   end
177
178   pcall(function ()
179     local f = io.open("/sys/class/i2c-dev/i2c-"..i2c_bus.."/device/new_device","w")
180     io.output(f)
181     io.write(device["name"].." "..device["address"])
182     io.close(f)
183   end)  
184
185   device_path=find_device(device["name"],subsystem)
186
187   if device_path and device["set_param"] then
188     for key,record in pairs(device["set_param"]) do
189       setparam = split(record,":")
190       setpath = device_path.."/"..setparam[1]
191       pcall(function ()
192         local f = io.open(setpath,"w")
193         io.output(f)
194         io.write(setparam[2])
195         io.close(f)
196       end)  
197     end
198   end
199
200   if device_path and device["parameter"] then
201
202     for key,record in pairs(device["parameter"]) do
203
204       getparam = split(record,":")
205       getparameter = {}
206       getparameter["path"] = device_path.."/"..getparam[1]
207       getparameter["name"] = getparam[2]
208       getscale = getparam[3]
209       getcorrection = getparam[4]
210       if not getscale then
211         getscale = 1
212       end  
213       if not getcorrection then
214         getcorrection = 0
215       end
216       getparameter["scale"] = tonumber(getscale)
217       getparameter["sensor"] = device["name"]:upper()
218       getparameter["correction"] = tonumber(getcorrection)
219       getparameter["function"] = get_parameter
220       parameters[#parameters+1] = getparameter
221
222     end
223
224   end
225   
226 end
227
228 function init_device(device,parameters)
229
230   if device["module"] then
231     os.execute("modprobe "..device["module"])
232   end    
233
234   if device["type"] then
235
236     devtype = split(device["type"],":")
237     bus = devtype[1]
238     subsystem = devtype[2]
239     
240     if (bus == "i2c") then
241
242       init_i2c_device(device,subsystem,parameters)
243     
244     elseif (bus == "serial") then
245       
246       init_serial_device(device,subsystem,parameters) 
247     
248     end
249     
250   end
251
252 end
253
254 function init(config_name)
255
256   local parameters= {}
257
258   i2c_bus = uci.get(config_name,"hardware","i2c_bus")
259
260   local devices = get_device_list(config_name)
261   
262   for key,devname in pairs(devices) do
263   
264     device = get_device(config_name,devname)
265     
266     if device then
267       init_device(device,parameters)
268     end  
269   
270   end
271
272   return parameters
273
274 end
275
276 function get_parameters(parameters)
277   local results = {}
278   for key,record in pairs(parameters) do
279
280     if not results[record["sensor"]] then 
281       results[record["sensor"]] = {}
282     end
283     pcall(function ()
284         results[record["sensor"]][record["name"]] = record["function"](record)
285       end)
286   end
287   
288   return results
289 end
290
291 config_name = arg[1]
292 if not config_name then
293   config_name = "weathermon"
294 end  
295
296 web_id = get_devid(config_name)
297
298 parameters = init(config_name)
299
300 local delay = uci.get(config_name,"process","delay")
301
302 local working_dir = uci.get(config_name,"process","working_dir")
303 if working_dir then
304   lfs.mkdir(working_dir)
305 end
306
307 if not delay then
308   delay = 60
309 end
310
311 while true do
312     values = get_parameters(parameters)
313     records = {}
314     records[web_id] = {}
315     for key,record in pairs(values) do
316       records[web_id][key] = record
317       records[web_id]["timestamp"] = os.date("%Y-%m-%dT%H:%M:%S")
318     end
319     for key,value in pairs(values) do
320       value["device"] = key
321       print(json.encode(value))
322     end
323   socket.sleep(delay)
324 end