logger.py 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537
  1. #!/usr/bin/env python
  2. import numpy as np
  3. import os, serial,time,socket,sys,json,logging,requests,getopt
  4. # import the server implementation
  5. # import ADC
  6. #import vedirect
  7. #import upload_osm
  8. configfile="config.json"
  9. cf=open(configfile,"r")
  10. log_conf=json.load(cf)
  11. cf.close()
  12. devicename=socket.gethostname()
  13. if "device" in log_conf:
  14. devicename=log_conf['device']
  15. mean_count=5
  16. if "mean_count" in log_conf:
  17. mean_count=int(log_conf['mean_count'])
  18. channel_names=["time","CPU_temp"]
  19. channel_info={"time":{"sensor":"CPU","timestamp":0,"i2c":0},"CPU_temp":{"sensor":"CPU","timestamp":0,"i2c":0}}
  20. a = 2
  21. # import vedirect from https://github.com/karioja/vedirect
  22. # description of channels:https://beta.ivc.no/wiki/index.php/Victron_VE_Direct_DIY_Cable
  23. class vedirect:
  24. def __init__(self, serialport, timeout):
  25. self.serialport = serialport
  26. self.ser = serial.Serial(serialport, 19200, timeout=timeout)
  27. self.header1 = '\r'
  28. self.header2 = '\n'
  29. self.hexmarker = ':'
  30. self.delimiter = '\t'
  31. self.key = ''
  32. self.value = ''
  33. self.bytes_sum = 0;
  34. self.state = self.WAIT_HEADER
  35. self.dict = {}
  36. (HEX, WAIT_HEADER, IN_KEY, IN_VALUE, IN_CHECKSUM) = range(5)
  37. def input(self, byte):
  38. if byte == self.hexmarker and self.state != self.IN_CHECKSUM:
  39. self.state = self.HEX
  40. if self.state == self.WAIT_HEADER:
  41. self.bytes_sum += ord(byte)
  42. if byte == self.header1:
  43. self.state = self.WAIT_HEADER
  44. elif byte == self.header2:
  45. self.state = self.IN_KEY
  46. return None
  47. elif self.state == self.IN_KEY:
  48. self.bytes_sum += ord(byte)
  49. if byte == self.delimiter:
  50. if (self.key == 'Checksum'):
  51. self.state = self.IN_CHECKSUM
  52. else:
  53. self.state = self.IN_VALUE
  54. else:
  55. self.key += byte
  56. return None
  57. elif self.state == self.IN_VALUE:
  58. self.bytes_sum += ord(byte)
  59. if byte == self.header1:
  60. self.state = self.WAIT_HEADER
  61. self.dict[self.key] = self.value;
  62. self.key = '';
  63. self.value = '';
  64. else:
  65. self.value += byte
  66. return None
  67. elif self.state == self.IN_CHECKSUM:
  68. self.bytes_sum += ord(byte)
  69. self.key = ''
  70. self.value = ''
  71. self.state = self.WAIT_HEADER
  72. if (self.bytes_sum % 256 == 0):
  73. self.bytes_sum = 0
  74. return self.dict
  75. else:
  76. print 'Malformed packet'
  77. self.bytes_sum = 0
  78. elif self.state == self.HEX:
  79. self.bytes_sum = 0
  80. if byte == self.header2:
  81. self.state = self.WAIT_HEADER
  82. else:
  83. raise AssertionError()
  84. def read_data(self):
  85. while True:
  86. byte = self.ser.read(1)
  87. packet = self.input(byte)
  88. def read_data_single(self):
  89. while True:
  90. byte = self.ser.read(1)
  91. packet = self.input(byte)
  92. if (packet != None):
  93. return packet
  94. def read_data_callback(self, callbackFunction):
  95. while True:
  96. byte = self.ser.read(1)
  97. if byte:
  98. packet = self.input(byte)
  99. if (packet != None):
  100. callbackFunction(packet)
  101. else:
  102. break
  103. def print_data_callback(data):
  104. print data
  105. # end import vedirect
  106. def upload_osm(sensebox_id,sensor_id,value):
  107. url="https://ingress.opensensemap.org/boxes/%s/%s" % (sensebox_id,sensor_id)
  108. r = requests.post(url,json={'value': value})
  109. if (r.status_code != requests.codes.ok) & (r.status_code != 201):
  110. print("Error %d: %s" % (r.status_code,r.text))
  111. # host and port of internal logging server
  112. bsql=False
  113. if "sqlserver" in log_conf:
  114. bsql=True
  115. if "enable" in log_conf['sqlserver']:
  116. if log_conf['sqlserver']['enable'] == 0:
  117. bsql=False
  118. if bsql:
  119. sqlhost=log_conf['sqlserver']['host']
  120. sqlport=log_conf['sqlserver']['port']
  121. # config of lux sensor tls2591
  122. btls=False
  123. if "tsl2591" in log_conf:
  124. btls=True
  125. if "enable" in log_conf['tsl2591']:
  126. if log_conf['tsl2591']['enable'] == 0:
  127. btls=False
  128. if btls:
  129. import tsl2591
  130. tsl_port=1
  131. if "port" in log_conf['tsl2591']:
  132. tsl_port=int(log_conf['tsl2591']['port'])
  133. tsl_add=0x29
  134. if "address" in log_conf['tsl2591']:
  135. tsl_add=int(log_conf['tsl2591']['address'],16)
  136. try:
  137. tsl = tsl2591.Tsl2591(i2c_bus=tsl_port,sensor_address=tsl_add) # initialize
  138. except:
  139. btls=False
  140. else:
  141. channel_names.append("lux")
  142. channel_info["lux"]={"sensor":"tsl2591","timestamp":0,"i2c":tsl_add}
  143. # config of lux sensor tls2591
  144. bveml=False
  145. if "veml6070" in log_conf:
  146. bveml=True
  147. if "enable" in log_conf['veml6070']:
  148. if log_conf['veml6070']['enable'] == 0:
  149. bveml=False
  150. if bveml:
  151. import veml6070
  152. veml_port=1
  153. if "port" in log_conf['veml6070']:
  154. veml_port=int(log_conf['veml6070']['port'])
  155. try:
  156. veml = veml6070.Veml6070(i2c_bus=veml_port)
  157. except:
  158. bveml=False
  159. else:
  160. channel_names.append("uv")
  161. channel_info["uv"]={"sensor":"veml6070","timestamp":0,"i2c":0x38}
  162. # config of bme280 sensor
  163. # use pip install RPi.bme280 for library
  164. bbme=False
  165. if "bme280" in log_conf:
  166. bbme=True
  167. if "enable" in log_conf['bme280']:
  168. if log_conf['bme280']['enable'] == 0:
  169. bbme=False
  170. if bbme:
  171. import smbus2
  172. import bme280
  173. if "port" in log_conf['bme280']:
  174. bme_port=log_conf['bme280']['port']
  175. else:
  176. bme_port=1
  177. if "address" in log_conf['bme280']:
  178. bme_add=int(log_conf['bme280']['address'],16)
  179. else:
  180. bme_add=0x77
  181. try:
  182. bme_bus=smbus2.SMBus(bme_port)
  183. except:
  184. bbme=False
  185. else:
  186. calibration_params=bme280.load_calibration_params(bme_bus,bme_add)
  187. for n in ("temperature","humidity","pressure"):
  188. channel_names.append(n)
  189. channel_info[n]={"sensor":"bme280","timestamp":0,"i2c":bme_add}
  190. # configure the client logging
  191. logging.basicConfig()
  192. log = logging.getLogger('./modbus.error')
  193. log.setLevel(logging.ERROR)
  194. # configure tristar
  195. btristar=False
  196. if "tristar" in log_conf:
  197. btristar=True
  198. if "enable" in log_conf['tristar']:
  199. if log_conf['tristar']['enable'] == 0:
  200. btristar=False
  201. if btristar:
  202. import smbus
  203. from pymodbus.client.sync import ModbusSerialClient as ModbusClient
  204. tri_port='/dev/ttyUSB0'
  205. if "port" in log_conf['tristar']:
  206. tri_port=log_conf['tristar']['port']
  207. tri_baud=9600
  208. if "baud" in log_conf['tristar']:
  209. tri_baud=log_conf['tristar']['baud']
  210. tri_timeout=1
  211. if "timeout" in log_conf['tristar']:
  212. tri_timeout=log_conf['tristar']['timeout']
  213. try:
  214. triclient = ModbusClient(method='rtu', port=tri_port, baudrate=tri_baud, timeout=tri_timeout)
  215. except:
  216. btristar=False
  217. else:
  218. triclient.connect()
  219. for i in ["volt_scale","amp_scale","volt_bat_term","volt_bat_sens","volt_arr","amp_bat","amp_arr","temp_heatsink","temp_bat","ah_res","ah_tot","kwh_res","kwh_tot","watt_in","watt_out","hour_tot","state","volt_sweep_mp","volt_sweep_oc"]:
  220. channel_names.append(i)
  221. channel_info[i]={"sensor":"tristar","timestamp":0,"i2c":0}
  222. # declare ve mppt
  223. bve=False
  224. if "vedirect" in log_conf:
  225. bve=True
  226. if "enable" in log_conf['vedirect']:
  227. if log_conf['vedirect']['enable'] == 0:
  228. bve=False
  229. if "port" in log_conf['vedirect']:
  230. bve_port=log_conf['vedirect']['port']
  231. else:
  232. bve_port='/dev/serial/by-id/usb-VictronEnergy_BV_VE_Direct_cable_VE1SSBVT-if00-port0'
  233. print(bve_port)
  234. try:
  235. ve=vedirect(bve_port,60)
  236. except:
  237. bve=False
  238. else:
  239. for i in ["volt_bat_ve","volt_arr_ve","amp_ve","watt_ve","days_ve"]:
  240. channel_names.append(i)
  241. channel_info[i]={"sensor":"victron","timestamp":0,"i2c":0}
  242. print(bve)
  243. # declare adc
  244. badc=False
  245. if "ads1x15" in log_conf:
  246. badc=True
  247. ads_conf=log_conf['ads1x15']
  248. if "enable" in ads_conf:
  249. if ads_conf['enable'] == 0:
  250. badc=False
  251. if badc:
  252. adc={}
  253. GAIN=1
  254. if "gain" in ads_conf:
  255. GAIN=ads_conf['gain']
  256. if "adc" in ads_conf:
  257. from Adafruit_ADS1x15 import ADS1115
  258. from Adafruit_ADS1x15 import ADS1015
  259. adc_count=0
  260. tadc=ads_conf['adc']
  261. for x in tadc:
  262. y=tadc[x]
  263. adc_address=0x48
  264. if "address" in y:
  265. adc_address=int(y['address'],16)
  266. abc_bus=1
  267. if "busnum" in y:
  268. adc_bus=y['busnum']
  269. adc_type=1015
  270. if "type" in y:
  271. adc_type=y['type']
  272. adc_assigned=False
  273. if adc_type == 1115:
  274. try:
  275. adc[adc_count]=ADS1115(address=adc_address,busnum=adc_bus)
  276. adc_assigned=True
  277. except:
  278. print("could not assign ADC")
  279. if adc_type == 1015:
  280. try:
  281. adc[adc_count]=ADS1015(address=adc_address,busnum=adc_bus)
  282. adc_assigned=True
  283. except:
  284. print("could not assign ADC")
  285. if adc_assigned:
  286. for j in range(4):
  287. cnadc="a"+str(adc_count)+"_"+str(j)
  288. channel_names.append(cnadc)
  289. channel_info[cnadc]={"sensor":"ads"+str(adc_type),"timestamp":0,"i2c":adc_address}
  290. adc_count=adc_count+1
  291. else:
  292. badc=False
  293. bmcp9808=False
  294. if "mcp9808" in log_conf:
  295. bmcp9808=True
  296. if "enable" in log_conf['mcp9808']:
  297. if log_conf['mcp9808']['enable'] == 0:
  298. bmcp9808=False
  299. if bmcp9808:
  300. import Adafruit_MCP9808.MCP9808 as MCP9808
  301. try:
  302. sens_9808 = MCP9808.MCP9808()
  303. except:
  304. bmcp9808 = False
  305. else:
  306. sens_9808.begin()
  307. channel_names.append("temp_9808")
  308. channel_info["temp_9808"]={"sensor":"bmcp9808","timestamp":0,"i2c":0x18}
  309. # push options to internet
  310. bosm=False
  311. if "opensensemap" in log_conf:
  312. bosm=True
  313. conf_osm=log_conf['opensensemap']
  314. if "enable" in conf_osm:
  315. if conf_osm['enable'] == 0:
  316. bosm = False
  317. if bosm:
  318. try:
  319. push_count=conf_osm['push_count']
  320. except:
  321. push_count=20 # wait 5 cycles till upload to opensensemap
  322. push_counter=0
  323. sensebox_id=conf_osm['sensebox_id'] # id of opensensemap
  324. push_vars=conf_osm['sensors'].keys()
  325. for pv in conf_osm['sensors'].keys():
  326. if (pv not in channel_names):
  327. if len(push_vars)>1:
  328. push_vars.remove(pv)
  329. else:
  330. push_vars=[]
  331. if (len(push_vars) == 0):
  332. bosm=False
  333. else:
  334. push_data={}
  335. push_mean_counts={}
  336. sensebox_sid={}
  337. for i in push_vars:
  338. push_data[i]=0
  339. push_mean_counts[i]=0
  340. sensebox_sid[i]=conf_osm['sensors'][i]
  341. #luftdaten_id=56009074600018881
  342. ch_val=np.zeros(len(channel_names))
  343. for i in range(len(channel_names)):
  344. ch_val[i]=(-1)
  345. while a > 1:
  346. # copy channel values to backup
  347. ch_old=ch_val.copy()
  348. ch_val=np.zeros(len(channel_names))
  349. ch_mean=ch_val.copy()
  350. # set actual time
  351. timestamp=int(1000*time.time())
  352. ch_val[channel_names.index("time")]=timestamp
  353. for n in range(mean_count):
  354. # get cpu temperature
  355. ch_val[channel_names.index("CPU_temp")] = int(open('/sys/class/thermal/thermal_zone0/temp').read())
  356. # get mcp9808 temperatur
  357. if bmcp9808:
  358. ch_val[channel_names.index("temp_9808")] = int(1000*sens_9808.readTempC())
  359. # get tls lux
  360. if btls:
  361. tsl_full,tsl_ir=tsl.get_full_luminosity()
  362. ch_val[channel_names.index("lux")] = int(1000*tsl.calculate_lux(tsl_full,tsl_ir))
  363. # get uv index
  364. if bveml:
  365. tsl_full,tsl_ir=tsl.get_full_luminosity()
  366. veml.set_integration_time(veml6070.INTEGRATIONTIME_1T)
  367. # uv_raw = veml.get_uva_light_intensity_raw()
  368. ch_val[channel_names.index("uv")] = int(1000*veml.get_uva_light_intensity())
  369. # read adc's
  370. if badc:
  371. for i in adc:
  372. cname="a"+str(i)+"_{0}"
  373. for j in range(4):
  374. ch_val[channel_names.index(cname.format(j))]=adc[i].read_adc(j,gain=GAIN)
  375. if btristar:
  376. try:
  377. rr = triclient.read_holding_registers(0,90,unit=1)
  378. except:
  379. print("could not get data from tristar")
  380. else:
  381. try:
  382. ch_val[channel_names.index("volt_scale")]=rr.registers[0]*65536+rr.registers[1]
  383. except:
  384. print("could not read from tristar")
  385. else:
  386. ch_val[channel_names.index("amp_scale")]= rr.registers[2]*65536+rr.registers[3]
  387. ch_val[channel_names.index("volt_bat_term")]=rr.registers[25]
  388. ch_val[channel_names.index("volt_bat_sens")]=rr.registers[26]
  389. ch_val[channel_names.index("volt_arr")]=rr.registers[27] # Array voltage
  390. ch_val[channel_names.index("amp_bat")]=rr.registers[28] # Battery current
  391. ch_val[channel_names.index("amp_arr")]=rr.registers[29] # Array current
  392. ch_val[channel_names.index("temp_heatsink")]=rr.registers[35] # Temperature heatsink
  393. ch_val[channel_names.index("temp_bat")]=rr.registers[36] # Temperature battery
  394. ch_val[channel_names.index("ah_res")]=rr.registers[52] * 65536 + rr.registers[53] # Ah resetable
  395. ch_val[channel_names.index("ah_tot")]=rr.registers[54] * 65536 + rr.registers[55] # Ah total
  396. ch_val[channel_names.index("kwh_res")]=rr.registers[56] # kwh resetable
  397. ch_val[channel_names.index("kwh_tot")]=rr.registers[57] # kwh total
  398. ch_val[channel_names.index("watt_in")]=rr.registers[58] # Power in
  399. ch_val[channel_names.index("watt_out")]=rr.registers[59] # Power out
  400. ch_val[channel_names.index("hour_tot")]=rr.registers[42] * 65536 + rr.registers[43] # hour total
  401. ch_val[channel_names.index("state")]=rr.registers[50] # State
  402. ch_val[channel_names.index("volt_sweep_mp")]=rr.registers[61] # Array voltage
  403. ch_val[channel_names.index("volt_sweep_oc")]=rr.registers[62] # Array voltage
  404. # read bme280 (temperature, humidity, pressure)
  405. if bbme:
  406. bme_data=bme280.sample(bme_bus,bme_add,calibration_params)
  407. ch_val[channel_names.index("temperature")]=int(1000*bme_data.temperature) # Temperature
  408. ch_val[channel_names.index("pressure")]=int(1000*bme_data.pressure) # Pressure
  409. ch_val[channel_names.index("humidity")]=int(1000*bme_data.humidity) # Humidity
  410. # read ve data
  411. if bve:
  412. try:
  413. vedata=ve.read_data_single()
  414. except:
  415. print("could not read VE")
  416. else:
  417. if 'V' in vedata:
  418. ch_val[channel_names.index("volt_bat_ve")]=int(vedata['V']) # Battery voltage measured by ve
  419. if 'VPV' in vedata:
  420. ch_val[channel_names.index("volt_arr_ve")]=int(vedata['VPV']) # Array voltage measured by ve
  421. if 'I' in vedata:
  422. ch_val[channel_names.index("amp_ve")]=int(vedata['I']) # loading current by ve
  423. if 'PPV' in vedata:
  424. ch_val[channel_names.index("watt_ve")]=int(vedata['PPV']) # Array power measured by ve
  425. if 'HSDS' in vedata:
  426. ch_val[channel_names.index("days_ve")]=int(vedata['HSDS']) # total days online ve
  427. for i in range(len(ch_val)):
  428. ch_mean[i]=ch_mean[i]+ch_val[i]
  429. for i in range(len(ch_val)):
  430. ch_val[i]=int(ch_mean[i]/mean_count)
  431. timefile=round(timestamp/3600000)
  432. f1=open("/home/pi/log/data_{:d}.txt".format(int(timefile)),"a")
  433. payload={}
  434. for i in range(len(ch_val)):
  435. cni=channel_names[i]
  436. if ch_val[i] != ch_old[i]:
  437. f1.write(cni + ":{0};".format(int(ch_val[i])))
  438. if cni != "time":
  439. payload[cni]=channel_info[cni]
  440. payload[cni]['value']=int(ch_val[i])
  441. channel_info[cni]['timestamp']=timestamp
  442. f1.write("\n")
  443. f1.close()
  444. if bosm:
  445. for pv in push_vars:
  446. if push_mean_counts[pv] == push_count:
  447. sense_data=push_data[pv]/(1000*push_mean_counts[pv])
  448. if pv == "volt_sweep_oc":
  449. sense_data=sense_data * ch_val[channel_names.index("volt_scale")] / 65536 / 32768
  450. upload_osm(sensebox_id,sensebox_sid[pv],round(sense_data,2))
  451. push_data[pv] = 0
  452. push_mean_counts[pv] = 0
  453. else:
  454. if channel_info[pv]['timestamp'] == timestamp:
  455. push_data[pv]=push_data[pv]+ch_val[channel_names.index(pv)]
  456. push_mean_counts[pv]=push_mean_counts[pv]+1
  457. if bsql:
  458. json_out={"time": ch_val[channel_names.index("time")],"device": devicename,"payload":payload}
  459. try:
  460. s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  461. except:
  462. print("{}: could not connect to database".format(time.time()))
  463. else:
  464. try:
  465. s.connect((sqlhost, sqlport))
  466. except:
  467. print("{}: could not connect to database".format(time.time()))
  468. else:
  469. s.sendall(json.dumps(json_out))
  470. s.close()
  471. # volt_batt=rr.registers[24] * volt_scaling / 65536 / 32768
  472. # volt_batt_t=rr.registers[25] * volt_scaling / 65536 / 32768
  473. # volt_batt_sens=rr.registers[26] * volt_scaling / 65536 / 32768
  474. # volt_arr=rr.registers[27] * volt_scaling / 65536 / 32768
  475. # curr_batt=rr.registers[28] * amp_scaling / 65536 / 32768
  476. # curr_arr=rr.registers[29] * amp_scaling / 65536 / 32768
  477. # temp_heatsink=rr.registers[35]
  478. # temp_batt=rr.registers[36]
  479. # ah_reset = rr.registers[52] * 65536 + rr.registers[53]
  480. # ah_total = rr.registers[54] * 65536 + rr.registers[55]
  481. # kwh_reset = rr.registers[56]
  482. # kwh_total = rr.registers[57]
  483. # power_in = rr.registers[58] * volt_scaling * amp_scaling / 131072 / 65536 / 65536
  484. # power_out = rr.registers[59] * volt_scaling * amp_scaling / 131072 / 65536 / 65536
  485. # hourm = rr.registers[42]*65536+rr.registers[43]
  486. # charge_state = rr.registers[50]
  487. if bosm:
  488. if push_counter>=push_count:
  489. push_counter = 1
  490. else:
  491. push_counter=push_counter+1
  492. time.sleep(5)
  493. # close the client
  494. client.close()
  495. f1.close()
  496. print("done")