Добавлен "ведомый" скрипт для использования hwmon и iio-интерфейсов датчиков.
+++ /dev/null
-#!/usr/bin/python
-
-import serial
-
-from os import listdir,system
-from os.path import isfile, join
-
-from pprint import pprint
-
-from termios import tcflush, TCIOFLUSH
-
-from time import sleep,time
-
-from uuid import getnode
-
-from hashlib import md5
-
-import socket
-
-import sys,traceback
-
-import pycurl
-from urllib import urlencode
-from StringIO import StringIO
-
-searchpath = '/dev/serial/by-id/'
-path = None
-baud = None
-timeout = None
-
-proc = None
-
-external_submit_interval = 320
-owm_submit_interval = 320
-expire_interval = 1200
-submit_time = time()
-submit_time_owm = time()
-
-import MySQLdb
-import ConfigParser
-
-import thread
-from threading import Timer
-
-def find_port():
-
- global serial_num
- global path
-
- files = listdir(searchpath)
- for f in files:
- if serialnum in f:
- return join(searchpath,f)
- return None
-
-def open_port(path):
-
- global proc, debug
-
- if debug>0:
- print "Opening path "+path
-
- if path == "-":
- return sys.stdin
-
- if path[0] == "=":
- import subprocess
- sleep(3)
- command=path[1:]
- try:
- command,args=command.split(' ',1)
- proc = subprocess.Popen([command,args],stdout=subprocess.PIPE)
- except:
- proc = subprocess.Popen([command],stdout=subprocess.PIPE)
- import subprocess
- return proc.stdout
-
- ser = serial.Serial(path,baud,timeout=timeout)
- if ser.portstr:
- tcflush(ser,TCIOFLUSH);
- return ser
-
-def read_port(ser):
-
- try:
- timeout_timer = Timer(timeout, thread.interrupt_main)
- timeout_timer.start()
-
- line=''
- while line=='':
- line = ser.readline()
- line = line.strip()
-
- return line
-
- except KeyboardInterrupt:
- return "<<TIMEOUT>>"
- finally:
- timeout_timer.cancel()
-
-def read_loop(ser,callback):
-
- global proc
-
- while True:
-
- try:
- line=read_port(ser)
- if line=="<<TIMEOUT>>":
- if debug>0:
- print "Reopening port..."
- print line
- ser.close()
- if proc:
- try:
- if debug>0:
- print "Terminating process..."
- proc.terminate()
- sleep(5)
- finally:
- None
- ser=open_port(path)
- if line:
- callback(line)
- except KeyboardInterrupt:
- break
- finally:
- None
-
-def print_log(str):
- global logging
- if debug>0:
- print str
- if logging == "on":
- system("logger -t weathermon \""+str+"\"")
-
-def submit_narodmon():
-
- param = { 'ID':devid }
-
- c = database.cursor()
- c.execute(
- '''
- select nm_id,value from
- (
- SELECT sensor_id,parameter_id,max(timestamp) timestamp,round(avg(value),1) value FROM meteo.sensor_values
- where
- timestamp>=date_add(now(), INTERVAL -300 SECOND)
- group by sensor_id,parameter_id
- ) v,meteo.ext_sensors e
- where v.sensor_id=e.sensor_id and v.parameter_id=e.param_id
- and nm_id is not null
- '''
- )
-
- queue=c.fetchall()
-
- if debug>1:
- pprint(queue)
-
- for (sensor,value) in queue:
- param[sensor] = value
-
- if debug>1:
- pprint (param)
-
- url = "http://narodmon.ru/post.php"
-
- try:
-
- response_buffer = StringIO()
- curl = pycurl.Curl()
-
- curl.setopt(curl.URL, url)
- curl.setopt(curl.WRITEFUNCTION, response_buffer.write)
- curl.setopt(curl.POST, 1)
- curl.setopt(curl.POSTFIELDS, urlencode(param))
-
- curl.perform()
- curl.close()
-
- response_value = response_buffer.getvalue()
-
- print_log('Narodmon response: '+response_value)
-
- return True
-
- except:
-
- exc_type, exc_value, exc_traceback = sys.exc_info()
- traceback.print_tb(exc_traceback, limit=1, file=sys.stdout)
- traceback.print_exception(exc_type, exc_value, exc_traceback,
- limit=2, file=sys.stdout)
- return False
-
-def submit_owm():
-
- url = "http://openweathermap.org/data/post"
- params = {'name':owm_station, 'lat':owm_lat, 'long':owm_lon}
-
- c = database.cursor()
- c.execute(
- '''
- select owm_id,value from
- (
- SELECT sensor_id,parameter_id,max(timestamp) timestamp,round(avg(value),1) value FROM meteo.sensor_values
- where
- timestamp>=date_add(now(), INTERVAL -300 SECOND)
- group by sensor_id,parameter_id
- ) v,meteo.ext_sensors e
- where v.sensor_id=e.sensor_id and v.parameter_id=e.param_id
- and owm_id is not null
- '''
- )
-
- queue=c.fetchall()
-
- if debug>1:
- pprint(queue)
-
- for (sensor,value) in queue:
- params[sensor]=value
- if debug>1:
- pprint (params)
-
- try:
-
- response_buffer = StringIO()
- curl = pycurl.Curl()
-
- curl.setopt(curl.URL, url)
- curl.setopt(curl.USERPWD, '%s:%s' % (owmuser, owmpasswd))
- curl.setopt(curl.WRITEFUNCTION, response_buffer.write)
- curl.setopt(curl.POST, 1)
- curl.setopt(curl.POSTFIELDS, urlencode(params))
-
- curl.perform()
- curl.close()
-
- response_value = response_buffer.getvalue()
-
- print_log('Openweathermap response: '+response_value)
-
- return True
-
- except:
-
- exc_type, exc_value, exc_traceback = sys.exc_info()
- traceback.print_tb(exc_traceback, limit=1, file=sys.stdout)
- traceback.print_exception(exc_type, exc_value, exc_traceback,
- limit=2, file=sys.stdout)
- return False
-
-def submit_data(sensor_type,sensor_id,sensor_param,param_value):
- global submit_time
- global submit_time_owm
- global external_submit_interval
- global owm_submit_interval
- c = database.cursor()
- c.execute('CALL meteo.submit_value(%s,%s,%s,%s,NULL)', (sensor_type,sensor_id,sensor_param,param_value))
- database.commit()
- if narmon=='on' and time()>submit_time+external_submit_interval:
- submit_narodmon()
- submit_time=time()
- if owmuser and time()>submit_time_owm+owm_submit_interval:
- submit_owm()
- submit_time_owm=time()
-
-def process_str(str):
- print_log("Received: "+str)
- try:
- msg_type, msg_body = str.split(':')
- except:
- return
- try:
- if msg_type == 'STATUS':
- print_log('Status: '+msg_body)
- elif msg_type == 'ERROR':
- print_log('Error: '+ msg_body)
- elif msg_type == 'SENSOR':
- sens = msg_body.split(',')
- sensor = {}
- sensor_type = None
- sensor_id = None
- for rec in sens:
- key,value = rec.split('=')
- value=value.strip()
- if len(value)>0:
- if key == 'TYPE':
- sensor_type = value
- elif key == 'ID':
- sensor_id = value
- else:
- sensor[key] = value
- if sensor_type:
- if not sensor_id:
- sensor_id=devid;
- for key in sensor:
- if sensor[key]:
- print_log('Type = '+sensor_type+', ID = '+sensor_id+', Param = '+key+', Value = '+sensor[key])
- submit_data(sensor_type,sensor_id,key,sensor[key])
- else:
- print_log('Error: got empty parameter value for '+sensor_type+'.'+sensor_id+'.'+key)
- elif msg_type == "ALARM":
- alarm = msg_body.split(',')
- device_type = None
- device_id = None
- for rec in alarm:
- key,value = rec.split('=')
- value=value.strip()
- if len(value)>0:
- if key == 'TYPE':
- device_type = value
- if key == 'ID':
- device_id = value
- if device_type:
- if not device_id:
- device_id=devid;
- print_log("Alarm: Type = "+device_type+", ID = "+device_id)
- if alarm_script:
- try:
- proc = subprocess.Popen([alarm_script,device_type,device_id,msg_body])
- except:
- print_log("Failed to execute alarm script")
- except:
- print_log('Exception processing...')
- exc_type, exc_value, exc_traceback = sys.exc_info()
- traceback.print_tb(exc_traceback, limit=1, file=sys.stdout)
- traceback.print_exception(exc_type, exc_value, exc_traceback,
- limit=5, file=sys.stdout)
- try:
- database.close()
- except:
- None
- reconnect()
-
-def weather_mon():
-
- global path
-
- if path is None:
- path = find_port()
- ser = open_port(path)
- read_loop(ser,process_str)
-
-def reconnect():
-
- connected = False
-
- while not connected:
-
- try:
-
- global database
- database = MySQLdb.connect(host=dbhost,user=dbuser,passwd=dbpasswd,use_unicode=True,connect_timeout=10)
- database.set_character_set('utf8')
- c = database.cursor()
- c.execute('SET NAMES utf8;')
- print_log("Database connected...")
- connected = True
-
- except:
-
- print_log("Error connecting database")
- sleep(30)
-
-def main():
- weather_mon()
-
-def init():
-
- global dbhost,dbuser,dbpasswd,path,serialnum,logging,debug;
- global timeout,baud,narmon,devid;
- global owmuser,owmpasswd,owm_temp,owm_pres,owm_humi,owm_lat,owm_lon,owm_station;
- global alarm_script;
-
- try:
-
- cfg = ConfigParser.RawConfigParser(allow_no_value=True)
- cfg.readfp(open('/etc/weathermon.conf'))
- dbhost = cfg.get("mysql","host")
- dbuser = cfg.get("mysql","user")
- dbpasswd = cfg.get("mysql","passwd")
- try:
- debug = cfg.get("logging","debug")
- except:
- debug = 0
- try:
- path = cfg.get("serial","port");
- except:
- path = None
- try:
- serialnum = cfg.get("serial","id")
- except:
- serialnum = None
- try:
- logging = cfg.get("logging","enabled")
- except:
- logging = None
- try:
- timeout = int(cfg.get("serial","timeout"))
- except:
- timeout = 120
- try:
- baud = int(cfg.get("serial","baud"))
- except:
- baud = 57600
- try:
- narmon = cfg.get("narodmon","enable")
- except:
- narmon = 'off'
- try:
- devid = cfg.get("narodmon","devid")
- except:
- devid = "{:0>12X}".format(getnode())
- try:
- owmuser = cfg.get("openweathermap","user")
- owmpasswd = cfg.get("openweathermap",'passwd')
- except:
- owmuser = None
- if owmuser:
- owm_temp = cfg.get("openweathermap",'temp')
- owm_pres = cfg.get("openweathermap",'pres')
- owm_humi = cfg.get("openweathermap",'humi')
- owm_lat = cfg.get("openweathermap",'lat')
- owm_lon = cfg.get("openweathermap",'lon')
- owm_station = cfg.get("openweathermap",'station')
- try:
- alarm_script = cfg.get("alarm","exec")
- import subprocess
- except:
- alarm_script = None
-
- reconnect()
-
- except:
-
- print_log("Cannot intialize system")
- exit()
-
-if __name__ == "__main__":
- import sys
- reload(sys)
- sys.setdefaultencoding('utf-8')
- init()
- main()
--- /dev/null
+#!/usr/bin/lua
+
+require "uci"
+cur = uci.cursor()
+
+lfs = require "lfs"
+json = require "json"
+socket = require "socket"
+
+function split(str, pat)
+ local t = {} -- NOTE: use {n = 0} in Lua-5.0
+ local fpat = "(.-)" .. pat
+ local last_end = 1
+ local s, e, cap = str:find(fpat, 1)
+ while s do
+ if s ~= 1 or cap ~= "" then
+ table.insert(t,cap)
+ end
+ last_end = e+1
+ s, e, cap = str:find(fpat, last_end)
+ end
+ if last_end <= #str then
+ cap = str:sub(last_end)
+ table.insert(t, cap)
+ end
+ return t
+end
+
+function trim(s)
+ return (s:gsub("^%s*(.-)%s*$", "%1"))
+end
+
+function get_device_list(config_name)
+
+ local devices
+ devices = {}
+
+ cur.foreach(config_name, "device", function(s)
+ devices[#devices+1] = s[".name"]
+ end)
+
+ return devices
+
+end
+
+function get_device(config_name,device_name)
+
+ local device
+ device = {}
+
+ cur.foreach(config_name, "device", function(s)
+ if s[".name"] == device_name then
+ device = s
+ end
+ end)
+
+ return device
+
+end
+
+function get_file_content(name)
+ local f = io.open(name,"r")
+ if f ~= nil then
+ local content = trim(f:read("*all"))
+ io.close(f)
+ return content
+ else
+ return false
+ end
+end
+
+function find_device(name,subsystem)
+
+ local search_base
+
+ if subsystem == "iio" then
+ search_base = "/sys/bus/iio/devices"
+ for file in lfs.dir(search_base) do
+ if get_file_content(search_base.."/"..file.."/name") == name then
+ return search_base.."/"..file
+ end
+ end
+ elseif subsystem == "hwmon" then
+ search_base = "/sys/class/hwmon"
+ for file in lfs.dir(search_base) do
+ if get_file_content(search_base.."/"..file.."/device/name") == name then
+ return search_base.."/"..file.."/device"
+ end
+ end
+ end
+
+ return nil
+
+end
+
+function init_device(device,parameters,i2c_bus)
+
+ if not i2c_bus then
+ i2c_bus = 0
+ end
+
+ if device["module"] then
+ os.execute("modprobe "..device["module"])
+ end
+
+ if device["type"] then
+
+ devtype = split(device["type"],":")
+ bus = devtype[1]
+ subsystem = devtype[2]
+
+ if (bus == "i2c") and device["address"] and device["name"] then
+ pcall(function ()
+ local f = io.open("/sys/class/i2c-dev/i2c-"..i2c_bus.."/device/new_device","w")
+ io.output(f)
+ io.write(device["name"].." "..device["address"])
+ io.close(f)
+ end)
+ end
+
+ device_path=find_device(device["name"],subsystem)
+
+ if device_path and device["set_param"] then
+ for key,record in pairs(device["set_param"]) do
+ setparam = split(record,":")
+ setpath = device_path.."/"..setparam[1]
+ pcall(function ()
+ local f = io.open(setpath,"w")
+ io.output(f)
+ io.write(setparam[2])
+ io.close(f)
+ end)
+ end
+ end
+
+ if device_path and device["parameter"] then
+
+ for key,record in pairs(device["parameter"]) do
+
+ getparam = split(record,":")
+ getparameter = {}
+ getparameter["path"] = device_path.."/"..getparam[1]
+ getparameter["name"] = getparam[2]
+ getscale = getparam[3]
+ getcorrection = getparam[4]
+ if not getscale then
+ getscale = 1
+ end
+ if not getcorrection then
+ getcorrection = 0
+ end
+ getparameter["scale"] = tonumber(getscale)
+ getparameter["sensor"] = device["name"]:upper()
+ getparameter["correction"] = tonumber(getcorrection)
+
+ parameters[#parameters+1] = getparameter
+
+ end
+
+ end
+
+ end
+
+end
+
+function init(config_name)
+
+ local parameters= {}
+
+ i2c_bus = uci.get(config_name,"hardware","i2c_bus")
+
+ local devices = get_device_list(config_name)
+
+ for key,devname in pairs(devices) do
+
+ device = get_device(config_name,devname)
+
+ if device then
+ init_device(device,parameters,i2c_bus)
+ end
+
+ end
+
+ return parameters
+
+end
+
+function get_parameter(parameter)
+ return tonumber(get_file_content(parameter["path"])) * parameter["scale"] + parameter["correction"]
+end
+
+function get_parameters(parameters)
+ local results = {}
+ for key,record in pairs(parameters) do
+ if not results[record["sensor"]] then
+ results[record["sensor"]] = {}
+ end
+ results[record["sensor"]][record["name"]] = get_parameter(record)
+ end
+ return results
+end
+
+config_name = arg[1]
+if not config_name then
+ config_name = "weathermon"
+end
+
+parameters = init(config_name)
+
+local delay = uci.get(config_name,"process","delay")
+
+local working_dir = uci.get(config_name,"process","working_dir")
+local dump_file = uci.get(config_name,"process","dump_file")
+if working_dir then
+ lfs.mkdir(working_dir)
+end
+
+if not delay then
+ delay = 60
+end
+
+while true do
+ values = get_parameters(parameters)
+ for key,record in pairs(values) do
+ dump = record
+ dump["device"] = key
+ print(json.encode(dump))
+ end
+ if dump_file then
+ local f = io.open(dump_file,"w")
+ io.output(f)
+ io.write(json.encode(values))
+ io.close(f)
+ end
+ socket.sleep(delay)
+end
+
\ No newline at end of file
+++ /dev/null
-#!/usr/bin/python
-
-import serial
-
-from os import system
-
-from pprint import pprint
-
-from termios import tcflush, TCIOFLUSH
-
-from time import sleep
-
-from uuid import getnode
-
-import sys,traceback
-
-import pycurl
-from urllib import urlencode
-from StringIO import StringIO
-
-path = None
-baud = None
-timeout = None
-
-import ConfigParser
-
-import thread
-from threading import Timer
-
-def open_port(path):
-
- global proc
-
- print "Opening path "+path
-
- ser = serial.Serial(path,1200);
- ser.open();
- ser.close();
-
- ser = serial.Serial(path,baud,timeout=timeout)
- if ser.portstr:
- tcflush(ser,TCIOFLUSH);
- return ser
-
-def read_port(ser):
-
- try:
- timeout_timer = Timer(timeout, thread.interrupt_main)
- timeout_timer.start()
- line = ser.readline()
- return line.strip()
- except KeyboardInterrupt:
- return "<<TIMEOUT>>"
- finally:
- timeout_timer.cancel()
-
-def read_loop(ser,callback):
-
- global proc
-
- while True:
-
- try:
- line=read_port(ser)
- if line=="<<TIMEOUT>>":
- print "Reopening port..."
- ser.close()
- ser=open_port(path)
- if line:
- callback(line)
- except KeyboardInterrupt:
- break
- finally:
- None
-
-def print_log(str):
- global logging
- print str
- if logging == "on":
- system("logger -t weathermon \""+str+"\"")
-
-def submit_data(stype,sid,param,value):
-
- global url,username,password
-
- params = {'stype':stype, 'sid':sid, 'param':param, 'value':value}
-
- pprint (params)
-
- try:
-
- response_buffer = StringIO()
- curl = pycurl.Curl()
-
- curl.setopt(curl.URL, url)
- if username:
- curl.setopt(curl.USERPWD, '%s:%s' % (username, password))
- curl.setopt(curl.WRITEFUNCTION, response_buffer.write)
- curl.setopt(curl.POST, 1)
- curl.setopt(curl.POSTFIELDS, urlencode(params))
-
- curl.perform()
- curl.close()
-
- response_value = response_buffer.getvalue()
-
- print_log('Server response: '+response_value)
-
- return True
-
- except:
-
- exc_type, exc_value, exc_traceback = sys.exc_info()
- traceback.print_tb(exc_traceback, limit=1, file=sys.stdout)
- traceback.print_exception(exc_type, exc_value, exc_traceback,
- limit=2, file=sys.stdout)
- return False
-
-def process_str(str):
- print_log("Received: "+str)
- try:
- msg_type, msg_body = str.split(':')
- except:
- return
- try:
- if msg_type == 'STATUS':
- print_log('Status: '+msg_body)
- elif msg_type == 'ERROR':
- print_log('Error: '+ msg_body)
- elif msg_type == 'SENSOR':
- sens = msg_body.split(',')
- sensor = {}
- sensor_type = None
- sensor_id = None
- for rec in sens:
- key,value = rec.split('=')
- value=value.strip()
- if len(value)>0:
- if key == 'TYPE':
- sensor_type = value
- elif key == 'ID':
- sensor_id = value
- else:
- sensor[key] = value
- if sensor_type:
- if not sensor_id:
- sensor_id=devid;
- for key in sensor:
- if sensor[key]:
- print_log('Type = '+sensor_type+', ID = '+sensor_id+', Param = '+key+', Value = '+sensor[key])
- submit_data(sensor_type,sensor_id,key,sensor[key])
- else:
- print_log('Error: got empty parameter value for '+sensor_type+'.'+sensor_id+'.'+key)
- except:
- print_log('Exception processing...')
- exc_type, exc_value, exc_traceback = sys.exc_info()
- traceback.print_tb(exc_traceback, limit=1, file=sys.stdout)
- traceback.print_exception(exc_type, exc_value, exc_traceback,
- limit=5, file=sys.stdout)
-
-def weather_mon():
-
- global path
-
- ser = open_port(path)
- read_loop(ser,process_str)
-
-def main():
- weather_mon()
-
-try:
-
- cfg = ConfigParser.RawConfigParser(allow_no_value=True)
- cfg.readfp(open('/etc/weathermon.conf'))
- url = cfg.get("web","url")
- path = cfg.get("serial","port");
- try:
- username = cfg.get("web","user");
- password = cfg.get("web","password");
- except:
- username = None
- password = None
- try:
- logging = cfg.get("logging","enabled")
- except:
- logging = None
- try:
- timeout = int(cfg.get("serial","timeout"))
- except:
- timeout = 120
- try:
- baud = int(cfg.get("serial","baud"))
- except:
- baud = 57600
- try:
- devid = cfg.get("web","devid")
- except:
- devid = "{:0>12X}".format(getnode())
-
-except:
-
- print_log("Cannot intialize system")
- exit()
-
-if __name__ == "__main__":
- import sys
- reload(sys)
- sys.setdefaultencoding('utf-8')
- main()
#!/usr/bin/lua
-require("json")
-require("socket")
+json = require("json")
+socket = require("socket")
function startswith(String,Start)
if String then
end
end
+ if not sensor_id then
+ sensor_id = web_devid
+ end
+
if not (sensor_type==nil or sensor_id==nil or sensor_type=='' or sensor_id=='') then
if next(sensor)==nil then
sensor["command"]="alarm"
end
end
else
- printLog("Cannot parse sensor input: "..msg_body)
+ printLog("Cannot parse sensor input: "..str)
end
end
return
end
while 1 do
- line=serialin:read()
+ line=serialin:read("*l")
if line == nil then
break
end
if startswith(line,'{') then
processJson(line)
else
- processLine(line)
+ processLine(line)
end
end
config internal 'web'
- option url http://server/path/send.php
+ option url http://url-to-submit-meteo-data
option user meteo
- option password somestrictpassword
- option iface eth0
+ option password some-password
+ option iface wlan0
-config internal 'serial'
- option port /dev/ttyUSB0
- option timeout 100
- option baud 9600
+config internal 'input'
+ option exec "/usr/bin/stdbuf -o0 /usr/bin/lua /usr/bin/weathermon-iio"
+# option port /dev/ttyATH0
+# option timeout 100
+# option baud 57600
config internal 'logging'
- option enabled on
+ option enabled off # on/stdout/syslog
+# option touch_file /var/run/weathermon/weathermon.last
config internal 'mqtt'
- option host server
+ option host mqtt.host.name
+ option user meteo-user
+ option password some-password
+
+config internal 'alarm'
+# option exec /usr/local/bin/alarm_received
+
+config internal 'hardware'
+ option i2c_bus 0
+
+config internal 'process'
+ option delay 48
+ option working_dir "/var/weather/"
+ option dump_file "/var/weather/weather.state"
+
+config device "bme280"
+ option module "bmp280_i2c"
+ option address "0x76"
+ option type "i2c:iio"
+ option name "bme280"
+ list set_param "in_humidityrelative_oversampling_ratio:4"
+ list set_param "in_temp_oversampling_ratio:8"
+ list set_param "in_pressure_oversampling_ratio:8"
+ list parameter "in_temp_input:T:0.001:-4" # source, name, scale, correction
+ list parameter "in_pressure_input:P:10"
+ list parameter "in_humidityrelative_input:H:0.001"
+
+config device "hmc5843"
+ option module "hmc5843_i2c"
+ option address "0x1e"
+ option type "i2c:iio"
+ option name "hmc5843"
+ list set_param "in_magn_meas_conf:normal"
+ list set_param "in_magn_sampling_frequency:2"
+ list set_param "in_magn_scale:0.000007692"
+ list parameter "in_magn_x_raw:MX"
+ list parameter "in_magn_y_raw:MY"
+ list parameter "in_magn_z_raw:MZ"
+
+config device "ads1115"
+ option module "ads1015"
+ option address "0x48"
+ option type "i2c:hwmon"
+ option name "ads1115"
+ list parameter "in4_input:CO"
+ list parameter "in5_input:CH4"
+ list parameter "in6_input:AIR"