Skip to content

Commit ef0cfa7

Browse files
committed
adding initial attempts at a websocket web interface
1 parent 060f51b commit ef0cfa7

File tree

5 files changed

+1162
-0
lines changed

5 files changed

+1162
-0
lines changed

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,12 @@ WORKS - v4.py also has data lookback when the websocket is created.
2020

2121
WORKS - v5.py adds additional input checking and has support for multiple data inputs.
2222

23+
## V4a
24+
25+
WORKS - v4a.py has some minor refactoring and tweaks for working with 555.html
26+
27+
**THIS** was the point where everything was version controlled in its own repository!
28+
2329
----
2430

2531
## To Do

py/v4a.py

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
#!/usr/bin/env python
2+
3+
import asyncio
4+
import serial.aio
5+
import websockets
6+
import random
7+
import time
8+
import functools
9+
10+
wsdelay = 0.5
11+
MSGMAX = 100000
12+
SERPATH = '/dev/arduino-uno'
13+
BAUDRATE = 9600
14+
WSPORT = 8801
15+
16+
class serPort(asyncio.Protocol):
17+
msgpart = ""
18+
msgs = []
19+
def connection_made(self, transport):
20+
self.transport = transport
21+
transport.serial.flush()
22+
print('port opened', transport)
23+
transport.serial.rts = False
24+
def data_received(self, data):
25+
datastr = data.decode('utf-8')
26+
#print("msg is currently: {}".format(self.msg))
27+
self.msgpart += datastr
28+
if '\n' in self.msgpart:
29+
for m in self.msgpart.split('\n'):
30+
mparse = m.strip().split(',')
31+
if (len(mparse) == 3 and mparse[-1] == ''):
32+
self.msgs.append(m)
33+
print("{!s}".format(self.msgs[-1]))
34+
self.msgpart = ""
35+
if len(self.msgs) > MSGMAX:
36+
self.msgs.pop(0)
37+
#self.transport.close()
38+
def connection_lost(self, exc):
39+
print('port closed')
40+
asyncio.get_event_loop().stop()
41+
42+
async def listener(websocket, path):
43+
name = await websocket.recv()
44+
return name
45+
46+
async def consumer(websocket, path, message):
47+
print("< {}".format(message))
48+
greeting = "Hello {}!".format(message)
49+
await websocket.send(greeting)
50+
print("> {}".format(greeting))
51+
52+
async def producer(websocket, ser):
53+
while True:
54+
await asyncio.sleep(wsdelay)
55+
#mstime = int(time.time()*1000)
56+
mstime = int(ser.msgs[-1].split(',')[0])
57+
val = float(ser.msgs[-1].split(',')[1])
58+
message = "{:d},{:0.2f}".format(mstime, val)
59+
return message
60+
61+
async def lookback(websocket, ser):
62+
print(len(ser.msgs))
63+
for msg in (ser.msgs[i] for i in range(0,len(ser.msgs),10)):
64+
try:
65+
mstime = int(msg.split(',')[0])
66+
val = float(msg.split(',')[1])
67+
message = "{:d},{:0.2f}".format(mstime, val)
68+
await websocket.send(message)
69+
print("lookback: {}".format(msg))
70+
except:
71+
pass
72+
73+
async def handler(websocket,path,mytasks=[],mycallbacks=[],ser=''):
74+
#await lookback(websocket, ser)
75+
76+
while True:
77+
listener_task = asyncio.ensure_future(listener(websocket, path))
78+
producer_task = asyncio.ensure_future(producer(websocket, ser))
79+
tasks = [listener_task, producer_task]
80+
done, pending = await asyncio.wait(tasks, return_when=asyncio.FIRST_COMPLETED)
81+
82+
if listener_task in done:
83+
## new data has arrived from the websocket
84+
print("listener_task triggered!")
85+
message = listener_task.result()
86+
await consumer(websocket, path, message)
87+
mycallbacks[0](message.encode('utf-8'))
88+
print(message.encode('utf-8'))
89+
else:
90+
listener_task.cancel()
91+
92+
if producer_task in done:
93+
message = producer_task.result()
94+
await websocket.send(message)
95+
print("{} sent".format(message))
96+
97+
98+
def main():
99+
loop = asyncio.get_event_loop()
100+
coro = serial.aio.create_serial_connection(loop, serPort, url=SERPATH, baudrate=BAUDRATE)
101+
#ser_task = asyncio.ensure_future(coro)
102+
ser_transport, ser_protocol = loop.run_until_complete(coro) # instead of coro?
103+
104+
#ser_transport = ser_task.result()[0]
105+
#ser_protocol = ser_task.result()[1]
106+
107+
myhandler = functools.partial(handler,mytasks=[],mycallbacks=[ser_transport.write],ser=ser_protocol)
108+
start_server = websockets.serve(myhandler, '0.0.0.0', WSPORT) # NOT localhost
109+
#ws_task = asyncio.ensure_future(start_server)
110+
asyncio.get_event_loop().run_until_complete(start_server)
111+
#print("ws_task results: {}".format(ws_task.result()))
112+
#ws_serv = ws_task.result()
113+
114+
asyncio.get_event_loop().run_forever()
115+
116+
if __name__ == "__main__":
117+
main()

www/555.html

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
<head>
2+
<script src="js/smoothie.js"></script>
3+
4+
<script>
5+
// parse arguments from URL
6+
// www.mysite.com/my_app.html?x=1234
7+
var GET = {};
8+
var query = window.location.search.substring(1).split("&");
9+
for (var i = 0, max = query.length; i < max; i++) {
10+
if (query[i] === "") // check for trailing & with no param
11+
continue;
12+
var param = query[i].split("=");
13+
GET[decodeURIComponent(param[0])] = decodeURIComponent(param[1] || "");
14+
}
15+
16+
var ws = new WebSocket('ws://169.229.198.105:8801/');
17+
var MSPERPOINT = 50;
18+
19+
var NUMLINES = 300;
20+
if ( Number.isInteger(parseInt(GET.lookback)) ) {
21+
NUMLINES = parseInt(parseFloat(GET.lookback)*60000/MSPERPOINT);
22+
}
23+
24+
var PLOTMAX = 1000;
25+
if ( Number.isInteger(parseInt(GET.ymax)) ) {
26+
PLOTMAX = parseFloat(GET.ymax);
27+
}
28+
29+
var PLOTMIN = 0;
30+
if ( Number.isInteger(parseInt(GET.ymin)) ) {
31+
PLOTMIN = parseFloat(GET.ymin);
32+
}
33+
34+
var PLOTWIDTH = 600;
35+
var PLOTHEIGHT = 300;
36+
var MILLISPERLINE = NUMLINES * MSPERPOINT / 5;
37+
38+
// create the chart stuff
39+
var mytimeline = new TimeSeries();
40+
var mytimeline2 = new TimeSeries();
41+
42+
var chart = new SmoothieChart({millisPerPixel:NUMLINES*MSPERPOINT/PLOTWIDTH,
43+
grid:{fillStyle:'#ffffff', millisPerLine:MILLISPERLINE,verticalSections: (PLOTMAX-PLOTMIN)/100},
44+
labels:{fillStyle:'#000000', fontSize:16, precision:0},
45+
timestampFormatter: function(date) {
46+
function pad2(number) { return (number < 10 ? '0' : '') + number }
47+
return pad2(date.getHours()) + ':' + pad2(date.getMinutes()) + ':' + pad2(date.getSeconds());
48+
},
49+
yMinFormatter: function(min, precision) {
50+
return parseFloat(min).toFixed(precision).toString();
51+
},
52+
yMaxFormatter: function(max, precision) {
53+
return parseFloat(max).toFixed(precision).toString();
54+
},
55+
interpolation:'step',maxDataSetLength: NUMLINES,maxValue:PLOTMAX,minValue:PLOTMIN});
56+
57+
chart.addTimeSeries(mytimeline, { strokeStyle: 'rgba(0, 255, 0, 1)',
58+
fillStyle: 'rgba(0, 255, 0, 0.3)', lineWidth: 4 });
59+
chart.addTimeSeries(mytimeline2, { strokeStyle: 'rgba(0, 0, 255, 1)',
60+
fillStyle: 'rgba(0, 0, 255, 0.15)', lineWidth: 1 });
61+
62+
var firstRun = true;
63+
64+
function myLoading() {
65+
if ( Number.isInteger(parseInt(GET.lookback)) ) {
66+
document.getElementById("in_lookback").value = parseFloat(GET.lookback);
67+
}
68+
if ( Number.isInteger(parseInt(GET.ymin)) ) {
69+
document.getElementById("in_ymin").value = parseInt(GET.ymin);
70+
}
71+
if ( Number.isInteger(parseInt(GET.ymax)) ) {
72+
document.getElementById("in_ymax").value = parseInt(GET.ymax);
73+
}
74+
}
75+
76+
// put in the data for the chart
77+
ws.onmessage = function(e){
78+
var line = e.data.split(',');
79+
if (line.length == 2) {
80+
var ptTime = Date.now();
81+
var ptVal = parseFloat(line[1]);
82+
var ptVal2 = parseFloat(line[0]);
83+
mytimeline.append(ptTime, ptVal);
84+
mytimeline2.append(ptTime, ptVal2);
85+
86+
document.getElementById("curVal").innerHTML = ptVal.toString();
87+
88+
if (firstRun) {
89+
chart.streamTo(document.getElementById("chart"));
90+
console.log("plotting started!");
91+
firstRun = false;
92+
}
93+
}
94+
}
95+
</script>
96+
</head>
97+
98+
<body onload="myLoading()">
99+
<h1>Fresh Data, Served Every Five Seconds</h1>
100+
<div>
101+
<p>Wonder what the 555 IC at work is doing? With Arduino, Websockets, and JS, wonder no more.
102+
<form style="width:270px;line-height: 1.5;border:4px solid black;padding:5px;">
103+
Minutes to look back:
104+
<input id="in_lookback" name="lookback" value="0.25" type="text"
105+
size=5 style="float:right;"><br>
106+
Minimum y-value:
107+
<input id="in_ymin" name="ymin" value="0" type="text"
108+
size=5 style="float:right;"><br>
109+
Maximum y-value:
110+
<input id="in_ymax" name="ymax" value="1000" type="text"
111+
size=5 style="float:right;"><br>
112+
<div style="text-align:center;">
113+
<input type="submit" value="Adjust View"
114+
style="margin: 5px auto;font-weight:bold;">
115+
</div>
116+
</form>
117+
</div>
118+
119+
<div id="divchart" style="position:relative;">
120+
<div style="position:absolute; left:5px; top:5px; z-index:1;
121+
color:white; font-weight:bold; font-family:sans-serif;
122+
background-color:black;">
123+
Current value: <span id="curVal"></span>
124+
</div>
125+
<canvas id="chart" width="600" height="300"
126+
style="position:absolute; z-index:0; border: 4px solid black;">
127+
</canvas>
128+
</div>
129+
130+
</body>

0 commit comments

Comments
 (0)