kopia lustrzana https://github.com/gshau/wxserver
Initial commit
commit
02e008ab25
|
@ -0,0 +1,14 @@
|
|||
Flask and SocketIO frontend to weather station. It can accept multiple stations simultaneously.
|
||||
|
||||
To setup:
|
||||
pip -r requirements
|
||||
|
||||
To run:
|
||||
Run station receiver that pushes socketIO packets to flask webserver:
|
||||
$python stationServer.py
|
||||
|
||||
Run flask webserver that listens for SocketIO packets:
|
||||
$python app.py
|
||||
|
||||
Webpage will be located at and will update when packet is received from stations
|
||||
http://localhost:5000/
|
|
@ -0,0 +1,66 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
# Set this variable to "threading", "eventlet" or "gevent" to test the
|
||||
# different async modes, or leave it set to None for the application to choose
|
||||
# the best option based on available packages.
|
||||
async_mode = None
|
||||
|
||||
if async_mode is None:
|
||||
try:
|
||||
import eventlet
|
||||
async_mode = 'eventlet'
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
if async_mode is None:
|
||||
try:
|
||||
from gevent import monkey
|
||||
async_mode = 'gevent'
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
if async_mode is None:
|
||||
async_mode = 'threading'
|
||||
|
||||
print('async_mode is ' + async_mode)
|
||||
|
||||
# monkey patching is necessary because this application uses a background
|
||||
# thread
|
||||
if async_mode == 'eventlet':
|
||||
import eventlet
|
||||
eventlet.monkey_patch()
|
||||
elif async_mode == 'gevent':
|
||||
from gevent import monkey
|
||||
monkey.patch_all()
|
||||
|
||||
import json
|
||||
import time
|
||||
from threading import Thread
|
||||
from flask import Flask, render_template
|
||||
from flask_socketio import SocketIO
|
||||
|
||||
from numpy import exp, cos, linspace
|
||||
import os, re
|
||||
|
||||
app = Flask(__name__, static_url_path='/static')
|
||||
app.config['SECRET_KEY'] = 'secret!'
|
||||
socketio = SocketIO(app, async_mode=async_mode)
|
||||
thread = None
|
||||
|
||||
|
||||
@socketio.on('dataPacket', namespace='/')
|
||||
def handle_message(message):
|
||||
if app.debug:
|
||||
print('Obtained packet from station: ')
|
||||
print message
|
||||
# print message['data']
|
||||
socketio.emit('dataPacket',
|
||||
{'data': 'Server generated event', 'packet': message},
|
||||
namespace='/web')
|
||||
|
||||
@app.route('/')
|
||||
def index():
|
||||
return render_template('index.html')
|
||||
|
||||
if __name__ == '__main__':
|
||||
socketio.run(app, host='0.0.0.0', port=5000, debug=False)
|
|
@ -0,0 +1,28 @@
|
|||
Flask==0.11.1
|
||||
Flask-SocketIO==2.7.1
|
||||
Jinja2==2.8
|
||||
MarkupSafe==0.23
|
||||
PyYAML==3.12
|
||||
Werkzeug==0.11.11
|
||||
argparse==1.2.1
|
||||
backports-abc==0.4
|
||||
bokeh==0.10.0
|
||||
certifi==2016.9.26
|
||||
click==6.6
|
||||
gevent==1.1.2
|
||||
greenlet==0.4.10
|
||||
itsdangerous==0.24
|
||||
numpy==1.11.1
|
||||
pandas==0.18.1
|
||||
python-dateutil==2.5.3
|
||||
python-engineio==1.0.3
|
||||
python-socketio==1.6.0
|
||||
pytz==2016.6.1
|
||||
pyzmq==15.4.0
|
||||
requests==2.11.1
|
||||
singledispatch==3.4.0.3
|
||||
six==1.10.0
|
||||
socketIO-client==0.7.0
|
||||
tornado==4.4.1
|
||||
websocket-client==0.37.0
|
||||
wsgiref==0.1.2
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
font-size: 320px;
|
||||
line-height: 2;
|
||||
}
|
|
@ -0,0 +1,198 @@
|
|||
import sqlite3
|
||||
import pandas as pd
|
||||
import stationDB as st
|
||||
from jinja2 import Template
|
||||
|
||||
from bokeh.plotting import *
|
||||
from bokeh.resources import INLINE
|
||||
# from bokeh.util.browser import view
|
||||
|
||||
from bokeh.models import HoverTool
|
||||
from bokeh.embed import components
|
||||
import time
|
||||
import datetime
|
||||
import os.path
|
||||
|
||||
|
||||
|
||||
class DB:
|
||||
def __init__(self,fileName, table):
|
||||
self.fileName=fileName
|
||||
self.table=table
|
||||
self.columnNames=[]
|
||||
if not os.path.isfile(self.fileName):
|
||||
self.createTable()
|
||||
self.getColumnNames()
|
||||
|
||||
def createTable(self):
|
||||
con=sqlite3.connect(self.fileName)
|
||||
c=con.cursor()
|
||||
c.execute("CREATE TABLE %s (timestamp real, timeString text)" % self.table)
|
||||
|
||||
con.close()
|
||||
|
||||
def addColumn(self, name):
|
||||
con=sqlite3.connect(self.fileName)
|
||||
c=con.cursor()
|
||||
type="real"
|
||||
c.execute("ALTER TABLE %s ADD COLUMN %s %s" % (self.table, name, type))
|
||||
con.close()
|
||||
|
||||
def getColumnNames(self):
|
||||
con=sqlite3.connect(self.fileName)
|
||||
command="SELECT * from %s" % self.table
|
||||
self.df=pd.read_sql_query(command, con)
|
||||
self.columnNames=self.df.columns
|
||||
con.close()
|
||||
|
||||
def addData(self, packet):
|
||||
con=sqlite3.connect(self.fileName)
|
||||
c=con.cursor()
|
||||
|
||||
# create column if not present
|
||||
for key in packet.keys():
|
||||
if key not in self.columnNames:
|
||||
self.addColumn(key)
|
||||
self.getColumnNames()
|
||||
|
||||
|
||||
columns=', '.join(packet.keys())
|
||||
placeholders = ', '.join('?' * len(packet))
|
||||
sql = 'INSERT INTO master ({}) VALUES ({})'.format(columns, placeholders)
|
||||
c.execute(sql, packet.values())
|
||||
|
||||
# # command="create table if not exists %s (date real, dateString text, name text, value real)" % name
|
||||
# c.execute(command)
|
||||
# timeString=datetime.datetime.fromtimestamp(time).strftime('%Y/%m/%d %H:%M:%S')
|
||||
# dataTuple=(time, timeString, name, data,)
|
||||
# command="INSERT INTO %s VALUES (?, ?, ?, ?)" % name
|
||||
# c.execute(command, dataTuple)
|
||||
con.commit()
|
||||
con.close()
|
||||
|
||||
def loadDB(self,name):
|
||||
con=sqlite3.connect(self.fileName)
|
||||
command="SELECT * from %s" % name
|
||||
self.df=pd.read_sql_query(command, con)
|
||||
con.close()
|
||||
|
||||
class DBview:
|
||||
def __init__(self,db,df,UTCOffset):
|
||||
# self.timeRange=timeRange
|
||||
self.db = db
|
||||
self.df = df
|
||||
self.UTCOffset = UTCOffset
|
||||
|
||||
|
||||
def qp(self,attrList, name, timeRange):
|
||||
now=datetime.datetime.now()
|
||||
epoch=datetime.datetime.utcfromtimestamp(0)
|
||||
tstart=now-datetime.timedelta(days=timeRange)
|
||||
tcut=(tstart-epoch).total_seconds()
|
||||
dataFrame=self.df[self.df.timestamp>tcut]
|
||||
dataFrame['t']=dataFrame.timestamp*1000
|
||||
# y1=getattr(self.df,attr1)
|
||||
# y2=getattr(self.df,attr2)
|
||||
output_file(name+'.html')
|
||||
timeString=[datetime.datetime.fromtimestamp(dt).strftime('%Y/%m/%d %H:%M:%S') for dt in dataFrame.timestamp]
|
||||
source=ColumnDataSource(data=dataFrame.to_dict('list'))
|
||||
TOOLS="resize,hover,crosshair,pan,wheel_zoom,box_zoom,reset,tap,previewsave,box_select,poly_select,lasso_select"
|
||||
p=figure(x_axis_type="datetime",tools=TOOLS)
|
||||
for key in attrList:
|
||||
print key
|
||||
p.scatter('t',key, source=source)
|
||||
p.select(dict(type=HoverTool)).tooltips=[
|
||||
("Time", "@timeString"),
|
||||
("Value", "@y1"),
|
||||
("Value", "@y2"),
|
||||
]
|
||||
show(p)
|
||||
|
||||
def qph(self,attrList, name, timeRange):
|
||||
now=datetime.datetime.now()
|
||||
epoch=datetime.datetime.utcfromtimestamp(0)
|
||||
tstart=now-datetime.timedelta(days=0,hours=timeRange)
|
||||
tcut=(tstart-epoch).total_seconds()
|
||||
dataFrame=self.df[self.df.timestamp>tcut]
|
||||
dataFrame['t']=dataFrame.timestamp*1000
|
||||
# y1=getattr(self.df,attr1)
|
||||
# y2=getattr(self.df,attr2)
|
||||
output_file(name+'.html')
|
||||
timeString=[datetime.datetime.fromtimestamp(dt).strftime('%Y/%m/%d %H:%M:%S') for dt in dataFrame.timestamp]
|
||||
source=ColumnDataSource(data=dataFrame.to_dict('list'))
|
||||
TOOLS="resize,hover,crosshair,pan,wheel_zoom,box_zoom,reset,tap,previewsave,box_select,poly_select,lasso_select"
|
||||
p=figure(x_axis_type="datetime",tools=TOOLS)
|
||||
for key in attrList:
|
||||
print key
|
||||
p.scatter('t',key, source=source)
|
||||
p.select(dict(type=HoverTool)).tooltips=[
|
||||
("Time", "@timeString"),
|
||||
("Value", "@y1"),
|
||||
("Value", "@y2"),
|
||||
]
|
||||
show(p)
|
||||
|
||||
return source
|
||||
|
||||
|
||||
def qphLive(self,attrList, name, timeRange):
|
||||
# attrList=['stationLoadVolt']
|
||||
now=datetime.datetime.now()
|
||||
epoch=datetime.datetime.utcfromtimestamp(0)
|
||||
tstart=now-datetime.timedelta(days=0,hours=timeRange-self.UTCOffset)
|
||||
tcut=(tstart-epoch).total_seconds()
|
||||
dataFrame=self.df[self.df.timestamp>tcut]
|
||||
dataFrame['t']=dataFrame.timestamp*1000 - self.UTCOffset*3600*1000
|
||||
output_server(name,url='http://10.0.1.2:5006')
|
||||
colors=["red","blue","green","orange","purple","black","gray","magenta","cyan","brown","gold","darkkhaki","darksalmon"]
|
||||
timeString=[datetime.datetime.fromtimestamp(dt).strftime('%Y/%m/%d %H:%M:%S') for dt in dataFrame.timestamp]
|
||||
source=ColumnDataSource(data=dataFrame.to_dict('list'))
|
||||
TOOLS="resize,hover,crosshair,pan,wheel_zoom,box_zoom,reset,tap,previewsave,box_select,poly_select,lasso_select"
|
||||
keyList=attrList.keys()
|
||||
p={}
|
||||
ds={}
|
||||
for mainKey in keyList:
|
||||
if mainKey == keyList[0]:
|
||||
p[mainKey]=figure(x_axis_type="datetime",tools=TOOLS, width=600, height=400, title=mainKey)
|
||||
else:
|
||||
p[mainKey]=figure(x_axis_type="datetime",tools=TOOLS, width=600, height=400, title=mainKey, x_range=p[keyList[0]].x_range)
|
||||
ikey=0
|
||||
hover={}
|
||||
for key in attrList[mainKey]:
|
||||
print key
|
||||
# keySource=ColumnDataSource({'x': source.data['t'], 'y': series.values, 'series_name': name_for_display, 'Date': toy_df.index.format()})
|
||||
p[mainKey].scatter('t',key, source=source, name=key,fill_color=colors[ikey],line_color=colors[ikey], legend=key)
|
||||
|
||||
hover = p[mainKey].select(dict(type=HoverTool))
|
||||
# hover[ikey].renderers=[source.data[key]]
|
||||
# hover[ikey].tooltips=tooltips+[("Series",key),("Time","@timeString"), ("Value", "@"+key)]
|
||||
hover.tooltips=[("Series",key),("Time","@timeString"), ("Value", "@"+key)]
|
||||
# hover.mode = "mouse"
|
||||
ikey+=1
|
||||
p[mainKey].legend.orientation="top_left"
|
||||
renderer = p[mainKey].select(dict(name=key))
|
||||
ds[mainKey]=renderer[0].data_source
|
||||
|
||||
|
||||
# allP = vplot(*p.values())
|
||||
# allP = gridplot([p.values()])
|
||||
group=lambda flat, size: [flat[i:i+size] for i in range(0,len(flat), size)]
|
||||
allP = gridplot(group(p.values(),1))
|
||||
show(allP)
|
||||
|
||||
|
||||
while True:
|
||||
print 'updating...'
|
||||
self.db=st.DB('data.sdb','master')
|
||||
self.db.loadDB('master')
|
||||
self.df = self.db.df
|
||||
now=datetime.datetime.now()
|
||||
tstart=now-datetime.timedelta(days=0,hours=timeRange-self.UTCOffset)
|
||||
tcut=(tstart-epoch).total_seconds()
|
||||
dataFrame=self.df[self.df.timestamp>tcut]
|
||||
dataFrame['t']=dataFrame.timestamp*1000 - self.UTCOffset*3600*1000
|
||||
for mainKey in keyList:
|
||||
ds[mainKey].data = dataFrame.to_dict('list')
|
||||
# print ds.data['stationIRTemp']
|
||||
cursession().store_objects(ds[mainKey])
|
||||
time.sleep(30)
|
|
@ -0,0 +1,26 @@
|
|||
import stationDB as st
|
||||
import numpy as np
|
||||
import sqlite3
|
||||
import pandas as pd
|
||||
from bokeh.plotting import *
|
||||
from bokeh.models import HoverTool
|
||||
|
||||
import time
|
||||
import datetime
|
||||
import pytz
|
||||
|
||||
db=st.DB('data.sdb','master')
|
||||
db.loadDB('master')
|
||||
UTCOffset = -5
|
||||
dbv=st.DBview(db, db.df, UTCOffset)
|
||||
|
||||
attrList={}
|
||||
attrList['Station Temperature']=['WeatherStationTemperature','WeatherStationHTUTemp','WeatherStationDewpoint']
|
||||
attrList['Station IR']=['WeatherStationIRTemp','WeatherStationMLXTemp']
|
||||
attrList['Brightness']=['WeatherStationBrightness']
|
||||
attrList['Voltage']=['WeatherStationLoad']
|
||||
attrList['Current']=['WeatherStationCurrent']
|
||||
attrList['UV']=['WeatherStationUVIndex']
|
||||
|
||||
viewHistory=24
|
||||
dbv.qphLive(attrList, 'DayView', viewHistory)
|
|
@ -0,0 +1,133 @@
|
|||
from datetime import datetime as dt
|
||||
import sys
|
||||
import os
|
||||
from socketIO_client import SocketIO, LoggingNamespace
|
||||
from subprocess import check_output
|
||||
import socket
|
||||
import time
|
||||
import json
|
||||
import stationDB
|
||||
import datetime
|
||||
from crypt import *
|
||||
import numpy as np
|
||||
|
||||
|
||||
class Station:
|
||||
def __init__(self):
|
||||
self.name='Rpi'
|
||||
self.udpPort=8123
|
||||
self.BUF_SIZE=1024
|
||||
self.packets={}
|
||||
self.packetMean={}
|
||||
self.nMeasurements={}
|
||||
self.lastUpdate={}
|
||||
self.avgFreq=30
|
||||
self.lastRelease=datetime.datetime.now()
|
||||
# self.secret_key = '1234567890123456'
|
||||
|
||||
|
||||
|
||||
|
||||
def initSocket(self, host, port ):
|
||||
try:
|
||||
self.socketIO = SocketIO(host, port)
|
||||
self.socketConnected=True
|
||||
except:
|
||||
print "Unable to open socket: ", sys.exc_info()[0]
|
||||
self.socketConnected=False
|
||||
raise
|
||||
|
||||
def startUDPListen(self,ip,port):
|
||||
self.sock = socket.socket(socket.AF_INET, # Internet
|
||||
socket.SOCK_DGRAM) # UDP
|
||||
self.sock.bind((ip, port))
|
||||
|
||||
def startUDPSend(self):
|
||||
self.sock = socket.socket(socket.AF_INET, # Internet
|
||||
socket.SOCK_DGRAM) # UDP
|
||||
|
||||
|
||||
def sendPacket(self, message, ip, port):
|
||||
self.sock.sendto(message, (ip, port))
|
||||
|
||||
def recvPacket(self, verbose):
|
||||
data, addr = self.sock.recvfrom(self.BUF_SIZE) # buffer size is 1024 bytes
|
||||
# decoded = decodePacket(self.secret_key,data)
|
||||
try:
|
||||
packet=json.loads(data)
|
||||
except ValueError, e:
|
||||
print 'bad json data'
|
||||
print data
|
||||
return {}
|
||||
|
||||
if verbose:
|
||||
print "Message from ", addr, " :", packet
|
||||
return packet
|
||||
|
||||
def checkForPacketRelease(self):
|
||||
releasePacket=False
|
||||
now=datetime.datetime.now()
|
||||
releaseTime=0
|
||||
if (now-self.lastRelease).seconds > self.avgFreq:
|
||||
self.packetMean['timestamp']=time.time()
|
||||
|
||||
releasePacket=True
|
||||
releaseTime=float(now.strftime("%s"))
|
||||
self.lastRelease=now
|
||||
timeString=now.strftime('%Y/%m/%d %H:%M:%S')
|
||||
self.packetMean['timeString']=timeString
|
||||
self.nMeasurements['timeString']=1
|
||||
# reset packet list
|
||||
for key in self.packets.keys():
|
||||
self.packets[key]=[]
|
||||
|
||||
|
||||
return (releasePacket,releaseTime)
|
||||
|
||||
|
||||
def updateMeasurement(self, key, value):
|
||||
|
||||
if key not in self.lastUpdate.keys():
|
||||
self.lastUpdate[key]=datetime.datetime.now()
|
||||
|
||||
self.lastUpdate[key]=datetime.datetime.now()
|
||||
if key not in self.packets.keys():
|
||||
self.packets[key]=value
|
||||
self.packets[key]=np.append(self.packets[key],value)
|
||||
self.packetMean[key] = np.mean(self.packets[key])
|
||||
self.nMeasurements[key] = len(self.packets[key])
|
||||
|
||||
|
||||
#
|
||||
db=stationDB.DB('data.sdb','master')
|
||||
s=Station()
|
||||
s.udpPort=9990
|
||||
s.startUDPListen('0.0.0.0',s.udpPort)
|
||||
|
||||
sio=SocketIO('localhost', 5000)
|
||||
verbose=False
|
||||
readPackets=True
|
||||
while readPackets:
|
||||
rawPacket=s.recvPacket(verbose)
|
||||
print rawPacket
|
||||
try:
|
||||
sio.emit('dataPacket', rawPacket)
|
||||
print 'sent packet'
|
||||
except:
|
||||
print 'socket not connected'
|
||||
|
||||
# rawPacket['name']
|
||||
|
||||
for dataName in rawPacket['data'].keys():
|
||||
key = rawPacket['name'].replace(' ','') + dataName.replace(' ','')
|
||||
value = rawPacket['data'][dataName]['value']
|
||||
releasePacket=s.updateMeasurement(key,value)
|
||||
releasePacket,releaseTime=s.checkForPacketRelease()
|
||||
if releasePacket:
|
||||
for (key,value) in s.packetMean.items():
|
||||
print 'Updating db: ',key,' = ',value
|
||||
db.addData(s.packetMean)
|
||||
s.packetMean={}
|
||||
|
||||
|
||||
s.sock.close()
|
|
@ -0,0 +1,153 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Home Conditions</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=0.6">
|
||||
<script type="text/javascript" src="//code.jquery.com/jquery-1.9.1.min.js"></script>
|
||||
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/socket.io/1.4.0/socket.io.js"></script>
|
||||
<LINK href="https://bootswatch.com/slate/bootstrap.min.css" rel="stylesheet" type="text/css">
|
||||
<script src="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
|
||||
<script src="{{ url_for('static', filename='js/lib/jquery.bsAlerts.min.js')}}"></script>
|
||||
|
||||
<script type="text/javascript" charset="utf-8">
|
||||
$(document).ready(function(){
|
||||
namespace = '/web'; // change to an empty string to use the global namespace
|
||||
|
||||
// the socket.io documentation recommends sending an explicit package upon connection
|
||||
// this is specially important when using the global namespace
|
||||
var socket = io.connect('http://' + document.domain + ':' + location.port + namespace);
|
||||
$("#success-alert").hide();
|
||||
// event handler for server sent data
|
||||
// the data is displayed in the "Received" section of the page
|
||||
socket.on('dataPacket', function(msg) {
|
||||
console.log(msg.packet);
|
||||
var parsedData= msg.packet;//JSON.parse(msg.packet);
|
||||
console.log('from: '+parsedData.name);
|
||||
var name = parsedData.name.replace(/ /g,'')
|
||||
var text='';
|
||||
|
||||
var options = new Array();
|
||||
$.each(parsedData.data, function(index, option) {
|
||||
if ((index != 'uptime') && (index != 'RSSsI')){
|
||||
options.push("<tr class='"+option.class+"'> <td class='col-md-4'><h6>" + index + "</h6></td> <td class='col-xs-8'> <h4>"+ option.value + " " + option.unit + "</h4></td></tr>");
|
||||
if (option.class == 'danger' || option.class == 'info'){
|
||||
console.log('options: ' + options);
|
||||
// console.log('test');
|
||||
$(document).trigger("add-alerts", [
|
||||
{
|
||||
'message': name + " " + index + " is outside of bounds: "+ option.value,
|
||||
'priority': option.class
|
||||
}
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
// get total seconds between the times
|
||||
var delta = parsedData['data']['uptime']['value'];
|
||||
console.log(parsedData)
|
||||
|
||||
var days = Math.floor(delta / 86400);
|
||||
delta -= days * 86400;
|
||||
var hours = Math.floor(delta / 3600) % 24;
|
||||
delta -= hours * 3600;
|
||||
var minutes = Math.floor(delta / 60) % 60;
|
||||
delta -= minutes * 60;
|
||||
var seconds = Math.round(delta % 60,1);
|
||||
|
||||
|
||||
|
||||
uptimeString =
|
||||
$('#'+name).html("<table class='table table-striped table-hover'><thead><tr><th>" + parsedData.name + " <br> uptime: " + days + "d " + hours + "h " + minutes + "m " + seconds + "s " +" </th><th>Value</th><th></th></tr></thead><tbody>" + options.join('')+ "</body></table>");
|
||||
|
||||
var nameIP = parsedData.name.replace(/ /g,'')+'IP'
|
||||
var ipAddr = parsedData.network.ip
|
||||
$('a[href="' + name + '"]').attr('href','http://'+ipAddr)
|
||||
|
||||
|
||||
});
|
||||
|
||||
// event handler for new connections
|
||||
socket.on('connect', function() {
|
||||
socket.emit('my event', {data: 'I\'m connected!'});
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<nav class="navbar navbar-default">
|
||||
<div class="container-fluid">
|
||||
<!-- Brand and toggle get grouped for better mobile display -->
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<!-- <a class="navbar-brand" href="#">Room Conditions</a> -->
|
||||
</div>
|
||||
<!-- Collect the nav links, forms, and other content for toggling -->
|
||||
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
|
||||
<ul class="nav navbar-nav ">
|
||||
<li><a href="#" target="_blank">Ephemeris</a></li>
|
||||
<li><a href="http://10.0.1.62:5006" target="_blank">Bokeh Plots</a></li>
|
||||
<li><a href="http://www.ssec.wisc.edu/data/wisc/" target="_blank">Satellite</a></li>
|
||||
<li class="dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"> Sensor pages <span class="caret"></span></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="DevelopmentServer" target="_blank">Dev</a></li>
|
||||
<li><a href="WeatherStation" target="_blank">Weather Station</a></li>
|
||||
<li role="separator" class="divider"></li>
|
||||
<li><a href="Garage" target="_blank">Garage</a></li>
|
||||
<li><a href="Office" target="_blank">Office</a></li>
|
||||
<li><a href="ServerRoom" target="_blank">Server Room</a></li>
|
||||
<li><a href="Shop" target="_blank">Shop</a></li>
|
||||
<li><a href="FamilyRoom" target="_blank">Family Room</a></li>
|
||||
<li><a href="RecRoom" target="_blank">Rec Room</a></li>
|
||||
<li><a href="MasterBedroom" target="_blank">Master Bed</a></li>
|
||||
<li><a href="LivingRoom" target="_blank">Living Room</a></li>
|
||||
<li><a href="Kitchen" target="_blank">Kitchen</a></li>
|
||||
<li><a href="GuestRoom" target="_blank">Guest Room</a></li>
|
||||
<li><a href="Loft" target="_blank">Loft</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</div><!-- /.navbar-collapse -->
|
||||
</div><!-- /.container-fluid -->
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<body>
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="col-xs-12">
|
||||
|
||||
<div class="page-header"><h1>Room Conditions<h1></div>
|
||||
<table class="table">
|
||||
<tr>
|
||||
<td class='col-xs-3'><div id="WeatherStation"></div></td>
|
||||
<td class='col-xs-3'><div id="Office"></div><div id="ServerRoom"></div><div id="RecRoom"></div></td>
|
||||
<td class='col-xs-3'><div id="Garage"></div><div id="GuestRoom"></div></td>
|
||||
<td class='col-xs-3'><div id="MasterBedroom"></div><div id="Loft"></div></td>
|
||||
<!-- <td class='col-xs-3'><div id="DevelopmentServer"></div><div id="WifiMonitor"></div></td> -->
|
||||
<!-- <td class='col-xs-4'><h3>Alerts:</h3><div data-alerts="alerts" data-fade="500"></div></td> -->
|
||||
</tr>
|
||||
<tr>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class='col-xs-3'><div id="Shop"></div></td>
|
||||
<td class='col-xs-3'><div id="FamilyRoom"></div></td>
|
||||
<td class='col-xs-3'><div id="LivingRoom"></div></td>
|
||||
<td class='col-xs-3'><div id="Kitchen"></div></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,132 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Home Conditions</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=0.6">
|
||||
<script type="text/javascript" src="//code.jquery.com/jquery-1.9.1.min.js"></script>
|
||||
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/socket.io/1.4.0/socket.io.js"></script>
|
||||
<LINK href="https://bootswatch.com/slate/bootstrap.min.css" rel="stylesheet" type="text/css">
|
||||
<script src="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
|
||||
<!-- <script src="{{ url_for('static', filename='js/lib/jquery.bsAlerts.min.js')}}"></script> -->
|
||||
|
||||
<script type="text/javascript" charset="utf-8">
|
||||
$(document).ready(function(){
|
||||
namespace = '/web'; // change to an empty string to use the global namespace
|
||||
|
||||
// the socket.io documentation recommends sending an explicit package upon connection
|
||||
// this is specially important when using the global namespace
|
||||
var socket = io.connect('http://' + document.domain + ':' + location.port + namespace);
|
||||
$("#success-alert").hide();
|
||||
// event handler for server sent data
|
||||
// the data is displayed in the "Received" section of the page
|
||||
socket.on('dataPacket', function(msg) {
|
||||
console.log(msg.packet);
|
||||
var parsedData= msg.packet;//JSON.parse(msg.packet);
|
||||
console.log('from: '+parsedData.name);
|
||||
var name = parsedData.name.replace(/ /g,'')
|
||||
var text='';
|
||||
|
||||
var options = new Array();
|
||||
$.each(parsedData.data, function(index, option) {
|
||||
if ((index != 'uptime') && (index != 'RSSsI')){
|
||||
options.push("<tr class='"+option.class+"'> <td class='col-md-4'><h6>" + index + "</h6></td> <td class='col-xs-8'> <h4>"+ option.value + " " + option.unit + "</h4></td></tr>");
|
||||
if (option.class == 'danger' || option.class == 'info'){
|
||||
console.log('options: ' + options);
|
||||
// console.log('test');
|
||||
$(document).trigger("add-alerts", [
|
||||
{
|
||||
'message': name + " " + index + " is outside of bounds: "+ option.value,
|
||||
'priority': option.class
|
||||
}
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
// get total seconds between the times
|
||||
var delta = parsedData['data']['uptime']['value'];
|
||||
console.log(parsedData)
|
||||
|
||||
var days = Math.floor(delta / 86400);
|
||||
delta -= days * 86400;
|
||||
var hours = Math.floor(delta / 3600) % 24;
|
||||
delta -= hours * 3600;
|
||||
var minutes = Math.floor(delta / 60) % 60;
|
||||
delta -= minutes * 60;
|
||||
var seconds = Math.round(delta % 60,1);
|
||||
|
||||
|
||||
|
||||
uptimeString =
|
||||
$('#'+name).html("<table class='table table-striped table-hover'><thead><tr><th>" + parsedData.name + " <br> uptime: " + days + "d " + hours + "h " + minutes + "m " + seconds + "s " +" </th><th>Value</th><th></th></tr></thead><tbody>" + options.join('')+ "</body></table>");
|
||||
|
||||
var nameIP = parsedData.name.replace(/ /g,'')+'IP'
|
||||
var ipAddr = parsedData.network.ip
|
||||
$('a[href="' + name + '"]').attr('href','http://'+ipAddr)
|
||||
|
||||
|
||||
});
|
||||
|
||||
// event handler for new connections
|
||||
socket.on('connect', function() {
|
||||
socket.emit('my event', {data: 'I\'m connected!'});
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<nav class="navbar navbar-default">
|
||||
<div class="container-fluid">
|
||||
<!-- Brand and toggle get grouped for better mobile display -->
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<!-- <a class="navbar-brand" href="#">Room Conditions</a> -->
|
||||
</div>
|
||||
<!-- Collect the nav links, forms, and other content for toggling -->
|
||||
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
|
||||
<ul class="nav navbar-nav ">
|
||||
<li><a href="#" target="_blank">Ephemeris</a></li>
|
||||
<li><a href="http://10.0.1.62:5006" target="_blank">Bokeh Plots</a></li>
|
||||
<li><a href="http://www.ssec.wisc.edu/data/wisc/" target="_blank">Satellite</a></li>
|
||||
<li class="dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"> Sensor pages <span class="caret"></span></a>
|
||||
<ul class="dropdown-menu">
|
||||
<!-- For every station sending data with name "Station Name", add following:
|
||||
<li><a href="StationName" target="_blank">Station Name</a></li>
|
||||
-->
|
||||
<li><a href="WeatherStation" target="_blank">Weather Station</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</div><!-- /.navbar-collapse -->
|
||||
</div><!-- /.container-fluid -->
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<body>
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="col-xs-12">
|
||||
|
||||
<div class="page-header"><h1>Conditions<h1></div>
|
||||
<table class="table">
|
||||
<tr>
|
||||
<!-- For every station sending data with name "Station Name", add following:
|
||||
<td class='col-xs-3'><div id="StationName"></div></td> -->
|
||||
<td class='col-xs-3'><div id="WeatherStation"></div></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,239 @@
|
|||
#if ARDUINO >= 100
|
||||
#include "Arduino.h"
|
||||
#else
|
||||
#include "WProgram.h"
|
||||
#endif
|
||||
|
||||
#ifdef __AVR_ATtiny85__
|
||||
#include "TinyWireM.h"
|
||||
#define Wire TinyWireM
|
||||
#else
|
||||
#include <Wire.h>
|
||||
#endif
|
||||
|
||||
#include "ESP_httplib.h"
|
||||
#include "ESP8266WiFi.h"
|
||||
|
||||
/**************************************************************************/
|
||||
// Instantiates new ESP class
|
||||
/**************************************************************************/
|
||||
ESP_HTTP::ESP_HTTP(){
|
||||
}
|
||||
|
||||
/**************************************************************************/
|
||||
// Instantiates new ESP class
|
||||
/**************************************************************************/
|
||||
boolean ESP_HTTP::begin(void){
|
||||
header="<!DOCTYPE html><html><head><title>" + stationName +"</title>";
|
||||
// refresh HTML page
|
||||
header +="<meta http-equiv=\"refresh\" content=\"30\">";
|
||||
header +="</head>";
|
||||
|
||||
css="";
|
||||
css+="<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">";
|
||||
css+="<link rel=\"stylesheet\" href=\"http://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css\">";
|
||||
css+="<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/1.12.0/jquery.min.js\"></script>";
|
||||
css+="<script src=\"http://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js\"></script>";
|
||||
css+="<style>";
|
||||
css+="footer {";
|
||||
css+="color: brown;";
|
||||
css+="font-style: oblique;";
|
||||
css+="}";
|
||||
css+="</style>";
|
||||
css+="<LINK href=\"https://bootswatch.com/slate/bootstrap.min.css\" rel=\"stylesheet\" type=\"text/css\">";
|
||||
body="<header>";
|
||||
endHTML="</header></body></html>";
|
||||
return true;
|
||||
}
|
||||
|
||||
String ESP_HTTP::getStatus(Data data){
|
||||
|
||||
// Apply color coding status using data limits
|
||||
String dataclass;
|
||||
if (data.value > data.ul2){
|
||||
if (data.invertLimits){
|
||||
dataclass = "info";
|
||||
} else {
|
||||
dataclass = "danger";
|
||||
}
|
||||
} else if (data.value > data.ul1){
|
||||
if (data.invertLimits){
|
||||
dataclass = "success";
|
||||
} else {
|
||||
dataclass = "warning";
|
||||
}
|
||||
} else if (data.value < data.ll2){
|
||||
if (data.invertLimits){
|
||||
dataclass = "danger";
|
||||
} else {
|
||||
dataclass = "info";
|
||||
}
|
||||
} else if (data.value < data.ll1){
|
||||
if (data.invertLimits){
|
||||
dataclass = "warning";
|
||||
} else {
|
||||
dataclass = "success";
|
||||
}
|
||||
} else {
|
||||
dataclass = "success";
|
||||
}
|
||||
|
||||
return dataclass;
|
||||
}
|
||||
|
||||
boolean ESP_HTTP::updatePage(DataSet dataset, String packet){
|
||||
|
||||
|
||||
content = "<div class=\"container\">";
|
||||
content += "<div class=\"page-header\">";
|
||||
content += "<h1>" + stationName + "<h1>";
|
||||
content += "</div>";
|
||||
|
||||
dataContent="<table class=\"table table-striped table-hover\"><thead><tr><th>Measurement</th><th>Value</th><th></th></tr></thead><tbody>";
|
||||
for (int i=0; i < dataset.nData; i++){
|
||||
if (dataset.data[i].name == "uptime"){
|
||||
uptime = dataset.data[i].value;
|
||||
continue;
|
||||
}
|
||||
String dataclass=getStatus(dataset.data[i]);
|
||||
|
||||
if (i==0) {
|
||||
dataContent += "<tr data-toggle='collapse' data-target='.collapseTest' class=\"" + dataclass + "\">" + " <td class=\"col-md-6\">" + dataset.data[i].name + "</td><td class=\"col-md-5\">" + String(dataset.data[i].value) + " " + dataset.data[i].unit + "</td><td class=\"col-md-1\"><button data-toggle='collapse' data-target='.collapseTest' class='btn btn-default btn-xs'><span class='glyphicon glyphicon-menu-up'></span></button></td></tr>";
|
||||
} else {
|
||||
dataContent += "<tr class=\"" + dataclass + " collapse in collapseTest\">" + " <td class=\"col-md-6\">" + dataset.data[i].name + "</td><td class=\"col-md-5\">" + String(dataset.data[i].value) + " " + dataset.data[i].unit + "</td><td class='col-md-1'></td></tr>";
|
||||
}
|
||||
|
||||
}
|
||||
dataContent += "</tbody></table>";
|
||||
|
||||
dataContent += " <div class=\"dropdown\"> \
|
||||
<button class=\"btn btn-primary dropdown-toggle\" type=\"button\" data-toggle=\"dropdown\">Dewpoint Calculation \
|
||||
<span class=\"caret\"></span></button> \
|
||||
<ul class=\"dropdown-menu\"> \
|
||||
<li><a href=\"post?dpTemp=mcp\">High Res Temperature</a></li> \
|
||||
<li><a href=\"post?dpTemp=htu\">Humidity Temperature</a></li> \
|
||||
</ul> \
|
||||
</div>";
|
||||
|
||||
dataContent += "<form action=\"post\" method=\"post\" >";
|
||||
|
||||
dataContent += "<div class=\"form-group row\">";
|
||||
dataContent += "<label class=\"col-sm-4\" for=\"udpFRQ\">UDP Packet Frequency: </label>";
|
||||
dataContent += "<div class=\"col-sm-4\"><input type=\"text\" name=\"udpFRQ\"; value=\""+String(settings.udpFRQ)+"\"class=\"btn-link\"></div>";
|
||||
dataContent += "</div>";
|
||||
|
||||
dataContent += "<div class=\"form-group row\">";
|
||||
dataContent += "<label class=\"col-sm-4\" for=\"udpIP\">UDP IP: </label>";
|
||||
dataContent += "<div class=\"col-sm-4\"><input type=\"text\" name=\"udpIP\"; value=\""+String(settings.udpIP)+"\"class=\"btn-link\"></div>";
|
||||
dataContent += "</div>";
|
||||
|
||||
dataContent += "<div class=\"form-group row\">";
|
||||
dataContent += "<label class=\"col-sm-4\" for=\"udpPort\">UDP Port: </label>";
|
||||
dataContent += "<div class=\"col-sm-4\"><input type=\"text\" name=\"udpPort\"; value=\""+String(settings.udpPort)+"\"class=\"btn-link\"></div>";
|
||||
dataContent += "</div>";
|
||||
|
||||
dataContent += "<input type=\"submit\" class=\"btn btn-info col-sm-4\" value=\"Submit\">";
|
||||
dataContent += "</form><br>";
|
||||
|
||||
|
||||
buttons = "<button type=\"button\" class=\"btn btn-info btn-block\" data-toggle=\"modal\" data-target=\"#myModal\">JSON Data</button><!-- Modal --><div class=\"modal fade\" id=\"myModal\" role=\"dialog\"><div class=\"modal-dialog\"><!-- Modal content--><div class=\"modal-content\"><div class=\"modal-header\"><button type=\"button\" class=\"close\" data-dismiss=\"modal\">×</button><h4 class=\"modal-title\">JSON Data</h4></div><div class=\"modal-body\"><p>"+ packet +"</p></div><div class=\"modal-footer\"><button type=\"button\" class=\"btn btn-default\" data-dismiss=\"modal\">Close</button></div></div></div></div>";
|
||||
|
||||
|
||||
buttons += "<form method='POST' action='/update' enctype='multipart/form-data'>\
|
||||
<input type='file' name='update' class='btn btn-info col-xs-8'>\
|
||||
<input type='submit' value='Update Firmware' class='btn btn-danger col-xs-4'> \
|
||||
</form>";
|
||||
|
||||
buttons += "<br><form action='ledOff' method='GET'><button class='btn-danger col-xs-4' name=\"led\" value=\"off\">LED Off</button></form>";
|
||||
buttons += "<form action='ledOn' method='GET'><button class='btn-success col-xs-4' name=\"led\" value=\"off\">LED On</button></form>";
|
||||
|
||||
buttons += "<button type=\"button\" class=\"btn btn-danger btn-block\"><a href=\"reset\">Reset Device</a></button>";
|
||||
|
||||
footer="<br<br><div class=\"container\"><div class=\"panel-footer\" class=\"container-fluid\"><div class=\"row\"></div>Hello from " + stationName + "<br>Station sensor: Version " + String(version) + " <br> Uptime: " + String(uptime) + " seconds </div></div></div>";
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
String ESP_HTTP::page(void){
|
||||
String htmlpage = header + css + body + content + dataContent + buttons + jsonButton + endHTML + footer;
|
||||
return htmlpage;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**************************************************************************/
|
||||
// Instantiates new ESP class
|
||||
/**************************************************************************/
|
||||
ESP_httplib::ESP_httplib(){
|
||||
}
|
||||
|
||||
/**************************************************************************/
|
||||
// Instantiates new ESP class
|
||||
/**************************************************************************/
|
||||
boolean ESP_httplib::begin(const char* ssid, const char* password) {
|
||||
|
||||
pinMode(RESET_PIN, OUTPUT);
|
||||
digitalWrite(RESET_PIN, HIGH);
|
||||
pinMode(NOTIFY_PIN, OUTPUT);
|
||||
digitalWrite(NOTIFY_PIN, HIGH);
|
||||
|
||||
http.begin();
|
||||
// // Connect to WiFi
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(ssid, password);
|
||||
Serial.begin(115200);
|
||||
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
delay(500);
|
||||
Serial.print(".");
|
||||
}
|
||||
Serial.println("");
|
||||
Serial.println("WiFi connected");
|
||||
|
||||
// Print the IP address
|
||||
stationIP=WiFi.localIP().toString();
|
||||
Serial.println(stationIP);
|
||||
|
||||
// tStart = millis();
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// float uptime(void){
|
||||
// return 0.001 * ( millis() - tStart);
|
||||
// }
|
||||
|
||||
void ESP_httplib::triggerActivityLED(void){
|
||||
digitalWrite(NOTIFY_PIN, LOW); // turn the LED off by making the voltage LOW
|
||||
delay(10);
|
||||
digitalWrite(NOTIFY_PIN, HIGH); // turn the LED off by making the voltage LOW
|
||||
|
||||
}
|
||||
|
||||
void ESP_httplib::triggerReset(){
|
||||
digitalWrite(RESET_PIN, LOW); // turn the LED off by making the voltage LOW
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ESP_httplib::formPacket(DataSet dataset){
|
||||
|
||||
String dataPacket = "";
|
||||
for (int i=0; i < dataset.nData; i++){
|
||||
dataPacket += "\"" + dataset.data[i].name + "\": { \"value\": " + String(dataset.data[i].value,3) + ", \"class\": \"" + http.getStatus(dataset.data[i]) + "\", \"unit\": \"" + dataset.data[i].unit + "\"}";
|
||||
if (i != dataset.nData - 1){
|
||||
dataPacket += ", ";
|
||||
}
|
||||
}
|
||||
|
||||
String name = "\"name\": \"" + http.stationName + "\"";
|
||||
String address = "\"ip\": \"" + stationIP + "\"";
|
||||
|
||||
String rssiPacket = "\"rssi\": " + String(WiFi.RSSI());
|
||||
String network = "\"network\": { " + address + ", " + rssiPacket + "}";
|
||||
|
||||
packet = "{ " + name + ", " + network + ", \"data\" : {" + dataPacket + "} }";
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
#if ARDUINO >= 100
|
||||
#include "Arduino.h"
|
||||
#else
|
||||
#include "WProgram.h"
|
||||
#endif
|
||||
|
||||
#ifdef __AVR_ATtiny85__
|
||||
#include "TinyWireM.h"
|
||||
#define Wire TinyWireM
|
||||
#else
|
||||
#include <Wire.h>
|
||||
#endif
|
||||
|
||||
#include "ESP8266WiFi.h"
|
||||
|
||||
#define NOTIFY_PIN 16
|
||||
#define RESET_PIN 5
|
||||
|
||||
typedef struct {
|
||||
String name;
|
||||
float value;
|
||||
String unit;
|
||||
float ul1, ul2, ll1, ll2;
|
||||
boolean invertLimits;
|
||||
} Data;
|
||||
|
||||
typedef struct{
|
||||
String udpIP;
|
||||
int udpPort;
|
||||
float udpFRQ;
|
||||
} Settings;
|
||||
|
||||
typedef struct {
|
||||
int maxData=20;
|
||||
int nData;
|
||||
Data data[20];
|
||||
|
||||
|
||||
} DataSet;
|
||||
|
||||
|
||||
|
||||
class ESP_HTTP {
|
||||
public:
|
||||
ESP_HTTP();
|
||||
boolean begin(void);
|
||||
boolean updatePage(DataSet dataset, String packet);
|
||||
Settings settings;
|
||||
String header;
|
||||
String stationName;
|
||||
String css;
|
||||
String body;
|
||||
String content;
|
||||
String dataContent;
|
||||
String buttons, jsonButton;
|
||||
String endHTML;
|
||||
String footer;
|
||||
String version;
|
||||
String getStatus(Data data);
|
||||
String page(void);
|
||||
float uptime;
|
||||
};
|
||||
|
||||
class ESP_httplib {
|
||||
public:
|
||||
ESP_httplib();
|
||||
ESP_HTTP http;
|
||||
boolean begin(const char* ssid, const char* password);
|
||||
void triggerActivityLED(void);
|
||||
void triggerReset(void);
|
||||
String stationIP;
|
||||
String packet;
|
||||
void formPacket(DataSet dataset);
|
||||
void sendUDPPacket(void);
|
||||
private:
|
||||
unsigned long int tStart;
|
||||
|
||||
|
||||
};
|
Plik diff jest za duży
Load Diff
|
@ -0,0 +1,29 @@
|
|||
#
|
||||
# Project Configuration File
|
||||
#
|
||||
# A detailed documentation with the EXAMPLES is located here:
|
||||
# http://docs.platformio.org/en/latest/projectconf.html
|
||||
#
|
||||
|
||||
# A sign `#` at the beginning of the line indicates a comment
|
||||
# Comment lines are ignored.
|
||||
|
||||
# Simple and base environment
|
||||
# [env:mybaseenv]
|
||||
# platform = %INSTALLED_PLATFORM_NAME_HERE%
|
||||
# framework =
|
||||
# board =
|
||||
#
|
||||
# Automatic targets - enable auto-uploading
|
||||
# targets = upload
|
||||
|
||||
[env:dev]
|
||||
platform = espressif
|
||||
framework = arduino
|
||||
board = nodemcu
|
||||
; upload_port = IPADDRESS
|
||||
; upload_flags = --auth=PASSWORD
|
||||
targets = upload
|
||||
upload_speed = 921600
|
||||
build_flags = -DNAME="Weather Station" -DMCP -DHTU -DINA -DSI -DMLX -DTSL
|
||||
src_filter = "+<ESP_station.cpp>"
|
|
@ -0,0 +1,81 @@
|
|||
#include "weatherCalcs.h"
|
||||
//#include <math.h>
|
||||
|
||||
float heatIndex(float T, float R){
|
||||
float c1,c2,c3,c4,c5,c6,c7,c8,c9;
|
||||
|
||||
|
||||
float HI = 0.5 * (T + 61.0 + ((T-68.0)*1.2) + (R*0.094));
|
||||
|
||||
if (HI > 80.0){
|
||||
c1 = -42.379;
|
||||
c2=2.04901523;
|
||||
c3=10.144333127;
|
||||
c4=-0.22475541;
|
||||
c5=-6.83783e-3;
|
||||
c6=-5.481717e-2;
|
||||
c7=1.22874e-3;
|
||||
c8=8.5282e-4;
|
||||
c9=-1.99e-6;
|
||||
float T2 = T*T;
|
||||
float R2 = R*R;
|
||||
HI = c1 + c2 * T + c3*R + c4 * T * R + c5 * T2 +
|
||||
c6 * R2 + c7 * T2 * R + c8 * T * R2 + c9 * T2 * R2;
|
||||
}
|
||||
|
||||
|
||||
return HI;
|
||||
}
|
||||
|
||||
|
||||
float dewPoint(float farenheight, float humidity)
|
||||
{
|
||||
// (1) Saturation Vapor Pressure = ESGG(T)
|
||||
float celsius = (farenheight - 32.0)/1.8;
|
||||
float RATIO = 373.15 / (273.15 + celsius);
|
||||
float RHS = -7.90298 * (RATIO - 1);
|
||||
RHS += 5.02808 * log10(RATIO);
|
||||
RHS += -1.3816e-7 * (pow(10, (11.344 * (1 - 1/RATIO ))) - 1) ;
|
||||
RHS += 8.1328e-3 * (pow(10, (-3.49149 * (RATIO - 1))) - 1) ;
|
||||
RHS += log10(1013.246);
|
||||
|
||||
// factor -3 is to adjust units - Vapor Pressure SVP * humidity
|
||||
float VP = pow(10, RHS - 3) * humidity;
|
||||
|
||||
// (2) DEWPOINT = F(Vapor Pressure)
|
||||
float T = log(VP/0.61078); // temp var
|
||||
return (241.88 * T) / (17.558 - T) * 1.8 + 32.0;
|
||||
}
|
||||
|
||||
float meanSeaLevelPressure(float P, float T, float h){
|
||||
float TK = (T - 32.)* 5./9. + 273.15;
|
||||
float r1 = 1. - 0.0065 * h / ( TK + 0.0065 * h);
|
||||
float r2 = P * pow(r1,-5.257); // P inHG to hPa
|
||||
|
||||
return r2;
|
||||
}
|
||||
|
||||
|
||||
|
||||
const int Ndirections=8;
|
||||
float voltFracByDir[Ndirections]={
|
||||
0.77, 0.46, 0.09, 0.18, 0.29, 0.62, 0.92, 0.87};
|
||||
float windDirArray[Ndirections]={
|
||||
0, 45, 90, 135, 180, 225, 270, 315};
|
||||
|
||||
|
||||
|
||||
float windDirByVoltageFraction( float inputVoltageFraction)
|
||||
{
|
||||
float Vdiffmin=1.0;
|
||||
float windDir;
|
||||
for (int i=0; i<Ndirections; i++){
|
||||
float Vdiff = fabs(inputVoltageFraction - voltFracByDir[i]);
|
||||
if (Vdiffmin > Vdiff){
|
||||
Vdiffmin = Vdiff;
|
||||
windDir = windDirArray[i];
|
||||
}
|
||||
}
|
||||
return windDir;
|
||||
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
#include <math.h>
|
||||
|
||||
float heatIndex(float T, float R);
|
||||
float dewPoint(float farenheight, float humidity);
|
||||
float meanSeaLevelPressure(float P, float T, float h);
|
Plik binarny nie jest wyświetlany.
Po Szerokość: | Wysokość: | Rozmiar: 102 KiB |
Plik binarny nie jest wyświetlany.
Po Szerokość: | Wysokość: | Rozmiar: 114 KiB |
Ładowanie…
Reference in New Issue