Code

Project: jvfeatures

jvtypes.h      jvfeatures.h      chessSeg.cpp      jvtypes.cpp      jvtest.cpp      jvfeatures.cpp     

Project: Other

migrateMailbox.scpt.txt     

Project: Infinite HMM Tutorial

run.m      iHMM_tutorial.zip      HDP_HMM.m      README.txt      ConditionalProbabilityTable.m      HDP.m      HMMProblem.m      HMM.m     

Project: RRT

RRT.h      plot_output.py      RRT.tgz      rrt_test.cpp      RRT.cpp      BidirectionalRRT.cpp      AbstractRRT.cpp     

Project: Box2D_friction_mod

WheelConstraint.h      test_TopDownCar.py      b2FrictionJoint.h      python_friction_joint.patch      test_TopDownFrictionJoint.py      TestEntries.cpp      TopDownCar.h      b2FrictionJoint.cpp      box2d_friction_joint.patch     

Project: Dirichlet Process Mixture Tutorial

EM_GM.m      DP_Demo.m      DPMM.m      DP_Tutorial.zip      DirichletProcess.m      gaussian_EM.m     

Project: Arduino_Code

plot_ardunio_data.sh      Arduino_Code.zip      convert_range2D.py      arduino-serial.c      oscilloscope.sh      oscilloscope.pde      motordriver.pde      helicopter_controller.pde      accelerometer_test.pde      ranger_plane_sweep.pde      clodbuster_controller.pde      pwm_manual.pde      ranger_test.pde      servo_test.pde     

Project: ArduCom

arducom.py      setup.py     

Project: support

geshi.php      Protector.php     

Project: Cogent

CodePane.php      NotesPane.php      PicsPane.php      Cogent.php      PubsTable.php     
Click here to download "resources/code/ArduCom/arducom.py"

resources/code/ArduCom/arducom.py

#!/usr/bin/env python  

#icons:
#echo
#write
#filter
#plot

# TODO:
# add button for plotting (make nice mouseover that explains what it expects)
# figure out how to plot...
# add some visualization to JS state (?)
# add checkboxes for gamepad settings
##
# add a toolbar to replace those buttons
# add baud,port,js(?) pulldowns, and write/echo JS checkboxes to toolbar
# replace JS multibyte commands with chr(val), and get arduino to read it
# add non-analog info about arduino to right col? eg: controlelr states,

#notebook:
#plotter
#gl viewer
#
#serial console
#
#info:
#tty
#bud
#status lights for JS, com, *even Heli*:
#     -controller status
#
#  1 got back to where i was yesterday, but with new interface.  Next up is
#  2 fixing the joystick->terminal calls to use a queue or something so
#  3 that they're threadsafe.  Currently it bombs out on linux and mac.  
#  4 Another thing is to figure out whether it's bad that my mac seems to
#  5 need to initialize pygame at the top, and why it needs gamepad
#  6 declared after terminal in arducom



# Class wrapper for reading joystick values and passing messages over serial
# to an arduino running a helicopter controller


#
################################
#read joystick events into local variables with appropriate names
#
#after each event, either:
#       call function to print all values of each var
#       call function to write that (or all?) vars over serial
#      
#* will need to calibrate analog values for the PWM range
#       -left stick between middle and up gets discretized by 255
#       -right stick right sets 255,-255
#       -right stick left sets -255,255
#       -right stick up sets forward value between 0-255 and backward to 0
#       -right stick down does opposite...
#
################################
#GUI:
#LED to show if joystick is connected
#LED to show if arduino is connected
#table to show most recently written values from gamepad
#frame to show traces of analog values from whatever's being read (gyro, accelero, magneto)
#manual serial terminal

import wx
import wx.lib.plot as plot
import serial
import threading
import Queue
import pygame
import glob
import time
import numpy

#################### UTILITIES ###################
def rescale(x, in_min, in_max, out_min, out_max):
    return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;

def getPorts():
    if wx.Platform == "__WXMAC__":
        portchoices = glob.glob("/dev/tty.usb*")
    elif wx.Platform == "__WXGTK__":
        portchoices = glob.glob("/dev/ttyUSB*")
       
    if len(portchoices) == 0:
        portchoices = ["/dev/tty.usbserial-A800csDh"]
       
    return portchoices
##################################################



#----------------------------------------------------------------------
# Create an own event type, so that GUI updates can be delegated
# this is required as on some platforms only the main thread can
# access the GUI without crashing. wxMutexGuiEnter/wxMutexGuiLeave
# could be used too, but an event is more elegant.
SERIALRX = wx.NewEventType()
# bind to serial data receive events
EVT_SERIALRX = wx.PyEventBinder(SERIALRX, 0)

class SerialRxEvent(wx.PyCommandEvent):
    eventType = SERIALRX
    def __init__(self, windowID, data):
        wx.PyCommandEvent.__init__(self, self.eventType, windowID)
        self.data = data

    def Clone(self):
        self.__class__(self.GetId(), self.data)

#----------------------------------------------------------------------

ID_CLEAR        = wx.NewId()
ID_SAVEAS       = wx.NewId()
ID_SETTINGS     = wx.NewId()
ID_TERM         = wx.NewId()
ID_EXIT         = wx.NewId()

NEWLINE_CR      = 0
NEWLINE_LF      = 1
NEWLINE_CRLF    = 2

EVT_OPENPORT    = 0
EVT_CLOSEPORT   = 1
EVT_OPENJS      = 2
EVT_CLOSEJS     = 3
EVT_TERMSETTINGS= 4
EVT_SETBAUD     = 5
EVT_SETPORT     = 6
EVT_ECHOJS      = 7
EVT_WRITEJS     = 8
EVT_FILTER      = 9
EVT_PLOT        = 10

pygame.display.init() # mac needs these declared at the top.  fishy...

class ArduCom(wx.Frame):
    def __init__(self, *args, **kwds):
        wx.Frame.__init__(self, *args, **kwds)

        # Status bar
        self.CreateStatusBar()
        self.SetStatusText("ArduCom: A joystick serial commander for the Arduino")
       
         # Menu Bar
        self.menubar = wx.MenuBar()
        self.SetMenuBar(self.menubar)
        # File menu pulldown
        filemenu = wx.Menu()
        filemenu.Append(ID_CLEAR, "&Clear", "", wx.ITEM_NORMAL)
        filemenu.Append(ID_SAVEAS, "&Save Text As...", "", wx.ITEM_NORMAL)
        filemenu.AppendSeparator()
        filemenu.Append(ID_TERM, "&Terminal Settings...", "", wx.ITEM_NORMAL)
        filemenu.AppendSeparator()
        filemenu.Append(ID_EXIT, "&Exit", "", wx.ITEM_NORMAL)        
        self.menubar.Append(filemenu, "&File")
        # Other pulldowns?
        #end Menu Bar

        # Add content
        self.terminal = TerminalPanel(self)
        self.mygamepad = Gamepad(self) # this had to come after terminal for some reason.  Very fishy...
        self.write_queue = Queue.Queue(3) # this arg should limit the lag, but it doesn't
       
        self.tabs = MainNotebook(self, -1)
        self.tb = self.CreateToolBar(wx.TB_HORIZONTAL
            | wx.NO_BORDER
            | wx.TB_FLAT
            #| wx.TB_TEXT
            #| wx.TB_HORZ_LAYOUT
            )
        self.__configure_tb()

        self.__set_properties()
        self.__attach_events()          #register events
        self.__do_layout()

    def __configure_tb(self):
        tsize = (36,36) # native res is 48x48 for my custom icons
        self.tb.SetToolBitmapSize(tsize)

        serial_open_bmp = wx.Bitmap("icons/serial_open.png")        
        self.tb.AddLabelTool(EVT_OPENPORT, "Open Serial", serial_open_bmp,
            shortHelp="Open Serial", longHelp="Opens serial port")
        serial_close_bmp = wx.Bitmap("icons/serial_close.png")
        self.tb.AddLabelTool(EVT_CLOSEPORT, "Close Serial", serial_close_bmp,
            shortHelp="Close Serial", longHelp="Closes serial port")

        self.tb.AddSeparator()
        gamepad_open_bmp = wx.Bitmap("icons/gamepad_open.png")
        self.tb.AddLabelTool(EVT_OPENJS, "Open Gamepad", gamepad_open_bmp,
            shortHelp="Open Gamepad", longHelp="Opens gamepad")
        gamepad_close_bmp = wx.Bitmap("icons/gamepad_close.png")
        self.tb.AddLabelTool(EVT_CLOSEJS, "Close Gamepad", gamepad_close_bmp,
            shortHelp="Close Gamepad", longHelp="Closes gamepad")
        gamepad_echo_bmp = wx.Bitmap("icons/gamepad_echo.png")
        self.tb.AddCheckLabelTool(EVT_ECHOJS, "Joystick Echo", gamepad_echo_bmp,
            shortHelp="Echo Joystick commands to stdout (depress to turn off)")
        gamepad_write_bmp = wx.Bitmap("icons/gamepad_transmit.png")
        self.tb.AddCheckLabelTool(EVT_WRITEJS, "Joystick Transmit", gamepad_write_bmp,
            shortHelp="Transmit Joystick commands over serial port (depress to turn off)")
     
        self.tb.AddSeparator()
        info_bmp = wx.Bitmap("icons/serial_config.png")
        self.tb.AddLabelTool(EVT_TERMSETTINGS, "Terminal Info", info_bmp,
            shortHelp="Term Info", longHelp="Opens terminal info dialog")

        self.tb.AddSeparator()        
        self.tb.AddControl(wx.Choice(self.tb, EVT_SETBAUD, (100,50), choices = ["9600", "115200"]))
        self.tb.AddControl(wx.Choice(self.tb, EVT_SETPORT, (100,50), choices = getPorts()))

        self.tb.AddSeparator()
        filter_bmp = wx.Bitmap("icons/filter.png")
        self.tb.AddCheckLabelTool(EVT_FILTER, "Filter Data", filter_bmp,
            shortHelp="Filter incoming data from serial port (depress to turn ON)")
        plot_bmp = wx.Bitmap("icons/plot.png")
        self.tb.AddCheckLabelTool(EVT_PLOT, "Plot Data", plot_bmp,
            shortHelp="Plot incoming data (depress to turn ON)")
           
        self.tb.Realize()

    def __set_properties(self):
        self.SetTitle("ArduCom")
        self.SetSize((1100, 750))

    def __attach_events(self):
        self.Bind(wx.EVT_MENU, self.OnExit, id = ID_EXIT)
        self.Bind(wx.EVT_MENU, self.OnTermSettings, id = ID_TERM)
        self.Bind(wx.EVT_TOOL, self.OnToolClick, id = EVT_OPENPORT)
        self.Bind(wx.EVT_TOOL, self.OnToolClick, id = EVT_CLOSEPORT)
        self.Bind(wx.EVT_TOOL, self.OnToolClick, id = EVT_OPENJS)
        self.Bind(wx.EVT_TOOL, self.OnToolClick, id = EVT_CLOSEJS)
        self.Bind(wx.EVT_TOOL, self.OnToolClick, id = EVT_TERMSETTINGS)
        self.Bind(wx.EVT_COMBOBOX, self.OnToolClick, id = EVT_SETBAUD)
        self.Bind(wx.EVT_COMBOBOX, self.OnToolClick, id = EVT_SETPORT)
        self.Bind(wx.EVT_TOOL, self.OnToolClick, id = EVT_ECHOJS)
        self.Bind(wx.EVT_TOOL, self.OnToolClick, id = EVT_WRITEJS)
        self.Bind(wx.EVT_TOOL, self.OnToolClick, id = EVT_FILTER)
        self.Bind(wx.EVT_TOOL, self.OnToolClick, id = EVT_PLOT)
        #self.Bind(wx.EVT_CLOSE, self.OnExit)
   
    def __do_layout(self):
        bottomsizer = wx.BoxSizer(wx.VERTICAL)
        topsizer = wx.BoxSizer(wx.HORIZONTAL)
        topsizer.Add(self.tabs,3,wx.EXPAND)
        bottomsizer.Add(topsizer, 2, wx.EXPAND)
        serialsizer = wx.StaticBoxSizer(wx.StaticBox(self, -1, "Serial Console"), wx.VERTICAL)
        serialsizer.Add(self.terminal, 1, wx.EXPAND)
        bottomsizer.Add(serialsizer, 1, wx.EXPAND)
        self.SetAutoLayout(True)
        self.SetSizer(bottomsizer)
        self.Layout()

    def OnToolClick(self, event):
        e = event.GetId()
        if e == EVT_OPENPORT:
            self.terminal.OpenPort()
        elif e == EVT_CLOSEPORT:
            self.terminal.OnClose(e)
        elif e == EVT_TERMSETTINGS:
            self.OnTermSettings(e)
        elif e == EVT_OPENJS:
            self.mygamepad.openJS()
        elif e == EVT_CLOSEJS:
            self.mygamepad.OnClose()
        elif e == EVT_SETBAUD:
            self.terminal.settings.baudrate = e.GetSelection()
        elif e == EVT_SETPORT:
            self.terminal.settings.port = e.GetSelection()
            print "set port to ", self.terminal.settings.port
        elif e == EVT_ECHOJS:
            self.mygamepad.settings.echo = not self.mygamepad.settings.echo
            print self.mygamepad.settings.echo
        elif e == EVT_WRITEJS:
            self.mygamepad.settings.write = not self.mygamepad.settings.write
            print self.mygamepad.settings.write
        elif e == EVT_FILTER:
            self.terminal.settings.runFilter = not self.terminal.settings.runFilter
            print "Filter = ", self.terminal.settings.runFilter
        elif e == EVT_PLOT:
            self.tabs.myplot.plotSerial = not self.tabs.myplot.plotSerial
            print "Plot = ", self.tabs.myplot.plotSerial
                                               
    def OnExit(self, event):
        """Menu point Exit"""
        self.terminal.OnClose(ID_EXIT)
        self.Destroy()

    def OnTermSettings(self, event):
        """Menu point Terminal Settings. Show the settings dialog
           with the current terminal settings"""

        dialog = TerminalSettingsDialog(None, -1, "", settings=self.terminal.settings)
        result = dialog.ShowModal()
        dialog.Destroy()        

class MainNotebook(wx.Notebook):
    def __init__(self, parent, id):
        wx.Notebook.__init__(self, parent, id, style=
                             wx.BK_DEFAULT
                             #wx.BK_TOP
                             #wx.BK_BOTTOM
                             #wx.BK_LEFT
                             #wx.BK_RIGHT
                             # | wx.NB_MULTILINE
                             )
        self.myplot = PlotPanel(self)
        self.myplot.StartThread()
        self.AddPage(self.myplot, "My Plot")

        win = self.makeColorPanel(wx.CYAN)
        self.AddPage(win, "Cyan")

        # TODO: remove colorpanel crap and add a glcanvas
     
    def makeColorPanel(self, color):
        p = wx.Panel(self, -1)
        win = ColorPanel(p, color)
        p.win = win
        def OnCPSize(evt, win=win):
            win.SetPosition((0,0))
            win.SetSize(evt.GetSize())
        p.Bind(wx.EVT_SIZE, OnCPSize)
        return p

class ColorPanel(wx.Window):
    def __init__(self, parent, color):
        wx.Window.__init__(self, parent, -1, style = wx.SIMPLE_BORDER)
        self.SetBackgroundColour(color)
        if wx.Platform == '__WXGTK__':
            self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM)
           
class PlotPanel(plot.PlotCanvas):
    def __init__(self, parent):
        plot.PlotCanvas.__init__(self,parent)
        self.thread = None
        self.plotSerial = False             # Flag for whether to run plotdata()
        self.alive = threading.Event()
        self.plotQueue = Queue.Queue(30)   # The Queue that plotdata() reads from
#        self.xVals = range(0,30) #TODO make this a variable
#        self.yVals = [[0]*30]*5 #TODO for now assume we're getting 5 lines
#        self.xVals = numpy.array([range(0,50)])
        #self.yVals = numpy.array([zeros(50)])
        self.yVals = [0]*50

    def OnClose(self):
        """Called on application shutdown."""
        self.plotSerial = False
        self.StopThread()            # stop reader thread

    def StartThread(self):
        """Start the receiver thread"""        
        self.thread = threading.Thread(target=self.readQueue)
        self.thread.setDaemon(1)
        self.alive.set()
        self.thread.start()

    def StopThread(self):
        """Stop the receiver thread, wait util it's finished."""
        if self.thread is not None:
            self.alive.clear()          # clear alive event for thread
            self.myjoy.quit()            # cleanup
            self.thread.join()         # this thread never seems to die by itself
            self.thread = None
               
    def readQueue(self):
        while True:
            data = self.plotQueue.get()
            while self.plotQueue.qsize() > 5:
                self.plotQueue.get_nowait()
            print "qsize = ",self.plotQueue.qsize()
            if self.plotSerial:
                # format a numpy array with 1:200 and yvals
                # initialize as zeros and 1:200
                # on each get, push a value from the back (possible)
                #self.data.append(numpy.transpose([d,[len(d[0])]*len(d)])
                self.yVals.pop(0)
                self.yVals.append(int(data[0]))
    #            data = numpy.vstack((self.xVals,self.yVals))
                d=[]
                for i in range(0,50):
                    d.append((i,self.yVals[i]))
#                print d
                self.plotdata(d);
    #            self.plotdata([[1,2,3,4],[2,4,6,4]]);
    #            self.plotdata([(1,2), (2,3), (3,5), (4,6), (5,8), (6,8), (10,10)]);
                self.plotQueue.task_done()
#                print "got here"
               
    def plotdata(self, data):
        """ Waits on a plotter queue, and redraws whenever a new datapoint is posted"""
#        self.data = [(1,2), (2,3), (3,5), (4,6), (5,8), (6,8), (10,10)]
#        line = plot.PolyLine(self.data, legend='', colour='pink', width=1)
#        gc = plot.PlotGraphics([line], 'Line Graph', 'X Axis', 'Y Axis')
#        self.Draw(gc, xAxis= (0,15), yAxis= (0,15))
        line = plot.PolyLine(data, legend='', colour='pink', width=3)
        gc = plot.PlotGraphics([line], 'Line Graph', 'X Axis', 'Y Axis')
        self.Draw(gc)

class GamepadSetup:
    """Placeholder for various gamepad settings. Used to pass the
       options to the GamepadSettingsDialog."""

    def __init__(self):
        self.echo = False
        self.write = False

class Gamepad(wx.Panel):
    def __init__(self, parent):
        wx.Panel.__init__(self, parent)
        self.parent = parent
        self.myjoy = None
        self.thread = None
        self.alive = threading.Event()
        self.settings = GamepadSetup() #placeholder for the settings

    def openJS(self):
        pygame.joystick.init()
        if pygame.joystick.get_count() == 0:
                print "\nPlease connect a joystick and run again.\n"  
        else:
            self.joysticks = []
            print "\n%d joystick(s) detected." % pygame.joystick.get_count()
            for i in range(pygame.joystick.get_count()):  
                self.myjoy = pygame.joystick.Joystick(i)  
                self.myjoy.init()
                self.joysticks.append(self.myjoy)  
                print "Joystick %d: " % (i) + self.joysticks[i].get_name()  

            self.StartThread()

    def pollJSThread(self):
        while self.doPoll == True:  
            e = pygame.event.wait()
            if (e.type == pygame.JOYBUTTONDOWN or e.type == pygame.JOYAXISMOTION):
                self.handleJoyEvent(e)
               
    def OnClose(self):
        """Called on application shutdown."""
        self.doPoll = False
        pygame.event.post(pygame.event.Event(pygame.K_BREAK)) # hack to get while loop to break
        self.StopThread()            # stop reader thread

    def StartThread(self):
        """Start the receiver thread"""        
        self.thread = threading.Thread(target=self.pollJSThread)
        self.thread.setDaemon(1)
        self.alive.set()
        self.doPoll = True
        self.thread.start()

    def StopThread(self):
        """Stop the receiver thread, wait util it's finished."""
        if self.thread is not None:
            self.alive.clear()          # clear alive event for thread
            self.myjoy.quit()            # cleanup
            #self.thread.join()         # this thread never seems to die by itself
            self.thread = None

    def handleJoyEvent(self,e):
        if e.type == pygame.JOYAXISMOTION:
            cmd = ''
            val = 0
            #print e.axis, e.value
            if e.axis == 1: # left-vertical
                # srping load mode: write values in proportion to up-stick; ignore down-stick
                val = int(rescale(e.value,0,-1,0,255))
                val = max(val,0)
                cmd = '!' # thrust
            elif e.axis == 2: # right-horizontal
                val = int(rescale(e.value,-1,1,0,100))
                cmd = '#' # yaw (same as $)
            elif e.axis == 3: # right-vertical
                val = int(rescale(e.value,-1,1,-250,250))
                if val > 0:
                    cmd = '^' # forward
                else:
                    cmd = '%' # backward
                    val = -val
            if e.axis != 0:
                if self.settings.echo:
                    print e.axis, e.value, "cmd: ", 'm'+str(val)+cmd+'m'
                if self.settings.write:
                    if self.parent.terminal.alive.isSet(): # don't fill the queue unless someone is clearing it
                        self.parent.write_queue.put('m'+str(val)+cmd+'m') # TODO: convert this to 3 byte version (currently 6)
        elif e.type == pygame.JOYBUTTONDOWN:  
            cmd = ' '
            if e.button == 0:
                cmd = 'v' # verbose
            if e.button == 1:
                cmd = 'm51#m' # toggle all controllers
            if e.button == 2:
                cmd = 'y' # yaw controller
            if e.button == 3:
                cmd = 'p' # pitch controller
            if e.button == 4:
                cmd = ',' # pgain--
            if e.button == 5:
                cmd = '.' # pgain++
            if e.button == 6:
                cmd = '<' # dgain--
            if e.button == 7:
                cmd = '>' # dgain++
            if e.button == 8:
                cmd = 'l' # log data (print analogRead vals to stdout)
            if e.button == 9:
                cmd = ' ' # reset
            if self.settings.echo:
                print e.button, "cmd: ",cmd
            if self.settings.write:
                self.parent.write_queue.put(cmd)
                               
class TerminalPanel(wx.Panel):
    """Simple terminal program for wxPython"""
   
    def __init__(self, parent):
        wx.Panel.__init__(self, parent)
        self.parent = parent
        self.serial = serial.Serial()
        self.serial.timeout = 0.5           # make sure that the alive event can be checked from time to time
        self.settings = TerminalSetup()     # placeholder for the settings
        self.dataMode = False               # currently building a data packet (see dataFilter)
        self.skip_for_newline = False       # clumsy way to avoid newlines between data reads
        self.dataBuf = []                   # container for a single data packet
        self.textBuf = []                   # container for non-data text
        self.readthread = None
        self.writethread = None
        self.alive = threading.Event()
        self.text_ctrl_output = wx.TextCtrl(self, -1, "", style=wx.TE_MULTILINE|wx.TE_READONLY)
        self.__attach_events()
        self.__do_layout()
   
    def __attach_events(self):
        self.Bind(EVT_SERIALRX, self.OnSerialRead)
        self.Bind(wx.EVT_MENU, self.OnClear, id = ID_CLEAR)
        self.Bind(wx.EVT_MENU, self.OnSaveAs, id = ID_SAVEAS)
        self.text_ctrl_output.Bind(wx.EVT_CHAR, self.OnKey)
       
    def __do_layout(self):
        sizer_1 = wx.BoxSizer(wx.VERTICAL)
        sizer_1.Add(self.text_ctrl_output, 1, wx.EXPAND, 0)
        self.SetAutoLayout(1)
        self.SetSizer(sizer_1)
        self.Layout()
       
    def OnClose(self, event):
        """Called on application shutdown."""
        self.StopThread()               #stop reader thread
        self.serial.close()             #cleanup

    def OnSaveAs(self, event):
        """Save contents of output window."""
        filename = None
        dlg = wx.FileDialog(None, "Save Text As...", ".", "", "Text File|*.txt|All Files|*",  wx.SAVE)
        if dlg.ShowModal() ==  wx.ID_OK:
            filename = dlg.GetPath()
        dlg.Destroy()
       
        if filename is not None:
            f = file(filename, 'w')
            text = self.text_ctrl_output.GetValue()
            if type(text) == unicode:
                text = text.encode("latin1")    #hm, is that a good asumption?
            f.write(text)
            f.close()
   
    def OnClear(self, event):
        """Clear contents of output window."""
        self.text_ctrl_output.Clear()
               
    def StartThread(self):
        """Start the receiver thread"""        
        self.readthread = threading.Thread(target=self.ComPortThread)
        self.readthread.setDaemon(1)

        self.writethread = threading.Thread(target=self. serialWriter)
        self.writethread.setDaemon(1)
       
        self.alive.set()
        self.readthread.start()
        self.writethread.start()

    def StopThread(self):
        """Stop the receiver thread, wait util it's finished."""
        if self.readthread is not None:
            self.alive.clear()          #clear alive event for thread
            self.readthread.join()          #wait until thread has finished
            #self.writethread.join()          #wait until thread has finished
            self.readthread = None
            #self.writethread = None

    def OpenPort(self):
        """Just initialize the port"""
        self.serial.baudrate = self.settings.baudrate
        self.serial.setPort(self.settings.port)
        print "Opening port: ", self.serial.port, " (", self.serial.baudrate, ")"
        try:
            self.serial.open()
            self.StartThread()
            # this title string should be passed instead to a staticboxsizer for htis panel
            # self.SetTitle("Serial Terminal on %s [%s, %s%s%s%s%s]" % (
            #     self.serial.portstr,
            #     self.serial.baudrate,
            #     self.serial.bytesize,
            #     self.serial.parity,
            #     self.serial.stopbits,
            #     self.serial.rtscts and ' RTS/CTS' or '',
            #     self.serial.xonxoff and ' Xon/Xoff' or '',
            #     )
            #)
            if not self.alive.isSet():
                print "closing.."
                self.Close()
        except serial.SerialException, e:
            dlg = wx.MessageDialog(None, str(e), "Serial Port Error", wx.OK | wx.ICON_ERROR)
            dlg.ShowModal()
            dlg.Destroy()
           
    def serialWriter(self):
        """Only this function gets to issue the serial.write call, since output
           to this port can potentially come from more than one source (kbd, joy).
           Reads a queue owned by arducom and executes commands in whatever order
           they arrive.  *Python supports priority queues, which we could use to
           force console commands over joy commands, but that hardly seems necessary"""

        if self.alive.isSet():
            while True:
                cmd = self.parent.write_queue.get()
                if self.settings.echo:          #do echo if needed
                    self.text_ctrl_output.WriteText(cmd)
                self.serial.write(cmd)
           
    def OnKey(self, event):
        """Key event handler. if the key is in the ASCII range, write it to the serial port.
           Newline handling and local echo is also done here."""

        if self.alive.isSet():
            code = event.GetKeyCode()
            if code < 256:                          #is it printable?
                if code == 13:                      #is it a newline? (check for CR which is the RETURN key)
                    if self.settings.echo:          #do echo if needed
                        self.text_ctrl_output.AppendText('\n')
                    cmd = ''
                    if self.settings.newline == NEWLINE_CR:
                        cmd = '\r'
                    elif self.settings.newline == NEWLINE_LF:
                        cmd = '\n'
                    elif self.settings.newline == NEWLINE_CRLF:
                        cmd = '\r\n'
                    self.parent.write_queue.put(cmd)
                else:
                    char = chr(code)
                    self.parent.write_queue.put(char)
            else:
                print "Extra Key:", code
        #else:
            #print "Serial Terminal not open"

    def ComPortThread(self):
        """Thread that handles the incomming traffic. Does the basic input
           transformation (newlines) and generates an SerialRxEvent"""

        while self.alive.isSet():               #loop while alive event is true
            text = self.serial.read(1)          #read one, with timout
            if text:                            #check if not timeout
                n = self.serial.inWaiting()     #look if there is more to read
                if n:
                    text = text + self.serial.read(n) #get it
                #newline transformation
                if self.settings.newline == NEWLINE_CR:
                    text = text.replace('\r', '\n')
                elif self.settings.newline == NEWLINE_LF:
                    pass
                elif self.settings.newline == NEWLINE_CRLF:
                    text = text.replace('\r\n', '\n')
                event = SerialRxEvent(self.GetId(), text)
                self.GetEventHandler().AddPendingEvent(event)
               
    def OnSerialRead(self, event):
        """Handle input from the serial port."""
        text = event.data   # Does this get multiple characters at once???
        #print text
        if self.settings.unprintable:
            # Substitute in ascii value in <> delimiters if character is unprintable (ord(c)<32)
            text = ''.join([(c >= ' ') and c or '<%d>' % ord(c)  for c in text])
        if self.settings.runFilter:
            filtered = self.dataFilter(text)
            del self.textBuf[:] # must clear this buffer here
            self.text_ctrl_output.AppendText(filtered)
        else:
            self.text_ctrl_output.AppendText(text)
               
    def dataFilter(self, text):
        """ Processes incoming serial data to extract analog data.
            Incoming data is indicated by surrounding it with '[]' delimiters.
            This function is mainly usefull for passing parsed data to a plotter
            or openGL"""

       
        for t in text:
            # switch modes as necessary
            if self.skip_for_newline:
                self.skip_for_newline = False
                continue
            if t == '[':
                self.dataMode = True
            elif t == ']':
                self.dataMode = False
                # on closing packet, send as necessary
                if self.parent.tabs.myplot.plotSerial:
                    datastring = ''.join(self.dataBuf)
                    packet = datastring.split(' ')
                    #print "packet = ",packet
                    self.parent.tabs.myplot.plotQueue.put(packet)
               
                # Reqest skip of next iteration, which we assume is an undesirable newline
                self.skip_for_newline = True    
                # cleanup
                del self.dataBuf[:]
            else:
                # Append to appropriate list
                if self.dataMode:
                    self.dataBuf.append(t)
                else:
                    self.textBuf.append(t)
           
        # return only the non-data portion
        return ''.join(self.textBuf)
           
# end of class TerminalPanel

class TerminalSetup:
    """Placeholder for various terminal settings. Used to pass the
       options to the TerminalSettingsDialog."""

    def __init__(self):
        self.echo = False
        self.unprintable = False
        self.newline = NEWLINE_CRLF
        self.baudrate = 9600          # default: arduino over USB
        self.port = getPorts()[0]    # default: first USB tty on linux
        self.runFilter = False

class TerminalSettingsDialog(wx.Dialog):
    """Simple dialog with common terminal settings like echo, newline mode."""
   
    def __init__(self, *args, **kwds):
        self.settings = kwds['settings']
        del kwds['settings']
        kwds["style"] = wx.DEFAULT_DIALOG_STYLE
        wx.Dialog.__init__(self, *args, **kwds)
       
        # Sizers must be declared before controls in OS X!
        mainsizer = wx.BoxSizer(wx.VERTICAL)
        baudsizer = wx.BoxSizer(wx.HORIZONTAL)
        portsizer = wx.BoxSizer(wx.HORIZONTAL)
        buttonsizer = wx.BoxSizer(wx.HORIZONTAL)
        formatsizer = wx.BoxSizer(wx.VERTICAL)
       
        # The dialog controls
        self.checkbox_echo = wx.CheckBox(self, -1, "Local Echo")
        self.checkbox_unprintable = wx.CheckBox(self, -1, "Show unprintable characters")
        self.radio_box_newline = wx.RadioBox(self, -1, "Newline Handling",
            choices=["CR only", "LF only", "CR+LF"], majorDimension=0, style=wx.RA_SPECIFY_ROWS)
        self.button_ok = wx.Button(self, -1, "OK")
        self.button_cancel = wx.Button(self, -1, "Cancel")
        self.baudPulldown = wx.Choice(self, -1, (100,50), choices = ["9600", "115200"])
        self.portPulldown = wx.Choice(self, -1, (100,50), choices = getPorts())

        # Set properties
        self.SetTitle("Terminal Settings")
        self.radio_box_newline.SetSelection(0)
        self.button_ok.SetDefault()
        self.checkbox_echo.SetValue(self.settings.echo)
        self.checkbox_unprintable.SetValue(self.settings.unprintable)
        self.radio_box_newline.SetSelection(self.settings.newline)

        # Do Layout
        baudsizer.Add(wx.StaticText(self, -1, "Baudrate: ", (60,20)), 0, wx.TOP, 8)
        baudsizer.Add(self.baudPulldown, -1, wx.ALL, 4)
        portsizer.Add(wx.StaticText(self, -1, "Port: ", (60,20)), 0, wx.TOP, 8)
        portsizer.Add(self.portPulldown, -1, wx.ALL, 4)
        buttonsizer.Add(self.button_ok, 0, wx.ALL, 2)
        buttonsizer.Add(self.button_cancel, 0, wx.ALL, 2)
        mainsizer.Add(baudsizer, 0, wx.ALL, 4)
        mainsizer.Add(portsizer, 0, wx.ALL, 4)
        formatsizer.Add(self.checkbox_echo, 0, wx.ALL, 4)
        formatsizer.Add(self.checkbox_unprintable, 0, wx.ALL, 4)
        formatsizer.Add(self.radio_box_newline, 0, wx.ALL | wx.ALIGN_CENTER, 4)
        mainsizer.Add(formatsizer, 0, wx.ALL | wx.ALIGN_CENTER,1)
        mainsizer.Add(buttonsizer, 0, wx.ALL | wx.ALIGN_CENTER,1)
        self.SetSizer(mainsizer)
        self.SetAutoLayout(1)
        mainsizer.Fit(self)
        mainsizer.SetSizeHints(self)
        self.Layout()
        self.CenterOnScreen()

        self.Bind(wx.EVT_BUTTON, self.OnOK, id = self.button_ok.GetId())
        self.Bind(wx.EVT_BUTTON, self.OnCancel, id = self.button_cancel.GetId())
        self.Bind(wx.EVT_CHOICE, self.BaudChoice, self.baudPulldown)
#    def __set_properties(self):
#        self.SetTitle("Terminal Settings")
#        self.radio_box_newline.SetSelection(0)
#        self.button_ok.SetDefault()
#
#    def __do_layout(self):
#        mainsizer = wx.BoxSizer(wx.VERTICAL)
#        baudsizer = wx.BoxSizer(wx.HORIZONTAL)
#        portsizer = wx.BoxSizer(wx.HORIZONTAL)
#        buttonsizer = wx.BoxSizer(wx.HORIZONTAL)
#        formatsizer = wx.BoxSizer(wx.VERTICAL)
#        
#        baudsizer.Add(wx.StaticText(self, -1, "Baudrate: ", (60,20)), 0, wx.TOP, 8)
#        baudsizer.Add(self.baudPulldown, -1, wx.ALL, 4)
#        portsizer.Add(wx.StaticText(self, -1, "Port: ", (60,20)), 0, wx.TOP, 8)
#        portsizer.Add(self.portPulldown, -1, wx.ALL, 4)
#
#        buttonsizer.Add(self.button_ok, 0, wx.ALL, 2)
#        buttonsizer.Add(self.button_cancel, 0, wx.ALL, 2)
#        mainsizer.Add(baudsizer, 0, wx.ALL, 4)
#        mainsizer.Add(portsizer, 0, wx.ALL, 4)
#        formatsizer.Add(self.checkbox_echo, 0, wx.ALL, 4)
#        formatsizer.Add(self.checkbox_unprintable, 0, wx.ALL, 4)
#        formatsizer.Add(self.radio_box_newline, 0, wx.ALL | wx.ALIGN_CENTER, 4)
#        mainsizer.Add(formatsizer, 0, wx.ALL | wx.ALIGN_CENTER,1)
#        mainsizer.Add(buttonsizer, 0, wx.ALL | wx.ALIGN_CENTER,1)
#
#        self.SetSizer(mainsizer)
#        self.SetAutoLayout(1)
#        mainsizer.Fit(self)
#        mainsizer.SetSizeHints(self)
#        self.Layout()
#        self.CenterOnScreen()
#
#    def __attach_events(self):
#        self.Bind(wx.EVT_BUTTON, self.OnOK, id = self.button_ok.GetId())
#        self.Bind(wx.EVT_BUTTON, self.OnCancel, id = self.button_cancel.GetId())
#        self.Bind(wx.EVT_CHOICE, self.BaudChoice, self.baudPulldown)
   
    def BaudChoice(self, event):
        print "set baudrate to ", event.GetString()

    def OnOK(self, events):
        """Update data wil new values and close dialog."""
        self.settings.echo = self.checkbox_echo.GetValue()
        self.settings.unprintable = self.checkbox_unprintable.GetValue()
        self.settings.newline = self.radio_box_newline.GetSelection()
        self.settings.baudrate = self.baudPulldown.GetSelection()
        self.settings.port = self.portPulldown.GetSelection()
        self.EndModal(wx.ID_OK)
   
    def OnCancel(self, events):
        """Do not update data but close dialog."""
        self.EndModal(wx.ID_CANCEL)

 #end of class TerminalSettingsDialog

class MyApp(wx.App):
    def OnInit(self):
        wx.InitAllImageHandlers()
        mainFrame = ArduCom(None, -1, "")                                              
        self.SetTopWindow(mainFrame)
        mainFrame.Show(1)
        return 1

# end of class MyApp

if __name__ == "__main__":
    app = MyApp(0)
    app.MainLoop()

 

About me

Pic of me