Hi,
I'm doing some more work on my sensor program - and it now has a GUI and graph which I'm happy with.
I'm having a look at a WX python tree - which looks like a good control for what I'm looking for.
The question I have is this - and I think its going to be simple - from the input I get from the sensor how can I best assign a specific sensor and its readings to a variable or string for example?
For example the address might be 4008B2E00 which would give a temp etc...but at the same time 3 other sensors would be giving readings.
I would like to be able to map each onto the graphi in the middle but I have no predetermined way of knowing how many sensors might be connected or their addresses - does that make sense?
At the moment it's using self.data - but obviously that's going to plot each reading as it comes in with no seperation...any idea on the best way to go?
The code I have so far is below - work in progress still...
The GUI_Update will hopefully soon search the tree and update the relevant child when it comes in...based on any ideas I can get
Cheers!
"""
Base auto read and write code attributed to:
Eli Bendersky (eliben@gmail.com)
License: this code is in the public domain
Last modified: 31.07.2008
"""
import os
import pprint
import random
import sys
import wx
import serial
import datetime;
# The recommended way to use wx with mpl is with the WXAgg
# backend.
#
import matplotlib
matplotlib.use('WXAgg')
from matplotlib.figure import Figure
from matplotlib.backends.backend_wxagg import \
FigureCanvasWxAgg as FigCanvas, \
NavigationToolbar2WxAgg as NavigationToolbar
import numpy as np
import pylab
class DataGen(object):
def __init__(self, init=0):
self.data = self.init = init
self.OpenSerial()
def OpenSerial(self):
try:
self.ser = serial.Serial('com7',9600,timeout=1)
self.next()
except serial.SerialException, e:
msg = "Serial Port Cannot be Initialized: Check the device is plugged in and restart"
wx.MessageBox(msg, "Serial Fault", wx.OK|wx.YES_DEFAULT|wx.ICON_EXCLAMATION)
sys.exit("Serial Errors")
def next(self):
self._recalc_data()
return self.data
def _recalc_data(self):
Data_in = self.ser.readline().encode('hex')
for data in Data_in.split('7e'):
if data[6:8] == '90':
print "=========================="
print "Found Packet: 7e%s" % data
print "Packet Type = ZgBee RX Packet"
self.AH = data [10:18]
self.AL = data [18:26]
print "Device Address = ", self.AH, self.AL
self.TH = data [34:36]
self.TL = data [38:40]
self.THc = int(self.TH, 16)
self.TLc = int(self.TL, 16)
if data[41:42] == '0':
self.Temp = "%d.%d" % (self.THc, self.TLc)
print "Temperature:", self.Temp
else:
self.Temp = "-%d.%d" % (self.THc, self.TLc)
print "Temperature:", self.Temp
now = datetime.datetime.now()
self.CUR_YEAR = now.year
self.CUR_MONTH = now.month
self.CUR_DAY = now.day
self.CUR_HOUR = now.hour
self.CUR_MIN = now.minute
self.CUR_SEC = now.second
self.CUR_MSEC = now.microsecond
self.timedate = "Time and Date: %d/%d/%d - %d:%d:%d:%d" % (self.CUR_DAY, self.CUR_MONTH, self.CUR_YEAR, self.CUR_HOUR, self.CUR_MIN, self.CUR_SEC, self.CUR_MSEC)
print self.timedate
print "======================="
print " "
self.data = self.Temp
class BoundControlBox(wx.Panel):
def __init__(self, parent, ID, label, initval):
wx.Panel.__init__(self, parent, ID)
self.value = initval
box = wx.StaticBox(self, -1, label)
sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
self.radio_manual = wx.RadioButton(self, -1,
label="Manual")
self.manual_text = wx.TextCtrl(self, -1,
size=(100,-1),
value=str(initval),
style=wx.TE_PROCESS_ENTER)
self.Bind(wx.EVT_UPDATE_UI, self.on_update_manual_text, self.manual_text)
self.Bind(wx.EVT_TEXT_ENTER, self.on_text_enter, self.manual_text)
manual_box = wx.BoxSizer(wx.HORIZONTAL)
manual_box.Add(self.radio_manual, flag=wx.ALIGN_CENTER_VERTICAL)
manual_box.Add(self.manual_text, flag=wx.ALIGN_CENTER_VERTICAL)
sizer.Add(manual_box, 0, wx.ALL, 10)
self.SetSizer(sizer)
sizer.Fit(self)
def on_update_manual_text(self, event):
self.manual_text.Enable(self.radio_manual.GetValue())
def on_text_enter(self, event):
self.value = self.manual_text.GetValue()
def is_auto(self):
return self.radio_auto.GetValue()
def manual_value(self):
return self.value
class GraphFrame(wx.Frame):
""" The main frame of the application
"""
title = 'Arduino / Xbee Sensor reading'
def __init__(self):
wx.Frame.__init__(self, None, -1, self.title)
self.datagen = DataGen()
self.data = [self.datagen.next()]
self.paused = False
self.create_menu()
self.create_status_bar()
self.create_main_panel()
self.redraw_timer = wx.Timer(self)
self.Bind(wx.EVT_TIMER, self.on_redraw_timer, self.redraw_timer)
self.redraw_timer.Start(2500)
def create_menu(self):
self.menubar = wx.MenuBar()
menu_file = wx.Menu()
m_expt = menu_file.Append(-1, "&Save plot\tCtrl-S", "Save plot to file")
self.Bind(wx.EVT_MENU, self.on_save_plot, m_expt)
menu_file.AppendSeparator()
m_exit = menu_file.Append(-1, "E&xit\tCtrl-X", "Exit")
self.Bind(wx.EVT_MENU, self.on_exit, m_exit)
self.menubar.Append(menu_file, "&File")
self.SetMenuBar(self.menubar)
def create_main_panel(self):
self.panel = wx.Panel(self)
self.init_plot()
self.canvas = FigCanvas(self.panel, -1, self.fig)
self.ymin_control = BoundControlBox(self.panel, -1, "Y min", 0)
self.ymax_control = BoundControlBox(self.panel, -1, "Y max", 100)
self.pause_button = wx.Button(self.panel, -1, "Pause")
self.Bind(wx.EVT_BUTTON, self.on_pause_button, self.pause_button)
self.Bind(wx.EVT_UPDATE_UI, self.on_update_pause_button, self.pause_button)
self.hbox1 = wx.BoxSizer(wx.HORIZONTAL)
self.hbox1.Add(self.pause_button, border=5, flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL)
self.hbox1.AddSpacer(20)
self.hbox2 = wx.BoxSizer(wx.HORIZONTAL)
self.hbox2.AddSpacer(24)
self.hbox2.Add(self.ymin_control, border=5, flag=wx.ALL)
self.hbox2.Add(self.ymax_control, border=5, flag=wx.ALL)
self.tree = wx.TreeCtrl(self.panel, 1, wx.DefaultPosition, (-1,-1), wx.TR_HIDE_ROOT|wx.TR_HAS_BUTTONS)
self.display = wx.StaticText(self.panel, -1, '',(10,10), style=wx.EXPAND | wx.GROW)
self.root = self.tree.AddRoot('XBee')
self.xb = self.tree.AppendItem(self.root, 'Connected Nodes')
self.tree.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnSelChanged, id=1)
self.hbox3 = wx.BoxSizer(wx.HORIZONTAL | wx.EXPAND)
self.hbox3.Add(self.tree, -1, flag=wx.EXPAND | wx.ALIGN_LEFT, border=5)
self.hbox3.Add(self.display, -1, flag=wx.EXPAND | wx.ALIGN_LEFT, border=10)
self.vbox = wx.BoxSizer(wx.VERTICAL)
self.vbox.Add(self.canvas, 1, flag=wx.LEFT | wx.TOP | wx.GROW)
self.vbox.Add(self.hbox1, 0, flag=wx.ALIGN_LEFT | wx.TOP)
self.vbox.Add(self.hbox2, 0, flag=wx.ALIGN_LEFT | wx.TOP)
self.vbox.Add(self.hbox3, 1, flag=wx.LEFT | wx.TOP)
self.panel.SetSizer(self.vbox)
self.vbox.Fit(self)
def OnSelChanged(self, event):
item = event.GetItem()
self.display.SetLabel(self.tree.GetItemText(item))
def create_status_bar(self):
self.statusbar = self.CreateStatusBar()
def init_plot(self):
self.dpi = 100
self.fig = Figure((3.0, 3.0), dpi=self.dpi)
self.axes = self.fig.add_subplot(111)
self.axes.set_axis_bgcolor('black')
self.axes.set_title('Arduno Read', size=12)
pylab.setp(self.axes.get_xticklabels(), fontsize=8)
pylab.setp(self.axes.get_yticklabels(), fontsize=8)
# plot the data as a line series, and save the reference
# to the plotted line series
#
self.plot_data = self.axes.plot(
self.data,
linewidth=1,
color=(1, 1, 0),
)[0]
def draw_plot(self):
""" Redraws the plot
"""
# when xmin is on auto, it "follows" xmax to produce a
# sliding window effect. therefore, xmin is assigned after
# xmax.
#
xmax = len(self.data) if len(self.data) > 50 else 50
xmin = xmax - 50
ymin = int(self.ymin_control.manual_value())
ymax = int(self.ymax_control.manual_value())
self.axes.set_xbound(lower=xmin, upper=xmax)
self.axes.set_ybound(lower=ymin, upper=ymax)
self.axes.grid(True, color='gray')
self.plot_data.set_xdata(np.arange(len(self.data)))
self.plot_data.set_ydata(np.array(self.data))
self.canvas.draw()
#self.GUI_Update()
#Complete Search function
#def GUI_Update(self):
# item, self.datagen.AL = self.tree.GetFirstChild(self.root)
#
# if self.tree.GetItemText(item) == self.datagen.AL:
# print "True"
# else:
# self.tree.AppendItem(self.xb, self.datagen.AL)
def on_pause_button(self, event):
self.paused = not self.paused
def on_update_pause_button(self, event):
label = "Resume" if self.paused else "Pause"
self.pause_button.SetLabel(label)
def on_save_plot(self, event):
file_choices = "PNG (*.png)|*.png"
dlg = wx.FileDialog(
self,
message="Save plot as...",
defaultDir=os.getcwd(),
defaultFile="plot.png",
wildcard=file_choices,
style=wx.SAVE)
if dlg.ShowModal() == wx.ID_OK:
path = dlg.GetPath()
self.canvas.print_figure(path, dpi=self.dpi)
self.flash_status_message("Saved to %s" % path)
def on_redraw_timer(self, event):
# if paused do not add data, but still redraw the plot
# (to respond to scale modifications, grid change, etc.)
#
if not self.paused:
self.data.append(self.datagen.next())
self.draw_plot()
def on_exit(self, event):
self.datagen.ser.close()
self.Destroy()
def flash_status_message(self, msg, flash_len_ms=1500):
self.statusbar.SetStatusText(msg)
self.timeroff = wx.Timer(self)
self.Bind(
wx.EVT_TIMER,
self.on_flash_status_off,
self.timeroff)
self.timeroff.Start(flash_len_ms, oneShot=True)
def on_flash_status_off(self, event):
self.statusbar.SetStatusText('')
if __name__ == '__main__':
app = wx.PySimpleApp()
app.frame = GraphFrame()
app.frame.Show()
app.MainLoop()