forked from dsoto/Tiny-BMS-Monitor
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbokeh_battery_app.py
122 lines (99 loc) · 4.74 KB
/
bokeh_battery_app.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
from bokeh.server.server import Server
from bokeh.application import Application
from bokeh.application.handlers.function import FunctionHandler
from bokeh.plotting import figure, ColumnDataSource
from bokeh.layouts import grid, row, column
from bokeh.io import curdoc
from bokeh.palettes import d3
import pymodbus.client.sync
import datetime
import numpy as np
def make_document(doc):
def update():
# get time stamp
sample_time = datetime.datetime.now()
# TODO: how do we update the data source with an array of voltages?
# voltages = client.read_holding_registers(0, 16, unit=0xAA).registers[2:]
# for now, make separate calls
v = {}
for i in cells:
address = i + 1
v[i] = client.read_holding_registers(address, 4, unit=0xAA).registers[0]/10
# get balance state and encode colors
balance = client.read_holding_registers(51, 16, unit=0xAA).registers[0]
balances = '{:016b}'.format(balance)
# get pack current and temp
current = np.array(client.read_holding_registers(38, 2, unit=0xAA).registers,
dtype=np.uint16).view(dtype=np.float32)[0]
voltage = np.array(client.read_holding_registers(36, 2, unit=0xAA).registers,
dtype=np.uint16).view(dtype=np.float32)[0]
temperature = client.read_holding_registers(48, 2, unit=0xAA).registers[0]/10.
# update trend source
new = {}
new.update({'v{}'.format(i):[v[i]] for i in cells})
new.update({'c{}'.format(i):[palette[i-1]] for i in cells})
new.update({'s{}'.format(i):[4] if balances[num_cells - i]=='1' else [0] for i in cells})
new['x'] = [sample_time]
trend_source.stream(new)
temp_source.stream({'x':[sample_time], 't':[temperature]})
current_source.stream({'ts':[sample_time], 'current':[current]})
voltage_source.stream({'ts':[sample_time], 'voltage':[voltage]})
# update bar source
bar_source.data['c'] = [palette[i-1] for i in cells]
bar_source.data['v'] = [v[i] for i in cells]
# write to log file
with open(log_file, 'a') as f:
f.write(sample_time.strftime('%Y-%m-%dT%H:%M:%S'))
f.write(', ')
for i in cells:
f.write('{}, '.format(v[i]))
f.write('{}, '.format(balances[:-2]))
f.write('{}, '.format(temperature))
f.write('{}, '.format(current))
f.write('\n')
# create data stores
num_cells = 14
cells = list(range(1, num_cells + 1))
# create colors for battery cell rows
palette = d3['Category20'][14]
trend_dict = {}
trend_dict.update({'v{}'.format(i):[] for i in cells})
trend_dict.update({'c{}'.format(i):[] for i in cells})
trend_dict.update({'s{}'.format(i):[] for i in cells})
trend_dict['x'] = []
trend_source = ColumnDataSource(trend_dict)
bar_source = ColumnDataSource({'x': cells, 'v': [], 'c':[]})
temp_source = ColumnDataSource({'x':[], 't':[]})
current_source = ColumnDataSource({'ts':[], 'current':[]})
voltage_source = ColumnDataSource({'ts':[], 'voltage':[]})
# set up graphs and callback
doc.add_periodic_callback(update, 5000)
# define figures and layout
bar_fig = figure(title='Cell Voltages', y_range=[3000, 4200])
trend_fig = figure(title='Cell Voltages Trend', x_axis_type='datetime')
temp_fig = figure(title='Temperature', x_axis_type='datetime')
current_fig = figure(title='Current', x_axis_type='datetime')
voltage_fig = figure(title='Pack Voltage', x_axis_type='datetime')
# generate trend lines and data points
for i in cells:
voltage = 'v{}'.format(i)
color = 'c{}'.format(i)
size = 's{}'.format(i)
trend_fig.circle(source=trend_source, x='x', y=voltage, color=color, size=size)
trend_fig.line(source=trend_source, x='x', y=voltage, color=palette[i-1], line_width=2)
bar_fig.vbar(source=bar_source, x='x', top='v', color='c', width=0.9)
temp_fig.line(source=temp_source, x='x', y='t', color='black')
current_fig.line(source=current_source, x='ts', y='current', color='black')
voltage_fig.line(source=voltage_source, x='ts', y='voltage', color='black')
plots = grid(row(column(trend_fig, row(voltage_fig, current_fig, temp_fig)), bar_fig),
sizing_mode='stretch_both')
doc.add_root(plots)
doc.title = "Energus BMS Real-Time Voltages"
bluetooth_port = '/dev/tty.EPSBMS-SPPDev'
usb_port = '/dev/tty.SLAB_USBtoUART'
port = usb_port
client = pymodbus.client.sync.ModbusSerialClient(method='rtu',
port=port, timeout=2, baudrate=115200)
client.connect()
log_file = datetime.datetime.now().strftime('%Y-%m-%d-%H%M%S') + '-log.csv'
make_document(curdoc())