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()

I dont use WX but I don't think that matters. There's a lot of code there and I admit I didn't read it all. So I don't know really what you're doing already, but you have asked a specific question.

I think I would make each sensor a different obect (instance of a sensor class). The instantiation of sensors should be mediated by a dictionary (d[a]=Sensor(initdata)) so you can step through each sensor obect (dictionary entry) and call a get_data() method, appending the appropriate information to a list

Cheers for that!

I've been reading up on dictionaries today and admittedley they didn't work as I expected them to do (they worked better so I'm not dissapointed!).
I didn't realise that once the key(?) was added it was updated each time it was called later on.

I have set sensors = {} globally and then at the end of the reading have:

sensors[self.AL] = self.Temp
print sensors

I thought I would have to check the sensor existed etc before but no works really well - this is why I love learning Python!

I noticed the dictionary you used looks a little different however - is there an advantages to doing it the way you have? I need to read though the answer in a bit more detail admittedly and clue up on my programming speak

From here I need to be able to take the following self.AL (the device address) and self.Temp (temp) and add it to the tree...which I guess your not sure of but thats cool
The root would be "Sensors"
I would need to dynamically append self.AL when it appears
I would then need to append self.Temp to the matching self.AL or make a new self.AL entry if it doesn't exist...
Phew

However what you posted has been a nice step forward which is cool

My description of the dictionary set-up was only notional and just to let you understand how to use objects as dictionary values.

This article has been dead for over six months. Start a new discussion instead.