Difference between revisions of "Python Smart Terminal Technical Details"

From OpenCircuits
Jump to navigation Jump to search
 
(13 intermediate revisions by the same user not shown)
Line 1: Line 1:
 
This page is for some fairly fine scale detail on how things are implemented in the Smart Terminal, they may be useful as techniques in other applications.  See also:  [[Russ Python Tips and Techniques]]
 
This page is for some fairly fine scale detail on how things are implemented in the Smart Terminal, they may be useful as techniques in other applications.  See also:  [[Russ Python Tips and Techniques]]
  
= Comm Port Details =
+
= GUI Construction =
 +
This is one of the earlier GUIs that I worked on and the code has been worked over a lot.  It still has some early features that I would not write now, I have left them in because they work well enough.
 +
......... more as I get to it......
 +
 
 +
= Some Thread Details =
 +
 
 +
There are two threads set up in the application, one meant to communicate from the gui to the helper thread and one the other way around.  Queues are supposed to be thread save so we should be ok so far.  Thread processing is quite localized in the code so you should be able to check it out pretty easily: search for '''queue_to_helper''' and '''queue_fr_helper'''.  Generally what is posted to the queues is a tuple with three parts: (action, function, function_args ).  In this tuple action is a string ( like "call"..."stop" )  function is a function reference, and function_args are arguments to the function.  Not all parts may be used for each action.  You should read the code to see what happens.
 +
 
 +
One example that exists at, this time of writing, is how the helper thread outputs to the gui thread since it is not supposed to interact with direct calls.  What happens is that the helper thread posts a tuple:  ( "info", None, ( "print me", ) ).  "info" indicates that the helper thread wants to post an "info message" to the gui. None indicates that there is no explicit function call involved ( it is implied in the action "info" and ("print me") is the data that is to be displayed.
 +
 
 +
This is all I will document for now because smaller detail may change.  Since the code is localized you should be able to figure out more details yourself, if no email me.
 +
 
 +
= Finding the Arduino - Comm Port Details =
  
 
In the parameter file there is a setting for the comm port, this is typical of all communications programs.  Other parameters hold other comm port values.  Again typical.  There are however, some less typical additions.
 
In the parameter file there is a setting for the comm port, this is typical of all communications programs.  Other parameters hold other comm port values.  Again typical.  There are however, some less typical additions.
Line 7: Line 19:
 
== Comm Port Validation ==
 
== Comm Port Validation ==
  
* So you open a comm port, the first level of validation is that the port opens.
+
In the "manual" mode you would press the Open button on the GUI and hopefully the port will open ( it will if the port exists and is available for opening ).  Then typically you will send something to the micro controller and look for a reasonable response. This is fineYou can also automate the process by using the method  find_arduino(  ) in HelperThread instance.  Here is what it does.
* The second level of validation is that the port can send and receive, this is checked by sending a text string and looking for a response.
 
* The third level is to look at the received stringIf it contains the correct substring then the port is valid.
 
  
For example the terminal may send "v<cr>", recieve "GreenHouse Monitor v3 2017 01 24.01<cr>", and look for "GreenHouse" in the recieved string.  This would validate the port.
+
* Works through your list of comm ports as specified in '''parameters.port'''.
 +
* For each port try to open it with the other comm parameters in '''parameters.py'''
 +
* If a port opens waits a bit ( for the arduino to reset ... time specified by: parameters.not_done_yet  ...... now hardcoded
 +
* The second level of validation is that the port can send and receive to the desired arduino, this is checked by sending a text string ( as specified in parameters.get_arduino_version) and looking for a response.
 +
* The third level is to look at the received string.  If it contains the correct substring ( as specified in parameters.arduino_version) then the port is valid.
 +
* Stops looking at the port list if a comm port passes this validation test.
  
So what does the terminal send and what does it look for -- see the parameter file"
+
For example the terminal may send "v<cr>", recieve "GreenHouse Monitor v3 2017 01 24.01<cr>", and look for "GreenHouse" in the received string.  This would validate the port.
 +
 
 +
So what does the terminal send and what does it look for -- see the parameter file; you should find something like this:
  
 
<pre>
 
<pre>
Line 19: Line 36:
 
         # the arduino is supposed to respond to a version request with a string
 
         # the arduino is supposed to respond to a version request with a string
 
         # containing this, they are part of the name of an Arduino application
 
         # containing this, they are part of the name of an Arduino application
        self.get_arduino_version    = "v"
 
  
         self.arduino_version        = "GreenHouse"
+
        self.get_arduino_version    = "v"    # send to the port to get a response from the arduino.
 +
 
 +
         self.arduino_version        = "GreenHouse" # the received response from the arduino should contain this as a substring if the comm port is valid.
 
</pre>
 
</pre>
 +
 +
Note that the find_arduino() method does not try different baud rates, you need to get that one right yourself.
 +
 +
== What are the Possible Comm Ports ==
 +
 +
The SmartTerminal uses two methods for this.  One is just back to the parameter file:
 +
 +
<pre>
 +
        # used to probe around for ports
 +
        if  self.os_win:
 +
            self.port_list  =  [ "COM1",  "COM2",  "COM3",  "COM4",  "COM5",  "COM6",  "COM7",  "COM8",  "COM9",  "COM10",
 +
                                "COM11", "COM12", "COM13", "COM14", "COM15", "COM16", "COM17", "COM18",  "COM19", "COM20",
 +
                              ]
 +
 +
        else:
 +
            self.port_list  =  [ "/dev/ttyUSB0", "/dev/ttyUSB1", "/dev/ttyUSB2",
 +
                                "/dev/ttyACM0", "/dev/ttyACM1", "/dev/ttyACM2",
 +
 
 +
</pre>
 +
 +
The above uses the automatic detection of the operating system.  You could of course skip that part of the implementation.
 +
 +
The second method.  There is a Python function for detecting ports.  It is documented as not always being right.  Here it is:
 +
 +
<pre>
 +
    list(serial.tools.list_ports.comports())
 +
</pre>
 +
 +
So finally when making a list of ports to probe I make a list ( in the order the ports will be tested ):
 +
 +
* Start with port in the parameter file.
 +
* Add the ports reported by python ( no duplicates ).
 +
* Add the ports in the parameter port list ( no duplicates ).
 +
 +
If no ports are actually present you can test 20 ports in seconds.  Sending and receiving strings takes longer.  Note that all of this is done using the baud rate and other parameters in the parameter file, we do not probe using variations on them.  Actually, in most cases, the baud rate.... are part of the identification of the arduino.
 +
 +
So with this list we loop through it trying to validate each port.
 +
 +
 +
<!-----------
 +
 +
 +
== Code in SmartTerminal ==
 +
 +
What?
 +
 +
* Review this for the inner inner details.  Send questions if not clear.
 +
 +
* in smart_terminal_helper.HelperThread.test_ports( self, a_port_list, a_get, a_valid_response ) is the probe and validation routine, but this needs the port list, the probe string and the valid response. 
 +
 +
* above uses, in the same object, test_port()
 +
 +
* first subroutine above is called from ( same object ) find_arduino( ) which constructs the port list and get parameters as needed from parameters.
 +
this cannot bee seen
 +
------------->
 +
 +
[[Category:SmartTerminal]] [[Category:Python]]

Latest revision as of 13:11, 28 August 2022

This page is for some fairly fine scale detail on how things are implemented in the Smart Terminal, they may be useful as techniques in other applications. See also: Russ Python Tips and Techniques

GUI Construction[edit]

This is one of the earlier GUIs that I worked on and the code has been worked over a lot. It still has some early features that I would not write now, I have left them in because they work well enough. ......... more as I get to it......

Some Thread Details[edit]

There are two threads set up in the application, one meant to communicate from the gui to the helper thread and one the other way around. Queues are supposed to be thread save so we should be ok so far. Thread processing is quite localized in the code so you should be able to check it out pretty easily: search for queue_to_helper and queue_fr_helper. Generally what is posted to the queues is a tuple with three parts: (action, function, function_args ). In this tuple action is a string ( like "call"..."stop" ) function is a function reference, and function_args are arguments to the function. Not all parts may be used for each action. You should read the code to see what happens.

One example that exists at, this time of writing, is how the helper thread outputs to the gui thread since it is not supposed to interact with direct calls. What happens is that the helper thread posts a tuple: ( "info", None, ( "print me", ) ). "info" indicates that the helper thread wants to post an "info message" to the gui. None indicates that there is no explicit function call involved ( it is implied in the action "info" and ("print me") is the data that is to be displayed.

This is all I will document for now because smaller detail may change. Since the code is localized you should be able to figure out more details yourself, if no email me.

Finding the Arduino - Comm Port Details[edit]

In the parameter file there is a setting for the comm port, this is typical of all communications programs. Other parameters hold other comm port values. Again typical. There are however, some less typical additions.

Comm Port Validation[edit]

In the "manual" mode you would press the Open button on the GUI and hopefully the port will open ( it will if the port exists and is available for opening ). Then typically you will send something to the micro controller and look for a reasonable response. This is fine. You can also automate the process by using the method find_arduino( ) in HelperThread instance. Here is what it does.

  • Works through your list of comm ports as specified in parameters.port.
  • For each port try to open it with the other comm parameters in parameters.py
  • If a port opens waits a bit ( for the arduino to reset ... time specified by: parameters.not_done_yet ...... now hardcoded
  • The second level of validation is that the port can send and receive to the desired arduino, this is checked by sending a text string ( as specified in parameters.get_arduino_version) and looking for a response.
  • The third level is to look at the received string. If it contains the correct substring ( as specified in parameters.arduino_version) then the port is valid.
  • Stops looking at the port list if a comm port passes this validation test.

For example the terminal may send "v<cr>", recieve "GreenHouse Monitor v3 2017 01 24.01<cr>", and look for "GreenHouse" in the received string. This would validate the port.

So what does the terminal send and what does it look for -- see the parameter file; you should find something like this:

        # next used in a port probe routine to help identify the port with an arduino
        # the arduino is supposed to respond to a version request with a string
        # containing this, they are part of the name of an Arduino application

        self.get_arduino_version    = "v"     # send to the port to get a response from the arduino.

        self.arduino_version        = "GreenHouse"  # the received response from the arduino should contain this as a substring if the comm port is valid.

Note that the find_arduino() method does not try different baud rates, you need to get that one right yourself.

What are the Possible Comm Ports[edit]

The SmartTerminal uses two methods for this. One is just back to the parameter file:

        # used to probe around for ports
        if  self.os_win:
            self.port_list  =  [ "COM1",  "COM2",  "COM3",  "COM4",  "COM5",  "COM6",  "COM7",  "COM8",  "COM9",  "COM10",
                                "COM11", "COM12", "COM13", "COM14", "COM15", "COM16", "COM17", "COM18",  "COM19", "COM20",
                               ]

        else:
            self.port_list  =  [ "/dev/ttyUSB0", "/dev/ttyUSB1", "/dev/ttyUSB2",
                                "/dev/ttyACM0", "/dev/ttyACM1", "/dev/ttyACM2",
  

The above uses the automatic detection of the operating system. You could of course skip that part of the implementation.

The second method. There is a Python function for detecting ports. It is documented as not always being right. Here it is:

     list(serial.tools.list_ports.comports())

So finally when making a list of ports to probe I make a list ( in the order the ports will be tested ):

  • Start with port in the parameter file.
  • Add the ports reported by python ( no duplicates ).
  • Add the ports in the parameter port list ( no duplicates ).

If no ports are actually present you can test 20 ports in seconds. Sending and receiving strings takes longer. Note that all of this is done using the baud rate and other parameters in the parameter file, we do not probe using variations on them. Actually, in most cases, the baud rate.... are part of the identification of the arduino.

So with this list we loop through it trying to validate each port.