Python Smart Terminal Technical Details
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 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.
Comm Port Validation
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
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:
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.